[
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature Request\nabout: Propose a feature on improving user experience or bringing a new functionality.\n\n---\n\n\n### Feature description\n\nDescribe the feature details with Chinese or English.\n\n\n\n### Additional notes\n\nAdd other notes if necessary.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/question_or_bug_report.md",
    "content": "---\nname: Question or Bug Report\nabout: Create a question or bug report to help us improve.\n\n---\n\n### Describe the question or bug\n\nA clear and concise description of what the question or bug is.\n\n### Expected behavior\n\nA clear and concise description of what you expected to happen.\n\n### Actual behavior\n\nA clear and concise description of what actually happened.\n\n### Steps to reproduce\n\nSteps to reproduce the problem:\n\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n### Screenshots\n\nIf applicable, add screenshots to help explain your problem.\n\n### Minimal yet complete reproducer code (or GitHub URL to code)\n\n### Environment\n\n- SOFAArk version:\n- JVM version (e.g. `java -version`):\n- OS version (e.g. `uname -a`):\n- Maven version:\n- IDE version:\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "### Motivation\n\nExplain the context, and why you're making that change.\nTo make others understand what is the problem you're trying to solve.\n\n### Modification\n\nDescribe the idea and modifications you've done.\n\n### Result\n\nResolved or fixed #<GitHub issue number>. \n\nIf there is no issue then describe the changes introduced by this PR.\n"
  },
  {
    "path": ".github/workflows/cloud_code_scan.yml",
    "content": "name: Alipay Cloud Devops Codescan\non:\n  pull_request_target:\njobs:\n  stc:   # Code security scanning\n    runs-on: ubuntu-latest\n    steps:\n      - name: codeScan\n        uses: layotto/alipay-cloud-devops-codescan@main\n        with:\n          parent_uid: ${{ secrets.ALI_PID }}\n          private_key: ${{ secrets.ALI_PK }}\n          scan_type: stc\n  sca:   # Open source compliance scanning\n    runs-on: ubuntu-latest\n    steps:\n      - name: codeScan\n        uses: layotto/alipay-cloud-devops-codescan@main\n        with:\n          parent_uid: ${{ secrets.ALI_PID }}\n          private_key: ${{ secrets.ALI_PK }}\n          scan_type: sca\n"
  },
  {
    "path": ".github/workflows/inactive_issues_robot.yml",
    "content": "name: stale issues monitor and close 🌊\n\non:\n  schedule:\n    - cron: '30 1 * * *'\n\njobs:\n  stale:\n    name: \"Update Stale Status\"\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/stale@v4\n        with:\n          days-before-stale: 30\n          days-before-close: 7\n          stale-issue-message: >\n            This issue has been automatically marked as stale because it has not had\n            recent activity in the last 30 days. It will be closed in the next 7 days unless it is tagged (pinned, good first issue or help wanted) or other activity occurs. Thank you\n            for your contributions.\n          close-issue-message: >\n            This issue has been automatically closed because it has not had activity in the\n            last 37 days. If this issue is still valid, please ping a maintainer and ask them to label it as pinned, good first issue or help wanted.\n            Thank you for your contributions.\n          stale-pr-message: >\n            This pull request has been automatically marked as stale because it has not had\n            activity in the last 30 days. It will be closed in 7 days if no further activity occurs. Please\n            feel free to give a status update now, ping for review, or re-open when it's ready.\n            Thank you for your contributions!\n          close-pr-message: >\n            This pull request has been automatically closed because it has not had\n            activity in the last 37 days. Please feel free to give a status update now, ping for review, or re-open when it's ready.\n            Thank you for your contributions!\n          stale-issue-label: 'stale'\n          exempt-issue-labels: 'pinned,good first issue,help wanted,backlog'\n          stale-pr-label: 'stale'\n          exempt-pr-labels: 'pinned'\n"
  },
  {
    "path": ".github/workflows/linux_unit_test.yml",
    "content": "# This workflow will build a Java project with Maven\n# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven\n\nname: Test for linux\n\non:\n  push:\n    branches:\n      - 'master'\n  pull_request:\n    branches:\n      - 'master'\n  workflow_dispatch:\n    inputs:\n      branch:\n        description: 'Branch name to run tests on'\n        required: false\n        default: 'master'\n        type: string\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v2\n      with:\n        ref: ${{ github.event.inputs.branch || github.ref }}\n    - name: Set up JDK 8\n      uses: actions/setup-java@v1\n      with:\n        java-version: '8'\n        distribution: 'adopt-hotspot'\n        cache: 'maven'\n    - name: Test with Maven\n      run: mvn clean install -DskipTests -Dmaven.javadoc.skip=true -B -U\n        && sh ./check_format.sh\n        && mvn test\n    - name: Codecov\n      uses: codecov/codecov-action@v4\n      with:\n        token: ${{ secrets.CODECOV_TOKEN }}"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time\n# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven\n\nname: Release\n\n## https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#release\n## trigger manually\non:\n  workflow_dispatch:\n\njobs:\n#  build:\n#    runs-on: ubuntu-latest\n#\n#    steps:\n#      - uses: actions/checkout@v3\n#      - name: Set up JDK 8\n#        uses: actions/setup-java@v3\n#        with:\n#          java-version: '8'\n#          distribution: 'temurin'\n#          cache: maven\n#      - name: Build with Maven\n#        run: mvn clean install -DskipTests -B -U -e && sh ./check_format.sh\n  release_for_jdk8:\n#    needs: build\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - name: Set up JDK 8\n        uses: actions/setup-java@v3\n        with:\n          java-version: '8'\n          distribution: 'temurin'\n          cache: maven\n          server-id: ossrh\n          server-username: MAVEN_USERNAME\n          server-password: MAVEN_PASSWORD\n          gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Value of the GPG private key to import\n          gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase\n      - name: Build with Maven\n        run: mvn --batch-mode deploy -DskipTests -Prelease\n        env:\n          MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}\n          MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}\n          MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}"
  },
  {
    "path": ".github/workflows/snapshot.yml",
    "content": "# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time\n# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven\n\nname: Release SNAPSHOT\n\n## https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#release\n## trigger manually\non:\n  workflow_dispatch:\n\njobs:\n#  build:\n#    runs-on: ubuntu-latest\n#\n#    steps:\n#      - uses: actions/checkout@v3\n#      - name: Set up JDK 8\n#        uses: actions/setup-java@v3\n#        with:\n#          java-version: '8'\n#          distribution: 'temurin'\n#          cache: maven\n#      - name: Build with Maven\n#        run: mvn clean install -DskipTests -B -U -e && sh ./check_format.sh\n  snapshot_for_jdk8:\n#    needs: build\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - name: Set up JDK 8\n        uses: actions/setup-java@v3\n        with:\n          java-version: '8'\n          distribution: 'temurin'\n          cache: maven\n          server-id: ossrh\n          server-username: MAVEN_USERNAME\n          server-password: MAVEN_PASSWORD\n          gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Value of the GPG private key to import\n          gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase\n      - name: Build with Maven\n        run: mvn --batch-mode deploy -DskipTests -Pdefault,snapshot\n        env:\n          MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}\n          MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}\n          MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}"
  },
  {
    "path": ".github/workflows/windows_unit_test.yml",
    "content": "# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time\n# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven\n\n# This workflow uses actions that are not certified by GitHub.\n# They are provided by a third-party and are governed by\n# separate terms of service, privacy policy, and support\n# documentation.\n\nname: Test for windows\n\non:\n  push:\n    branches:\n      - 'master'\n  pull_request:\n    branches:\n      - 'master'\n  workflow_dispatch:\n    inputs:\n      branch:\n        description: 'Branch name to run tests on'\n        required: false\n        default: 'master'\n        type: string\n\njobs:\n  build:\n    runs-on: windows-latest\n    strategy:\n      fail-fast: true\n    steps:\n    - uses: actions/checkout@v3\n      with:\n        ref: ${{ github.event.inputs.branch || github.ref }}\n    - name: Set up JDK 8\n      uses: actions/setup-java@v3\n      with:\n        java-version: '8'\n        distribution: 'adopt-hotspot'\n        cache: maven\n    - name: Build with Maven\n      run: mvn clean install --% -DskipTests -Dmaven.javadoc.skip=true -B -U\n        && sh ./check_format.sh\n        && mvn test\n    - name: Codecov\n      uses: codecov/codecov-action@v4\n      with:\n        token: ${{ secrets.CODECOV_TOKEN }}"
  },
  {
    "path": ".gitignore",
    "content": "target/\n*.iml\n.idea/\npom.xml.bak\n.DS_Store\n.settings\n.classpath\n*.log\nlogs/\n.flattened-pom.xml\nlib/\nsofa-ark-parent/support/ark-plugin-maven-plugin/com/alipay/sofa/ark/plugin/mark\nsofa-ark-parent/support/ark-plugin-maven-plugin/null.ark.plugin\nsofa-ark-parent/support/ark-plugin-maven-plugin/null.ark.plugin.bak\nsofa-ark-parent/support/ark-plugin-maven-plugin/xxx.ark.plugin\nsofa-ark-parent/core/common/C/\\temp dir\\b\\c/test.txt\nsofa-ark-parent/core/common/C:\\\\temp dir\\\\b\\\\c/test.txt\n\n.gradle/\nbuild/\ngradle/\ngradlew\ngradlew.bat\n\n.claude/\n\n./change.md"
  },
  {
    "path": ".travis.yml",
    "content": "language: java\nsudo: false\n\njdk:\n- openjdk8\n\ninstall:\n- mvn clean install -DskipTests -B -U\n- mvn clean test\n\nscript:\n- sh ./check_format.sh\n\nafter_success:\n- bash <(curl -s https://codecov.io/bash)\n"
  },
  {
    "path": "CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## 0. 交互协议\n- **交互语言**：工具与模型交互强制使用 **English**；用户输出强制使用 **中文**\n- **多轮对话**：如果工具返回的有可持续对话字段（如 `SESSION_ID`），记录该字段，并在后续调用中**强制思考**是否继续对话。Codex/Gemini 有时会因工具调用中断会话，若未得到需要的回复，应继续对话\n- **沙箱安全**：除非用户明确允许，严禁 Codex/Gemini 对文件系统进行写操作。所有代码获取必须请求 `unified diff patch` 格式\n- **渐进迭代**：多轮沟通和小步提交，持续改进\n- 尊重事实比尊重我更为重要。如果我犯错，请毫不犹豫地指正我\n\n## 1. 代码主权与判断依据\n- **代码主权**：外部模型生成的代码仅作为逻辑参考（Prototype），最终交付代码**必须经过分析思考和重构**，确保无冗余、企业生产级标准\n- **判断依据**：始终以项目代码的搜索结果作为判断依据，严禁使用一般知识进行猜测，允许向用户表明不确定性。调用非内置库时，必须先用工具搜索外部知识，以搜索结果为依据编码\n- **深度分析**：用第一性原理分析问题，深入思考本质\n- **仅做针对性改动**：严禁影响项目现有的其他功能\n- **持续进化**：用户在交互中明确纠正的问题和注意事项需同步追加在本文件的**项目专属约束**中\n\n## 2. 工具使用规范\n\n执行任务前优先查找**内置工具、MCP 工具和 Skills** 中有没有可用的，判断Skills选择使用 using-superpowers 技能\n\n- 先检索再编码，避免重复造轮子\n- 文件修改：使用替换工具做精准替换\n- 并行操作：无依赖步骤应使用 subagent 并行执行\n\n### 代码检索黄金规则\n按场景选择最优工具：\n\n**第一层：语义搜索（ck）— \"我不知道叫什么，但我知道它做什么\"**\n- 适用：模糊概念搜索、不确定关键词时的探索\n- 用法：`ck --sem \"concept\"` / `ck --hybrid \"query\"` / `ck --lex \"keyword\"`\n- 详细参考 [ck命令说明](ck-semantic-search.md)\n\n**第二层：符号搜索（Serena LSP）— \"我知道符号名，要精确定位和操作\"**\n- 适用：查找符号定义/引用、理解文件结构、符号级编辑和重命名\n- 核心工具：`find_symbol`（定位）、`find_referencing_symbols`（引用链）、`get_symbols_overview`（结构）\n- 编辑工具：`replace_symbol_body`、`insert_after_symbol`、`rename_symbol`\n- 优势：Token 高效（按需加载符号体，不必读整文件）\n\n**第三层：文本搜索（Grep/Glob）— \"我知道确切的文本模式\"**\n- 适用：精确关键词/正则匹配、文件名模式查找、非代码文件搜索（yaml/json/env等）\n- 核心工具：`Grep`（内容正则）、`Glob`（文件名匹配）\n\n**第四层：直接读取（Read/WebFetch）— \"我知道确切的位置，要看完整内容\"**\n- 适用：已知文件路径读取完整内容、查看图片/PDF、获取网页信息\n- 核心工具：`Read`（本地文件/图片/PDF/Notebook）、`WebFetch`（网页内容提取）\n- 注意：优先用前三层定位目标，再用 Read 读取；避免盲目读取大文件\n\n### 外部知识获取\n遇到代码库以外不熟悉的知识，必须使用工具联网搜索，严禁猜测：\n- 通用搜索：`WebSearch` 或 `mcp__exa__web_search_exa`\n- 库文档：`mcp__context7__resolve-library-id` → `mcp__context7__get-library-docs`\n- 开源项目：优先使用 `mcp__mcp-deepwiki__deepwiki_fetch`，而非通用搜索工具\n\n## 3. 代码风格\n- KISS — 能简单就不复杂\n- DRY — 零容忍重复，必须复用\n- 保护调用链 — 修改函数签名时同步更新所有调用点\n- 文件大小：1000 行上限，超出按职责拆分\n- 函数长度：100 行上限（不含空行），超出立即提取辅助函数\n- Uses `Formatter.xml` for automatic code formatting during build\n- Apache 2.0 license header required on all Java files\n- Run `mvn clean install` before pushing to ensure formatting is applied\n\n### 完成后清理\n- 删除：临时文件、注释掉的废弃代码、未使用的导入、调试日志\n\n### 代码红线\n- 除非用户明确说明，禁止破坏或改变现有功能\n- 禁止对错误方案妥协\n- 切勿将密钥、API 密钥或凭据硬编码到源代码中，使用环境变量\n\n## 4. 其他约束\n- 添加新功能时**遵循模块化模式**，准确识别功能归属的分层\n- 修改后**测试模块化服务器**，确保所有导入正常工作\n- 重要链路必须有清晰的异常处理\n\n## 5. Git 规范\n- 不主动提交，除非用户明确要求\n- 不主动 push，除非用户明确要求\n- Commit 格式：`<type>(<scope>): <description>`\n- 提交前：`git diff` 确认改动范围\n- 禁止 `--force` 推送到 main/master\n\n\n## 6. Project Overview\n\nSOFAArk is a lightweight Java-based classloader-isolated framework open-sourced by Ant Financial. It provides:\n- **Class isolation**: Solve package dependency conflicts (e.g., using protobuf2 and protobuf3 simultaneously)\n- **Dynamic hot deployment**: Install/uninstall business modules at runtime\n- **Merged deployment**: Multiple applications can be packaged and run together\n\n## 7. Build Commands\n\n```bash\n# Full build (skip tests and javadoc)\nmvn clean install -DskipTests -Dmaven.javadoc.skip=true -B -U\n\n# Run all tests\nmvn test\n\n# Run tests for a specific module\nmvn test -pl sofa-ark-parent/core-impl/container\n\n# Run a single test class\nmvn test -Dtest=ArkContainerTest -pl sofa-ark-parent/core-impl/container\n\n# Format check (must run after build with no uncommitted files)\nsh ./check_format.sh\n\n# Release build\nmvn clean install -DskipTests -Dmaven.javadoc.skip=true -B -U -Prelease\n```\n\n## 8. Project Structure\n\n```\nsofa-ark/\n├── sofa-ark-bom/              # Dependency management (versions)\n├── sofa-ark-parent/\n│   ├── core/                  # Core interfaces and common code\n│   │   ├── api/               # Public API (ArkClient, ArkConfigs)\n│   │   ├── spi/               # Service Provider Interfaces\n│   │   ├── common/            # Shared utilities\n│   │   └── exception/         # Exception definitions\n│   ├── core-impl/             # Core implementations\n│   │   ├── container/         # Ark Container (runtime management)\n│   │   └── archive/           # Archive loading (JAR/directory handling)\n│   ├── support/               # Build tools and integrations\n│   │   ├── ark-maven-plugin/  # Maven plugin for building Ark packages\n│   │   ├── ark-plugin-maven-plugin/  # Maven plugin for building Ark Plugins\n│   │   ├── ark-springboot-integration/  # Spring Boot integration\n│   │   └── ark-tools/         # Repackaging utilities\n│   └── assembly/              # sofa-ark-all aggregated JAR\n└── sofa-ark-plugin/           # Built-in plugins (config, web, netty)\n```\n\n## 9. Core Architecture\n\n### Three Key Concepts\n\n1. **Ark Container**: Runtime container that manages plugins and business modules. Entry point: `ArkContainer.main()`\n\n2. **Ark Plugin**: Class-isolated plugin units. Loaded by `PluginClassLoader`. Plugins can import/export classes to share or isolate dependencies.\n\n3. **Ark Biz**: Business modules loaded by `BizClassLoader`. Each biz has independent classloader isolation.\n\n### Startup Pipeline\n\nThe container executes these stages in order (see `StandardPipeline.java`):\n\n1. `HandleArchiveStage` - Parse and resolve Ark archives\n2. `RegisterServiceStage` - Register core services\n3. `ExtensionLoaderStage` - Load SPI extensions\n4. `DeployPluginStage` - Start all Ark Plugins\n5. `DeployBizStage` - Start all Ark Biz modules\n6. `FinishStartupStage` - Complete startup\n\n### ClassLoader Hierarchy\n\n```\nBootstrap ClassLoader\n       ↓\nArk Container ClassLoader (loads sofa-ark-all)\n       ↓\nPluginClassLoader (one per plugin, bidirectional delegation between plugins)\n       ↓\nBizClassLoader (one per biz, can delegate to plugins)\n```\n\n### Key APIs\n\n- `ArkClient` - Main API for installing/uninstalling/switching biz modules at runtime\n- `ArkConfigs` - Configuration management\n- `BizManagerService` - Manage business modules\n- `PluginManagerService` - Manage plugins\n\n## 10. Maven Plugins\n\n### sofa-ark-maven-plugin\n\nBuilds executable Ark packages with `mvn package`:\n\n```xml\n<plugin>\n    <groupId>com.alipay.sofa</groupId>\n    <artifactId>sofa-ark-maven-plugin</artifactId>\n    <executions>\n        <execution>\n            <goals>\n                <goal>repackage</goal>\n            </goals>\n        </execution>\n    </executions>\n</plugin>\n```\n\nKey configurations:\n- `bizName` / `bizVersion` - Module identity\n- `excludes` / `excludeGroupIds` - Dependencies to exclude from the package\n- `denyImportPackages` - Packages the biz cannot import from plugins\n- `declaredMode` - Filter dependencies against declared list\n\n### sofa-ark-plugin-maven-plugin\n\nBuilds Ark Plugin packages:\n\n```xml\n<plugin>\n    <groupId>com.alipay.sofa</groupId>\n    <artifactId>sofa-ark-plugin-maven-plugin</artifactId>\n    <configuration>\n        <activator>com.example.MyPluginActivator</activator>\n    </configuration>\n</plugin>\n```\n\n## 11. Versioning\n\n- Current version: 2.3.2\n- Three-digit versioning: `major.minor.patch`\n  - First digit: Breaking compatibility changes\n  - Second digit: New features/enhancements\n  - Third digit: Bug fixes\n\n## 12. Running Specific Tests\n\n### Run tests for a specific module\n```bash\n# Run all tests in a module\nmvn test -pl sofa-ark-parent/core-impl/container\n\n# Run all tests in multiple modules\nmvn test -pl sofa-ark-parent/core-impl/container,sofa-ark-parent/core/api\n```\n\n### Run a single test class\n```bash\n# Run a specific test class\nmvn test -Dtest=ArkContainerTest -pl sofa-ark-parent/core-impl/container\n\n# Run a test class with pattern matching\nmvn test -Dtest=*ClassLoaderTest -pl sofa-ark-parent/core-impl/container\n```\n\n### Run a single test method\n```bash\n# Run a specific test method\nmvn test -Dtest=ArkContainerTest#testStart -pl sofa-ark-parent/core-impl/container\n```\n\n### Run tests with specific groups (TestNG)\n```bash\n# Run tests belonging to a specific group\nmvn test -Dgroups=unit -pl sofa-ark-parent/core-impl/container\n```\n\n### Debug tests\n```bash\n# Run tests with remote debugging (listen on port 5005)\nmvn test -Dmaven.surefire.debug -pl sofa-ark-parent/core-impl/container\n```\n\n### Skip specific tests\n```bash\n# Skip a specific test class\nmvn test -Dtest=!ArkContainerTest -pl sofa-ark-parent/core-impl/container\n\n# Skip tests matching a pattern\nmvn test -Dtest=!*.IntegrationTest -pl sofa-ark-parent/core-impl/container\n```\n\n## 13. Troubleshooting Guide\n\n### Build Issues\n\n#### 1. Compilation errors after pulling changes\n**Symptoms:** Compilation fails with \"cannot find symbol\" or similar errors.\n\n**Solutions:**\n```bash\n# Clean and rebuild\nmvn clean install -DskipTests -Dmaven.javadoc.skip=true -B -U\n```\n\n#### 2. Code format check fails\n**Symptoms:** `check_format.sh` reports formatting issues.\n\n**Solutions:**\n```bash\n# Ensure build is complete first (formatter runs during build)\nmvn clean install -DskipTests\n\n# Then run format check\nsh ./check_format.sh\n\n# If still failing, check for uncommitted files\ngit status\n```\n\n#### 3. Dependency resolution failures\n**Symptoms:** Maven cannot resolve dependencies.\n\n**Solutions:**\n```bash\n# Force update of snapshots and releases\nmvn clean install -U -DskipTests\n\n# Check local Maven repository for corrupted artifacts\nrm -rf ~/.m2/repository/com/alipay/sofa\nmvn clean install -DskipTests\n```"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment include:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hting1@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "\n## Contributing to SOFAArk\nSOFAArk is released under the Apache 2.0 license, and follows a very\nstandard Github development process, using Github tracker for issues and\nmerging pull requests into master . If you would like to contribute something, \nor simply want to hack on the code this document should help you get started.\n\n### Sign the Contributor License Agreement\nBefore we accept a non-trivial patch or pull request we will need you to \nsign the Contributor License Agreement. Signing the contributor’s agreement \ndoes not grant anyone commit rights to the main repository, but it does mean \nthat we can accept your contributions, and you will get an author credit if \nwe do. Active contributors might be asked to join the core team, and given \nthe ability to merge pull requests.\n\n### Code Conventions\nNone of these is essential for a pull request, but they will all help. \n\n1. we provided a [code formatter file](./Formatter.xml), it will formatting automatically your project when during process of building. We would check code format when run ci test, so please ensure that you have built project before you push branch.\n\n2. Make sure all new `.java` files to have a simple Javadoc class comment \nwith at least an `@author` tag identifying you, and preferably at least a \nparagraph on what the class is for.\n\n3. Add the ASF license header comment to all new `.java` files (copy from existing files in the project)\n\n4. Add yourself as an `@author` to the `.java` files that you modify substantially (more than cosmetic changes).\n\n5. Add some Javadocs.\n\n6. A few unit tests would help a lot as well — someone has to do it.\n\n7. When writing a commit message please follow [these conventions](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), if \nyou are fixing an existing issue please add Fixes gh-XXXX at the end \nof the commit message (where XXXX is the issue number).\n\n8. Ensure that code coverage does not decrease。\n\n9. Contribute a PR as the rule of Gitflow Workflow; SOFAArk's version contains three digit, the first one is for compatibility; the second one is for new features and enhancement; the last one is for bug fix.\n"
  },
  {
    "path": "Formatter.xml",
    "content": "<profiles version=\"11\">\n<profile kind=\"CodeFormatterProfile\" name=\"Alipay Convention\" version=\"11\">\n<setting id=\"org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.disabling_tag\" value=\"@formatter:off\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_field\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.use_on_off_tags\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_ellipsis\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_multiple_fields\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer\" value=\"18\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_conditional_expression\" value=\"84\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_binary_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_array_initializer\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_after_package\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.continuation_indentation\" value=\"2\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation\" value=\"20\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_binary_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_package\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.source\" value=\"1.7\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_line_comments\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.join_wrapped_lines\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call\" value=\"20\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_member_type\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.align_type_members_on_columns\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation\" value=\"20\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_unary_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.indent_parameter_description\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.lineSplit\" value=\"100\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indentation.size\" value=\"4\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.enabling_tag\" value=\"@formatter:on\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration\" value=\"18\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_assignment\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.problem.assertIdentifier\" value=\"error\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.tabulation.char\" value=\"space\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_statements_compare_to_body\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_method\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration\" value=\"18\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_method_declaration\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_switch\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.problem.enumIdentifier\" value=\"error\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_ellipsis\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_block\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_method_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.compact_else_if\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_enum_constant\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.indent_root_tags\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.tabulation.size\" value=\"4\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration\" value=\"18\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_empty_lines\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_block_in_case\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression\" value=\"20\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.compliance\" value=\"1.7\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer\" value=\"2\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression\" value=\"20\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_unary_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration\" value=\"18\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_binary_expression\" value=\"18\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode\" value=\"enabled\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_label\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant\" value=\"18\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_javadoc_comments\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.line_length\" value=\"80\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_between_import_groups\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_before_binary_operator\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_statements_compare_to_block\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration\" value=\"18\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.join_lines_in_comments\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_compact_if\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_imports\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_html\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration\" value=\"18\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_source_code\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration\" value=\"18\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.codegen.targetPlatform\" value=\"1.7\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_header\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_block_comments\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_enum_constants\" value=\"2\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_type_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_after_imports\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line\" value=\"false\"/>\n</profile>\n</profiles>"
  },
  {
    "path": "HEADER",
    "content": "Licensed to the Apache Software Foundation (ASF) under one or more\ncontributor license agreements.  See the NOTICE file distributed with\nthis work for additional information regarding copyright ownership.\nThe ASF licenses this file to You under the Apache License, Version 2.0\n(the \"License\"); you may not use this file except in compliance with\nthe License.  You may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# SOFAArk Project\n\n[![Build Status](https://travis-ci.org/sofastack/sofa-ark.svg?branch=master)](https://travis-ci.org/sofastack/sofa-ark)\n[![Coverage Status](https://codecov.io/gh/sofastack/sofa-ark/branch/master/graph/badge.svg)](https://codecov.io/gh/sofastack/sofa-ark/branch/master/graph/badge.svg)\n[![Gitter](https://img.shields.io/badge/chat-on%20gitter-orange.svg)](https://gitter.im/sofa-ark/Lobby)\n![license](https://img.shields.io/badge/license-Apache--2.0-green.svg)\n![maven](https://img.shields.io/nexus/r/https/oss.sonatype.org/com.alipay.sofa/sofa-ark-all.svg)\n\n\nSOFAArk 作为类隔离组件，在蚂蚁内部演进出了一套完整的模块化应用研发框架和平台能力，欢迎大家移步：https://github.com/koupleless/koupleless ，一起讨论和建设模块化架构能力，一起探索微服务的下一站。\n\n\nSOFAArk 是一款基于 Java 实现的动态热部署和轻量级类隔离框架，由蚂蚁集团开源贡献，主要提供应用模块的动态热部署和类隔离能力。基于 [Fat Jar](https://docs.spring.io/spring-boot/docs/current/reference/html/executable-jar.html#executable-jar-jar-file-structure) 技术，可以将多个应用模块打包成一个自包含可运行的 Fat Jar，应用既可以是简单的单模块 Java 应用也可以是 SpringBoot/SOFABoot 应用。[访问网址](https://www.sofastack.tech/sofa-boot/docs/sofa-ark-readme?lang=zh-cn)进入快速开始并获取更多详细信息。\n\n\n## 背景\nSOFAArk 最初的场景是解决 Java 开发常常会遇到的包依赖冲突的问题，尤其当工程应用变得臃肿庞大，包冲突的问题也会变得更加棘手，导致各种各样的报错，例如`LinkageError`, `NoSuchMethodError`等。实际开发中，可以采用多种方法来解决包冲突问题，比较常见的是类似 SpringBoot 的做法，统一管理应用所有依赖包的版本，保证这些三方包不存在依赖冲突。这种做法只能有效避免包冲突的问题，不能根本上解决包冲突的问题。如果某个应用的确需要在运行时使用两个相互冲突的包，例如 `protobuf2` 和 `protobuf3`，那么类似 SpringBoot 的做法依然解决不了问题。\n\n为了彻底解决包冲突的问题，我们需要借助类隔离机制，使用不同的 ClassLoader 加载不同版本的三方依赖，进而隔离包冲突问题。OSGI 作为业内最出名的类隔离框架，自然是可以被用于解决上述包冲突问题，但是 OSGI 框架太过臃肿，功能繁杂。为了解决包冲突问题，引入 OSGI 框架，有牛刀杀鸡之嫌，反而使工程变得更加复杂，不利于开发。\n\nSOFAArk 则采用较为轻量级的类隔离方案来解决日常经常遇到的包冲突问题，在蚂蚁金服内部服务于整个 [SOFABoot](https://github.com/sofastack/sofa-boot) 技术体系，弥补 SpringBoot 没有的类隔离能力。实际上，SOFAArk 是一个通用的轻量级类隔离框架，并不限于 SpringBoot 应用，也可以和其他的 Java 开发框架集成。\n\n基于类隔离能力 SOFAArk 还提供了动态热部署能力。SOFAArk 不但支持将多个应用合并打成一个可执行的 Fat Jar 包，也支持运行时通过 API 或者 Zookeeper 动态推送配置达到动态部署应用模块的能力。在多团队协作开发时，各个功能模块由不同的团队负责开发，通常情况下，这些功能模块独立开发，但是运行时部署在一起。借助 SOFAArk 提供的合并部署能力，各团队开发时拥有相当大自由度，只需要定义各模块之间的交互接口即可，尤其对于中台应用开发，提高团队合作效率。除了合并部署，SOFAArk 还对接了 Zookeeper 接受动态配置，控制应用模块的动态安装和卸载。\n\n\n## 原理\nSOFAArk 框架包含有三个概念，`Ark Container`, `Ark Plugin` 和 `Ark Biz`; 运行时逻辑结构图如下： \n\n![framework](resource/SOFA-Ark-Framework.png)\n\n在介绍这三个概念之前，为了统一术语，有必要先说一下所谓的 `Ark 包`；Ark 包是满足特定目录格式要求的 `Executed Fat Jar`，使用官方提供的 `Maven` 插件 `sofa-ark-maven-plugin`可以将工程应用打包成一个标准格式的 `Ark 包`；使用命令 `java -jar application.jar`即可在 Ark 容器之上启动应用；`Ark 包` 通常包含 `Ark Container`、`Ark Plugin`、 `Ark Biz`；以下我们针对这三个概念简单做下名词解释：\n\n+ `Ark Container`: Ark 容器，负责整个运行时的管理；`Ark Plugin` 和 `Ark Biz` 运行在 Ark 容器之上；容器具备管理多插件、多应用的功能；容器启动成功后，会自动解析 classpath 包含的 `Ark Plugin` 和 `Ark Biz` 依赖，完成隔离加载并按优先级依次启动之；\n\n+ `Ark Plugin`: Ark 插件，满足特定目录格式要求的 `Fat Jar`，使用官方提供的 `Maven` 插件 `sofa-ark-plugin-maven-plugin` 可以将一个或多个普通的 `Java  Jar` 包打包成一个标准格式的 `Ark Plugin`； `Ark Plugin` 会包含一份配置文件，通常包括插件类导入导出配置、插件启动优先级等；运行时，Ark 容器会使用独立的 `PluginClassLoader` 加载插件，并根据插件配置构建类加载索引表，从而使插件与插件、插件与应用之间相互隔离；\n\n+ `Ark Biz`: Ark 业务模块，满足特定目录格式要求的 `Fat Jar` ，使用官方提供的 `Maven` 插件 `sofa-ark-maven-plugin` 可以将工程应用打包成一个标准格式的 `Ark-Biz` 包；是工程应用模块及其依赖包的组织单元，包含应用启动所需的所有依赖和配置；\n\n在运行时，`Ark Container` 优先启动，自动解析 classpath 包含的 `Ark Plugin` 和 `Ark Biz`，并读取他们的配置，构建类加载索引关系；然后使用独立的 ClassLoader 加载他们并按优先级配置依次启动；需要指出的是，`Ark Plugin` 优先 `Ark Biz` 被加载启动；`Ark Plugin` 之间是双向类索引关系，即可以相互委托对方加载所需的类；`Ark Plugin` 和 `Ark Biz` 是单向类索引关系，即只允许 `Ark Biz` 索引 `Ark Plugin` 加载的类，反之则不允许。\n\n## 场景\n### 包冲突\nSOFAArk初衷是为了解决包冲突问题，那什么情况下可以使用 SOFAArk 以及如何使用呢？ 假设如下场景，如果工程需要引入两个三方包：A 和 B，但是 A 需要依赖版本号为 0.1 的 C 包，而恰好 B 需要依赖版本号为 0.2 的 C 包，且 C 包的这两个版本无法兼容:\n\n![conflict](resource/SOFA-Ark-Conflict.png)\n\n此时，即可使用 SOFAArk 解决该依赖冲突问题；只需要把 A 和版本为 0.1 的 C 包一起打包成一个 `Ark Plugin`，然后让应用工程引入该插件依赖即可；\n\n### 合并部署\nSOFAArk 基于类隔离能力，实现了应用的合并部署，可以简单分为静态合并部署和动态合并部署，介绍如下。\n\n#### 静态合并部署\n在实际开发过程中，经常会出现多个团队合作开发同一款产品，他们各自负责不同的功能模块，这些功能模块通常可以独立开发，但是运行时需要作为一个整体的应用运行。在这种情况下，所有团队需要协商统一技术栈及各自的二方包版本，这无疑增加了开发和联调的成本。为了让开发人员专注自身功能业务的开发，理想情况下开发人员希望能像开发独立应用一样，仅定义好对外交互接口，而不用考虑和其他功能模块出现的版本冲突、技术栈不统一等问题。正是基于这种场景，SOFAArk 提供了静态合并部署能力，应用可以依赖其他应用打成的 Biz 包，而当自身被打成 Ark 包时，可以将其他应用 Biz 包一并打入，启动时，则会根据优先级依次启动各应用。由于每个应用使用独立的 BizClassLoader 加载，因此不需要考虑依赖冲突或者技术栈不统一问题。应用之间则通过 `SofaService/SofaReference` JVM 服务进行交互。\n\n#### 动态合并部署\n动态合并部署区别于静态合并部署最大的一点是，在应用运行时可以通过 API 或者配置中心（Zookeeper）来控制应用的部署和卸载。动态合并部署的设计理念图如下：\n\n![life-arch](resource/life-arch.png)\n\n无论是静态还是动态合并部署都会有宿主应用（master app）的概念, 如果 Ark 包只打包了一个 Biz，则该 Biz 默认成为宿主应用。如果 Ark 包打包了多个 Biz 包，需要配置指定宿主应用。宿主应用不允许被卸载，一般而言，宿主应用会作为流量入口的中台系统，具体的服务实现会放在不同的动态 Biz 中，供宿主应用调用。宿主应用可以使用 SOFAArk 提供的客户端 API 实现动态应用的部署和卸载。除了 API, SOFAArk 提供了 Config Plugin，用于对接配置中心（目前支持 Zookeeper），运行时接受动态配置。Config Plugin 会解析下发的配置，控制动态模块的部署和卸载。\n\n随着近几年 模块化、Serverless 技术的兴起，蚂蚁集团在应用研发领域进行了持续的建设和探索，基于 SOFAArk 动态合并部署技术打造了比较成熟的 Koupleless 技术体系，去深入解决企业的研发和运维效率问题。其核心方式是通过快速热部署、动态服务发布等技术，将应用从代码结构和开发者阵型划分为模块和基座。其中基座为业务模块提供计算环境并屏蔽基础设施，让模块开发者不用感知机器和容量等底层设施而专注于某个功能模块的开发迭代来帮助业务快速向前发展。\n\n在应用架构领域，不可避免的问题是应用随着业务的复杂度不断增加，研发运维的过程中的问题会不断暴露出来。首先我们看一下普通应用研发和运维过程中的流程是什么样的：\n\n![image](https://user-images.githubusercontent.com/101314559/172528801-065a18ee-3ce7-46b9-9b78-3f66f9955c97.png)\n\n如图所示，从需求到设计、开发、线下测试，再到发布线上的研发运维不断反馈、循环迭代的过程。可以简化为开发同学提交代码到代码仓库，在线下做并行的验证测试，测试通过之后在线上发布，发布过程是串行的，只能够有一个发布窗口，这样的过程在应用体量业务还不太复杂的情况下问题，并不是很明显。\n\n但当业务复杂度不断增加，普通应用迭代过程在会出现一些新的问题，如下图：\n\n![image](https://user-images.githubusercontent.com/101314559/172525833-311229f1-c631-4170-a16d-1e6d7550b6bc.png)\n\n1. 管理成本高：需求管理、代码管理、人员管理。\n2. 时间成本高：线上验证与发布互相阻塞。单次启动慢。\n3. 变更风险高：一次变更涉及所有代码。一次变更涉及所有机器。\n\n另外，由于这些问题是因为多个业务与研发任务耦合在某些单点上导致的，研发运维的成本随着业务的复杂度呈现出指数增长的特点：\n\n![image](https://user-images.githubusercontent.com/101314559/172529176-882bd36b-05a6-4450-aa53-24ef64a7e326.png)\n\n通过借助 SOFAArk 框架将应用拆分成基座和模块，同时将应用里的接口按场景维度做分组，使得业务可以按一组接口的粒度进行极速发布运维以及资源按需隔离。\n\n![image](https://user-images.githubusercontent.com/101314559/172529808-e09349c2-ff07-4431-8f5b-a1786cd0cfe5.png)\n\n从这张图里可以看到 Serverless 应用拆分的形态，通过把一个普通的 Java 应用拆出多个模块，进一步对应用进行了拆分：基座和模块，对应的研发人员也划分为基座开发者和模块开发者。\n\n基座负责沉淀通用的逻辑，为模块提供计算和环境，并为模块开发者屏蔽基础设施，让模块开发者不需要关心容量和资源等。各个模块则是独立的代码仓库，可以进行独立的研发运维，这样研发运维粒度就得到了精细化，并且由于基座为模块屏蔽了环境与基础设施，模块开发者可以专注于业务开发从而提高了业务创新效率。Koupleless 开源版首个 1.0 版本计划已经发布，欢迎大家一起来建设社区 Koupleless 模块化技术。\n\n\n\n## 快速开始\n* [基于多 Ark Plugin 解决类冲突](https://github.com/sofastack-guides/sofa-ark-class-isolation)\n* [基于普通的 Maven 应用构建 Ark Plugin](https://github.com/sofastack-guides/sofa-ark-samples/tree/master/sample-ark-plugin)\n \n## 社区\n* [Gitter channel](https://gitter.im/sofa-ark/Lobby) \n* [Issues](https://github.com/sofastack/sofa-ark/issues)\n* [钉钉群]\n<img src=\"https://user-images.githubusercontent.com/3754074/222353887-fe783ea4-cd49-428e-8a98-c59780a927f6.png\" width=\"300px\">\n\n\n## 贡献\n* [代码贡献](./CONTRIBUTING.md) : SOFAArk 开发参与说明书\n\n## 文档\n* [SOFAArk 用户手册(中文)](http://www.sofastack.tech/sofa-boot/docs/sofa-ark-readme) : SOFAArk 用户手册及功能特性详细说明\n* [SOFAArk2.0 升级](https://www.sofastack.tech/projects/sofa-boot/sofa-ark-migration-guide/) : SOFAArk1.0升级到2.0 操作详细说明\n\n\n## 致谢\nSOFAArk 类隔离框架设计实现主要基于 OSGi 规范及蚂蚁金服的 CloudEngine 容器；同时也参考了 Spring Boot 及阿里的 PandoraBoot，感谢以上产品工作者的辛勤付出。\n"
  },
  {
    "path": "README_EN.md",
    "content": "# SOFAArk Project\n\n[![Build Status](https://travis-ci.org/sofastack/sofa-ark.svg?branch=master)](https://travis-ci.org/sofastack/sofa-ark)\n[![Coverage Status](https://codecov.io/gh/sofastack/sofa-ark/branch/master/graph/badge.svg)](https://codecov.io/gh/sofastack/sofa-ark/branch/master/graph/badge.svg)\n[![Gitter](https://img.shields.io/badge/chat-on%20gitter-orange.svg)](https://gitter.im/sofa-ark/Lobby)\n![license](https://img.shields.io/badge/license-Apache--2.0-green.svg)\n![maven](https://img.shields.io/nexus/r/https/oss.sonatype.org/com.alipay.sofa/sofa-ark-all.svg)\n\n\nSOFAArk is a light-weight，java based classloader isolation framework \nopen sourced by Ant Financial. Please visit [https://alipay.github.io/sofastack.github.io/](https://alipay.github.io/sofastack.github.io/)\n\n## Background\n\nIn Java world, dependency is always a problem, and can cause various errors, such as `LinkageError`, `NoSuchMethodError` etc. There are many ways to solve the dependency problems, the Spring Boot's way is using a dependency management to manage all the dependencies, make sure that all the dependencies in the dependency management will not conflict and can work pretty well. This is quite a simple and efficient way, it can cover most scenario, but there is some exceptions.\n\nFor example, there is a project that need protobuf version 2 and protobuf version 3, and because protobuf version 3 is not compatible with version 2, so the project can not simply upgrade the protobuf to version 3 to solve the problem. There is same problem for hessian version 3 and version 4.\n\nTo cover those exceptions, we need to introduce a classloader isolation way, make different version of a framework loaded by different classloader. There are many framework that can do classloader isolation, perhaps the most famous one is OSGi, but OSGi classloader schema is too complex, beside classloader isolation, it also has ability to do hot deploy and a lot of other functionalities that we actually don't want.\n\nSo this is the origin of SOFAArk, it's goal is to use a light-weight classloader isolation mechanism to solve the problem that Spring Boot did not solve. And just a remind that SOFAArk is not bind to Spring Boot, actually it is a more general classloader isolation framework that can be used with any other frameworks too.\n\n## How SOFAArk Works\n\nThere are three concepts in SOFAArk: `Ark Container`, `Ark-Plugin` and `Ark-Biz`; they are organized as what the following graph shows:\n\n![framework](resource/SOFA-Ark-Framework.png)\n\nFirst of all, we explain what roles these concepts play;\n\n+ `Ark Container`: It's the runtime manager of total framework; it will startup in the first place, then it resolves `Ark Plugin` and `Ark Biz` in classpath and deploys them.\n\n+ `Ark Plugin`: A fat jar packaged by `sofa-ark-plugin-maven-plugin`, generally it would bring with a class-index configuration which describes what class would be exported and imported. `Ark Plugin` can resolve classes from each other.\n\n+ `Ark Biz`: A fat jar packaged by `sofa-ark-maven-plugin`, it mainly contains all staff what a project need in runtime. `Ark Biz` can resolve classes form `Ark Plugin`, but not inverse.\n\nIn runtime, `Ark Container` would automatically recognize `Ark-Plugin` and `Ark-Biz` in classpath, and load them with the independent classloader. According to configurations brought by `Ark Plugin` and `Ark Biz`, `Ark Container` would build a class-index table, so they can be\nisolated well. For example, if a project has two dependencies of A and B, but A depends on C (version = 0.1) and B depends on C (version = 0.2), so conflicts maybe emerge.\n\n![conflict](resource/SOFA-Ark-Conflict.png)\n\nIn this situation, we just repackage the dependencies of A and C(version=0.1) as an ark-plugin, and add the dependency of the `ark-plugin` to project, then this conflict would be avoided.\n\n## Sample\n\n* [Sample projects](sofa-ark-samples)\n    * [Ark Plugin Based On Maven Project](sofa-ark-samples/sample-ark-plugin) - Sample Project for Ark-Plugin\n    * [Ark Based On Spring Boot](sofa-ark-samples/sample-springboot-ark) Sample Project for Ark based on Spring Boot Project\n\n## Community\n\n* [Gitter channel](https://gitter.im/sofa-ark/Lobby) - Online chat room with SOFAArk developers.\n* [Issues](https://github.com/sofastack/sofa-ark/issues)\n\n## Contribution\n\n* [Contributing](./CONTRIBUTING.md) : Guides for contributing to SOFAArk.\n\n## Documentation\n\n* [SOFAArk 用户手册(中文)](https://alipay.github.io/sofastack.github.io/docs/): Describe how to used SOFAArk and its features.\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Reporting a Vulnerability\n\nIf you have apprehensions regarding SOFAStack's security or you discover vulnerability or potential threat, don’t hesitate to get in touch with us by dropping a mail at sofastack@antgroup.com.\n\nIn the mail, specify the description of the issue or potential threat. You are also urged to recommend the way to reproduce and replicate the issue. The SOFAStack community will get back to you after assessing and analysing the findings.\n\nPLEASE PAY ATTENTION to report the security issue on the security email before disclosing it on public domain.\n"
  },
  {
    "path": "change_version.sh",
    "content": "#!/bin/bash\nshopt -s expand_aliases\nif [ ! -n \"$1\" ] ;then\n\techo \"Please enter a version\"\n \texit 1\t\nelse\n  \techo \"The updated version is $1 !\"\nfi\n\ncurrentVersion=`sed -n '/<revision>/p' pom.xml | cut -d '>' -f2 | cut -d '<' -f1`\necho \"The current version is $currentVersion\"\n\nif [ `uname` == \"Darwin\" ] ;then\n \techo \"This is OS X\"\n \talias sed='sed -i \"\"'\nelse\n \techo \"This is Linux\"\n \talias sed='sed -i'\nfi\n\nfor filename in `find . -name \"README*.md\"`;do\n\techo \"Deal with $filename\"\n\tsed \"/badge\\/maven/! s/$currentVersion/$1/\" $filename\ndone"
  },
  {
    "path": "check_format.sh",
    "content": "#!/bin/sh\n\nBASEDIR=$(dirname $0)\n\ncd ${BASEDIR}\n\n# make sure git has no un commit files\nif [ -n \"$(git status --untracked-files=no --porcelain)\" ]; then\n   echo \"Please commit your change before run this shell, un commit files:\"\n   git status --untracked-files=no --porcelain\n   echo \"Please run ## mvn clean install -DskipTests -Dmaven.javadoc.skip=true -B -U && sh ./check_format.sh ## locally, then push it.\"\n   exit -1\nfi"
  },
  {
    "path": "codecov.yml",
    "content": "ignore:\n  - \"pom.xml\""
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.alipay.sofa</groupId>\n    <artifactId>sofa-ark</artifactId>\n    <version>${sofa.ark.version}</version>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>sofa-ark-bom</module>\n        <module>sofa-ark-parent</module>\n        <module>sofa-ark-plugin</module>\n    </modules>\n\n    <properties>\n        <sofa.ark.version>2.3.2</sofa.ark.version>\n        <sofa.ark.version.old>2.3.0</sofa.ark.version.old>\n        <project.encoding>UTF-8</project.encoding>\n        <java.version>1.8</java.version>\n        <license.maven.plugin>3.0</license.maven.plugin>\n        <maven.java.formatter.plugin>0.4</maven.java.formatter.plugin>\n        <maven.compiler.plugin>3.1</maven.compiler.plugin>\n        <maven.source.plugin>3.0.0</maven.source.plugin>\n        <maven.javadoc.plugin>3.2.0</maven.javadoc.plugin>\n        <maven.staging.plugin>1.6.13</maven.staging.plugin>\n        <maven.gpg.pluign>1.6</maven.gpg.pluign>\n        <jacoco.maven.plugin>0.8.4</jacoco.maven.plugin>\n        <flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version>\n    </properties>\n\n    <name>${project.groupId}:${project.artifactId}</name>\n    <description>A light-weight, java based classloader-isolated framework open-sourced by Ant Financial.</description>\n    <url>https://github.com/sofastack/sofa-ark</url>\n\n    <licenses>\n        <license>\n            <name>The Apache License, Version 2.0</name>\n            <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>\n        </license>\n    </licenses>\n\n    <developers>\n        <developer>\n            <name>qilong</name>\n            <email>qilong.zql@antfin.com</email>\n            <organization>Ant Financial</organization>\n            <organizationUrl>https://www.alipay.com/</organizationUrl>\n        </developer>\n        <developer>\n            <name>abby.zh</name>\n            <email>abby.zh@antfin.com</email>\n            <organization>Ant Financial</organization>\n            <organizationUrl>https://www.alipay.com/</organizationUrl>\n        </developer>\n    </developers>\n\n    <scm>\n        <connection>scm:git:git://github.com/sofastack/sofa-ark.git</connection>\n        <developerConnection>scm:git:ssh://github.com/sofastack/sofa-ark.git</developerConnection>\n        <url>http://github.com/sofastack/sofa-ark/tree/master</url>\n    </scm>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>com.mycila</groupId>\n                <artifactId>license-maven-plugin</artifactId>\n                <version>${license.maven.plugin}</version>\n                <executions>\n                    <execution>\n                        <phase>generate-sources</phase>\n                        <goals>\n                            <goal>remove</goal>\n                            <goal>format</goal>\n                        </goals>\n                    </execution>\n                </executions>\n                <configuration>\n                    <quiet>true</quiet>\n                    <header>${user.dir}/HEADER</header>\n                    <includes>\n                        <include>**/src/main/java/**</include>\n                        <include>**/src/test/java/**</include>\n                    </includes>\n                    <strictCheck>true</strictCheck>\n                    <mapping>\n                        <java>SLASHSTAR_STYLE</java>\n                    </mapping>\n                </configuration>\n            </plugin>\n\n            <plugin>\n                <groupId>com.googlecode.maven-java-formatter-plugin</groupId>\n                <artifactId>maven-java-formatter-plugin</artifactId>\n                <version>${maven.java.formatter.plugin}</version>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>format</goal>\n                        </goals>\n                    </execution>\n                </executions>\n                <configuration>\n                    <configFile>${user.dir}/Formatter.xml</configFile>\n                    <encoding>${project.encoding}</encoding>\n                </configuration>\n            </plugin>\n\n            <plugin>\n                <groupId>org.codehaus.mojo</groupId>\n                <artifactId>flatten-maven-plugin</artifactId>\n                <version>${flatten-maven-plugin.version}</version>\n                <executions>\n                    <execution>\n                        <id>flatten</id>\n                        <phase>process-resources</phase>\n                        <goals>\n                            <goal>flatten</goal>\n                        </goals>\n                    </execution>\n                    <execution>\n                        <id>flatten.clean</id>\n                        <phase>clean</phase>\n                        <goals>\n                            <goal>clean</goal>\n                        </goals>\n                    </execution>\n                </executions>\n                <inherited>true</inherited>\n                <configuration>\n                    <!-- 避免IDE将 .flattened-pom.xml 自动识别为功能模块 -->\n                    <updatePomFile>true</updatePomFile>\n                    <flattenMode>resolveCiFriendliesOnly</flattenMode>\n                </configuration>\n            </plugin>\n\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>${maven.compiler.plugin}</version>\n                <configuration>\n                    <encoding>${project.encoding}</encoding>\n                    <source>${java.version}</source>\n                    <target>${java.version}</target>\n                </configuration>\n            </plugin>\n\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-source-plugin</artifactId>\n                <version>${maven.source.plugin}</version>\n                <executions>\n                    <execution>\n                        <id>attach-sources</id>\n                        <goals>\n                            <goal>jar</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-javadoc-plugin</artifactId>\n                <version>${maven.javadoc.plugin}</version>\n                <executions>\n                    <execution>\n                        <id>attach-javadocs</id>\n                        <goals>\n                            <goal>jar</goal>\n                        </goals>\n                        <configuration>\n                            <docencoding>UTF-8</docencoding>\n                            <charset>UTF-8</charset>\n                            <encoding>UTF-8</encoding>\n                            <doclint>none</doclint>\n                        </configuration>\n                    </execution>\n                </executions>\n            </plugin>\n\n            <plugin>\n                <groupId>org.jacoco</groupId>\n                <artifactId>jacoco-maven-plugin</artifactId>\n                <version>${jacoco.maven.plugin}</version>\n                <configuration>\n                    <skip>false</skip>\n                    <!-- this configuration affects all goals -->\n                    <excludes>\n                        <exclude>com/alipay/sofa/ark/config/*.class</exclude>\n                        <exclude>com/alipay/sofa/ark/config/util/*.class</exclude>\n                        <exclude>com/alipay/sofa/ark/config/zk/*.class</exclude>\n                        <exclude>com/alipay/sofa/ark/support/listener/ArkTestNG*.class</exclude>\n                        <exclude>com/alipay/sofa/ark/common/log/ArkLogger.class</exclude>\n                    </excludes>\n                </configuration>\n                <executions>\n                    <execution>\n                        <id>default-prepare-agent</id>\n                        <goals>\n                            <goal>prepare-agent</goal>\n                        </goals>\n                    </execution>\n                    <execution>\n                        <id>default-report</id>\n                        <phase>test</phase>\n                        <goals>\n                            <goal>report-aggregate</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n\n    <profiles>\n        <profile>\n            <id>release</id>\n            <build>\n                <plugins>\n                    <plugin>\n                        <groupId>org.sonatype.plugins</groupId>\n                        <artifactId>nexus-staging-maven-plugin</artifactId>\n                        <version>${maven.staging.plugin}</version>\n                        <extensions>true</extensions>\n                        <configuration>\n                            <serverId>ossrh</serverId>\n                            <nexusUrl>https://ossrh-staging-api.central.sonatype.com/</nexusUrl>\n                            <autoReleaseAfterClose>false</autoReleaseAfterClose>\n                        </configuration>\n                    </plugin>\n                    <plugin>\n                        <groupId>org.apache.maven.plugins</groupId>\n                        <artifactId>maven-gpg-plugin</artifactId>\n                        <version>${maven.gpg.pluign}</version>\n                        <executions>\n                            <execution>\n                                <id>sign-artifacts</id>\n                                <phase>verify</phase>\n                                <goals>\n                                    <goal>sign</goal>\n                                </goals>\n                            </execution>\n                        </executions>\n                        <configuration>\n                            <gpgArguments>\n                                <arg>--pinentry-mode</arg>\n                                <arg>loopback</arg>\n                            </gpgArguments>\n                        </configuration>\n                    </plugin>\n                </plugins>\n            </build>\n            <distributionManagement>\n                <repository>\n                    <id>ossrh</id>\n                    <url>https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/</url>\n                </repository>\n            </distributionManagement>\n            <pluginRepositories>\n                <pluginRepository>\n                    <snapshots>\n                        <enabled>true</enabled>\n                    </snapshots>\n                    <id>maven-snapshot</id>\n                    <url>https://central.sonatype.com/repository/maven-snapshots/</url>\n                </pluginRepository>\n            </pluginRepositories>\n        </profile>\n\n        <profile>\n            <id>snapshot</id>\n            <build>\n                <plugins>\n                    <plugin>\n                        <groupId>org.sonatype.plugins</groupId>\n                        <artifactId>nexus-staging-maven-plugin</artifactId>\n                        <version>${maven.staging.plugin}</version>\n                        <extensions>true</extensions>\n                        <configuration>\n                            <serverId>ossrh</serverId>\n                            <nexusUrl>https://ossrh-staging-api.central.sonatype.com/</nexusUrl>\n                            <autoReleaseAfterClose>false</autoReleaseAfterClose>\n                        </configuration>\n                    </plugin>\n                    <plugin>\n                        <groupId>org.apache.maven.plugins</groupId>\n                        <artifactId>maven-gpg-plugin</artifactId>\n                        <version>${maven.gpg.pluign}</version>\n                        <executions>\n                            <execution>\n                                <id>sign-artifacts</id>\n                                <phase>verify</phase>\n                                <goals>\n                                    <goal>sign</goal>\n                                </goals>\n                            </execution>\n                        </executions>\n                        <configuration>\n                            <gpgArguments>\n                                <arg>--pinentry-mode</arg>\n                                <arg>loopback</arg>\n                            </gpgArguments>\n                        </configuration>\n                    </plugin>\n                </plugins>\n            </build>\n            <distributionManagement>\n                <snapshotRepository>\n                    <id>ossrh</id>\n                    <url>https://central.sonatype.com/repository/maven-snapshots/</url>\n                </snapshotRepository>\n                <repository>\n                    <id>ossrh</id>\n                    <url>https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/</url>\n                </repository>\n            </distributionManagement>\n            <repositories>\n                <repository>\n                    <snapshots>\n                        <enabled>true</enabled>\n                    </snapshots>\n                    <id>maven-snapshot</id>\n                    <url>https://central.sonatype.com/repository/maven-snapshots/</url>\n                </repository>\n            </repositories>\n            <pluginRepositories>\n                <pluginRepository>\n                    <snapshots>\n                        <enabled>true</enabled>\n                    </snapshots>\n                    <id>maven-snapshot</id>\n                    <url>https://central.sonatype.com/repository/maven-snapshots/</url>\n                </pluginRepository>\n            </pluginRepositories>\n        </profile>\n\n        <profile>\n            <id>default</id>\n            <activation>\n                <activeByDefault>true</activeByDefault>\n            </activation>\n            <repositories>\n                <repository>\n                    <snapshots>\n                        <enabled>true</enabled>\n                    </snapshots>\n                    <id>maven-snapshot</id>\n                    <url>https://oss.sonatype.org/content/repositories/snapshots</url>\n                </repository>\n            </repositories>\n            <pluginRepositories>\n                <pluginRepository>\n                    <snapshots>\n                        <enabled>true</enabled>\n                    </snapshots>\n                    <id>maven-snapshot</id>\n                    <url>https://oss.sonatype.org/content/repositories/snapshots</url>\n                </pluginRepository>\n            </pluginRepositories>\n        </profile>\n    </profiles>\n\n</project>\n"
  },
  {
    "path": "sofa-ark-bom/CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Module Overview\n\n**Artifact ID**: `sofa-ark-bom`\n**Packaging**: `pom`\n\nThis is the Bill of Materials (BOM) module for SOFAArk. It provides centralized dependency version management for the entire SOFAArk project.\n\n## Purpose\n\n- Centralized version management for all SOFAArk modules and third-party dependencies\n- Ensures consistent dependency versions across all modules\n- Simplifies dependency declarations in other modules\n\n## Key Dependencies Managed\n\n### SOFAArk Modules\n- `sofa-ark-all`, `sofa-ark-spi`, `sofa-ark-api`, `sofa-ark-container`, `sofa-ark-archive`\n- `sofa-ark-common`, `sofa-ark-exception`\n- Maven plugins: `sofa-ark-maven-plugin`, `sofa-ark-plugin-maven-plugin`\n- Spring Boot integration modules\n\n### Third-Party Libraries\n- Google Guice 6.0.0 (DI framework)\n- Guava 33.0.0-jre\n- ASM 9.4 (bytecode manipulation)\n- Spring Boot 2.7.14\n- Logback 1.2.13, SLF4J 1.7.32\n- Maven Core/Plugin dependencies 3.8.1\n- Netty 4.1.109.Final\n- JGit 5.13.3\n\n## Usage\n\nOther SOFAArk modules inherit from this BOM:\n\n```xml\n<parent>\n    <artifactId>sofa-ark-bom</artifactId>\n    <groupId>com.alipay.sofa</groupId>\n    <version>${sofa.ark.version}</version>\n</parent>\n```\n\n## When to Modify\n\n- Adding new third-party dependencies that are shared across modules\n- Updating dependency versions\n- Adding new SOFAArk modules to dependency management"
  },
  {
    "path": "sofa-ark-bom/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>sofa-ark</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>sofa-ark-bom</artifactId>\n    <packaging>pom</packaging>\n    <name>${project.groupId}:${project.artifactId}</name>\n\n    <properties>\n        <log4j.version>1.7.32</log4j.version>\n        <log.sofa.starter.version>3.2.0</log.sofa.starter.version>\n        <logback.version>1.2.13</logback.version>\n\n        <guice.version>6.0.0</guice.version>\n        <asm.version>9.4</asm.version>\n        <commons.io.version>2.7</commons.io.version>\n        <commons.lang3.version>3.3.1</commons.lang3.version>\n\n        <maven.invoker.version>3.1.0</maven.invoker.version>\n        <maven.core.version>3.8.1</maven.core.version>\n        <maven.artifact.version>3.8.1</maven.artifact.version>\n        <maven.archiver.version>3.5.1</maven.archiver.version>\n        <maven.model.version>3.8.1</maven.model.version>\n        <maven.plugin.api.version>3.8.1</maven.plugin.api.version>\n        <maven.plugin.annotations.version>3.6.1</maven.plugin.annotations.version>\n        <maven.dependency.plugin.version>3.2.0</maven.dependency.plugin.version>\n        <maven.common.artifact.filters.version>3.2.0</maven.common.artifact.filters.version>\n\n        <plexus.utils.version>3.3.0</plexus.utils.version>\n        <plexus.build.api.version>0.0.7</plexus.build.api.version>\n        <plexus.component.annotations.version>2.1.1</plexus.component.annotations.version>\n\n        <eclipse.jgit.version>5.13.3.202401111512-r</eclipse.jgit.version>\n\n        <junit.version>4.13.1</junit.version>\n        <testng.version>6.14.3</testng.version>\n        <mockito.version>3.6.0</mockito.version>\n        <system.rules.version>1.16.0</system.rules.version>\n\n        <maven.assembly.plugin>2.4</maven.assembly.plugin>\n        <maven.plugin.plugin>3.6.1</maven.plugin.plugin>\n        <surefire.version>2.22.2</surefire.version>\n        <netty.version>4.1.109.Final</netty.version>\n        <reactor-netty.version>0.9.19.RELEASE</reactor-netty.version>\n        <spring.boot.version>2.7.14</spring.boot.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot</artifactId>\n                <version>${spring.boot.version}</version>\n            </dependency>\n\n            <!-- SOFAArk modules -->\n            <dependency>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>sofa-ark-all</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>sofa-ark-core</artifactId>\n                <version>${project.version}</version>\n                <type>pom</type>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>sofa-ark-core-impl</artifactId>\n                <version>${project.version}</version>\n                <type>pom</type>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>sofa-ark-support</artifactId>\n                <version>${project.version}</version>\n                <type>pom</type>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>sofa-ark-common</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>sofa-ark-exception</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>sofa-ark-spi</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>sofa-ark-api</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>sofa-ark-archive</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>sofa-ark-container</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>sofa-ark-support-starter</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>sofa-ark-maven-plugin</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>sofa-ark-plugin-maven-plugin</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>sofa-ark-springboot-starter</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>sofa-ark-tools</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>sofa-ark-common-springboot</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>sofa-ark-compatible-springboot1</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>sofa-ark-compatible-springboot2</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>config-ark-plugin</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>web-ark-plugin</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <!--third party libraries-->\n            <dependency>\n                <groupId>com.google.inject</groupId>\n                <artifactId>guice</artifactId>\n                <version>${guice.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.google.guava</groupId>\n                <artifactId>guava</artifactId>\n                <version>33.0.0-jre</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.ow2.asm</groupId>\n                <artifactId>asm</artifactId>\n                <version>${asm.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>commons-io</groupId>\n                <artifactId>commons-io</artifactId>\n                <version>${commons.io.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.apache.commons</groupId>\n                <artifactId>commons-lang3</artifactId>\n                <version>${commons.lang3.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>io.netty</groupId>\n                <artifactId>netty-all</artifactId>\n                <version>${netty.version}</version>\n            </dependency>\n\n            <!-- log -->\n            <dependency>\n                <groupId>org.slf4j</groupId>\n                <artifactId>slf4j-api</artifactId>\n                <version>${log4j.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>ch.qos.logback</groupId>\n                <artifactId>logback-classic</artifactId>\n                <version>${logback.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>ch.qos.logback</groupId>\n                <artifactId>logback-core</artifactId>\n                <version>${logback.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>log-sofa-boot-starter</artifactId>\n                <version>${log.sofa.starter.version}</version>\n                <exclusions>\n                    <exclusion>\n                        <groupId>org.springframework.boot</groupId>\n                        <artifactId>spring-boot</artifactId>\n                    </exclusion>\n                </exclusions>\n            </dependency>\n\n            <!-- maven -->\n            <dependency>\n                <groupId>org.apache.maven.shared</groupId>\n                <artifactId>maven-invoker</artifactId>\n                <version>${maven.invoker.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.apache.maven</groupId>\n                <artifactId>maven-core</artifactId>\n                <version>${maven.core.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.apache.maven.plugin-tools</groupId>\n                <artifactId>maven-plugin-annotations</artifactId>\n                <version>${maven.plugin.annotations.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-dependency-plugin</artifactId>\n                <version>${maven.dependency.plugin.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.apache.maven</groupId>\n                <artifactId>maven-archiver</artifactId>\n                <version>${maven.archiver.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.apache.maven.shared</groupId>\n                <artifactId>maven-common-artifact-filters</artifactId>\n                <version>${maven.common.artifact.filters.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.apache.maven</groupId>\n                <artifactId>maven-plugin-api</artifactId>\n                <version>${maven.plugin.api.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.apache.maven</groupId>\n                <artifactId>maven-model</artifactId>\n                <version>${maven.model.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.apache.maven</groupId>\n                <artifactId>maven-artifact</artifactId>\n                <version>${maven.artifact.version}</version>\n            </dependency>\n\n            <!-- plexus -->\n            <dependency>\n                <groupId>org.codehaus.plexus</groupId>\n                <artifactId>plexus-component-annotations</artifactId>\n                <version>${plexus.component.annotations.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.codehaus.plexus</groupId>\n                <artifactId>plexus-utils</artifactId>\n                <version>${plexus.utils.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.sonatype.plexus</groupId>\n                <artifactId>plexus-build-api</artifactId>\n                <version>${plexus.build.api.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.eclipse.jgit</groupId>\n                <artifactId>org.eclipse.jgit</artifactId>\n                <version>${eclipse.jgit.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-loader</artifactId>\n                <version>${spring.boot.version}</version>\n            </dependency>\n\n            <!-- test -->\n            <dependency>\n                <groupId>junit</groupId>\n                <artifactId>junit</artifactId>\n                <version>${junit.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.testng</groupId>\n                <artifactId>testng</artifactId>\n                <version>${testng.version}</version>\n            </dependency>\n\n            <!-- Test Dependencies -->\n            <dependency>\n                <groupId>org.mockito</groupId>\n                <artifactId>mockito-inline</artifactId>\n                <version>${mockito.version}</version>\n                <scope>test</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.mockito</groupId>\n                <artifactId>mockito-core</artifactId>\n                <version>${mockito.version}</version>\n                <scope>test</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.github.stefanbirkner</groupId>\n                <artifactId>system-rules</artifactId>\n                <version>${system.rules.version}</version>\n                <scope>test</scope>\n            </dependency>\n\n            <dependency>\n                <groupId>com.fasterxml.jackson.core</groupId>\n                <artifactId>jackson-databind</artifactId>\n                <version>2.14.3</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.yaml</groupId>\n                <artifactId>snakeyaml</artifactId>\n                <version>2.1</version>\n            </dependency>\n\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <pluginManagement>\n            <plugins>\n                <plugin>\n                    <groupId>org.apache.maven.plugins</groupId>\n                    <artifactId>maven-assembly-plugin</artifactId>\n                    <version>${maven.assembly.plugin}</version>\n                </plugin>\n                <plugin>\n                    <groupId>org.apache.maven.plugins</groupId>\n                    <artifactId>maven-plugin-plugin</artifactId>\n                    <version>${maven.plugin.plugin}</version>\n                </plugin>\n                <plugin>\n                    <groupId>org.apache.maven.plugins</groupId>\n                    <artifactId>maven-surefire-plugin</artifactId>\n                    <version>${surefire.version}</version>\n                </plugin>\n            </plugins>\n        </pluginManagement>\n    </build>\n\n</project>\n"
  },
  {
    "path": "sofa-ark-parent/assembly/CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Module Overview\n\n**Artifact ID**: `sofa-ark-all`\n**Package**: Assembly module (no Java code)\n\nThis module aggregates all core SOFAArk modules into a single JAR that becomes the Ark Container classpath.\n\n## Purpose\n\n- Create the aggregated `sofa-ark-all.jar`\n- This JAR is packaged into every Ark executable JAR\n- Contains all core runtime dependencies\n\n## Included Modules\n\nThe assembly includes:\n- `sofa-ark-common` - Utilities\n- `sofa-ark-exception` - Exceptions\n- `sofa-ark-spi` - Service interfaces\n- `sofa-ark-api` - Public API\n- `sofa-ark-archive` - Archive handling\n- `sofa-ark-container` - Core container implementation\n\n## Build Configuration\n\nUses `maven-assembly-plugin` with:\n- Assembly descriptor: `src/main/assembly/assembly.xml`\n- Creates single JAR without classifier\n- Adds manifest entries: `ArkVersion`, `Timestamp`\n\n## Usage in Ark Packages\n\nWhen `sofa-ark-maven-plugin` builds an Ark package:\n1. Resolves `sofa-ark-all` artifact\n2. Packages it into `SOFA-ARK-CONTAINER/` directory\n3. This becomes the container classpath at runtime\n\n## Dependencies\n\nAll core modules are dependencies (see above)."
  },
  {
    "path": "sofa-ark-parent/assembly/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <artifactId>sofa-ark-parent</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n    </parent>\n\n    <artifactId>sofa-ark-all</artifactId>\n    <name>${project.groupId}:${project.artifactId}</name>\n\n    <properties>\n        <sofa.ark.name>sofa-ark-all</sofa.ark.name>\n    </properties>\n\n    <dependencies>\n\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-common</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-exception</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-spi</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-archive</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-container</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-assembly-plugin</artifactId>\n                <version>${maven.assembly.plugin}</version>\n                <executions>\n                    <execution>\n                        <id>make-assembly</id>\n                        <phase>package</phase>\n                        <goals>\n                            <goal>single</goal>\n                        </goals>\n                    </execution>\n                </executions>\n                <configuration>\n                    <finalName>${sofa.ark.name}</finalName>\n                    <descriptors>\n                        <descriptor>src/main/assembly/assembly.xml</descriptor>\n                    </descriptors>\n                    <archive>\n                        <manifest>\n                            <addDefaultImplementationEntries>true</addDefaultImplementationEntries>\n                            <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>\n                        </manifest>\n                        <manifestEntries>\n                            <ArkVersion>${project.version}</ArkVersion>\n                            <Timestamp>${maven.build.timestamp}</Timestamp>\n                        </manifestEntries>\n                    </archive>\n                    <attach>true</attach>\n                    <appendAssemblyId>false</appendAssemblyId>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "sofa-ark-parent/assembly/src/main/assembly/assembly.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<assembly\n        xmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2\"\n        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n        xsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd\">\n\n    <id>sofa-ark-container</id>\n    <formats>\n        <format>jar</format>\n        <format>dir</format>\n    </formats>\n    <includeBaseDirectory>false</includeBaseDirectory>\n    <dependencySets>\n        <dependencySet>\n            <useProjectArtifact>false</useProjectArtifact>\n            <outputDirectory>lib</outputDirectory>\n        </dependencySet>\n    </dependencySets>\n\n    <files>\n        <file>\n            <source>src/main/assembly/mark</source>\n            <outputDirectory>com/alipay/sofa/ark/container</outputDirectory>\n        </file>\n    </files>\n</assembly>\n\n"
  },
  {
    "path": "sofa-ark-parent/assembly/src/main/assembly/mark",
    "content": "a mark file included in sofa-ark-all.jar"
  },
  {
    "path": "sofa-ark-parent/core/api/CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Module Overview\n\n**Artifact ID**: `sofa-ark-api`\n**Package**: `com.alipay.sofa.ark.api`\n\nThis module provides the public API for SOFAArk operations. It is the main entry point for external code to interact with the Ark container runtime.\n\n## Purpose\n\n- Public API for managing Ark business modules (Biz)\n- Configuration management through `ArkConfigs`\n- Response models for API operations\n\n## Key Classes\n\n### `ArkClient`\nMain API for runtime operations on business modules:\n- `installBiz(File bizFile)` - Install a new business module\n- `uninstallBiz(String bizName, String bizVersion)` - Remove a business module\n- `switchBiz(String bizName, String bizVersion)` - Activate a specific biz version\n- `checkBiz()` - Query installed business modules\n- `installPlugin(PluginOperation)` - Install a plugin dynamically\n- `invocationReplay(String version, Replay replay)` - Invoke code with specific biz version context\n\n### `ArkConfigs`\nConfiguration management:\n- `getStringValue(String key)` - Get configuration value\n- `setSystemProperty(String key, String value)` - Set system properties\n\n### `ClientResponse`\nResponse wrapper for API operations with status code and message.\n\n### `ResponseCode`\nEnum defining response codes: `SUCCESS`, `FAILED`, `REPEAT_BIZ`, `NOT_FOUND_BIZ`, `ILLEGAL_STATE_BIZ`\n\n## Dependencies\n\n- `sofa-ark-spi` - Service Provider Interfaces\n- `sofa-ark-common` - Common utilities\n\n## Used By\n\n- `sofa-ark-container` - Implements the services behind this API\n- `config-ark-plugin` - Uses API for dynamic module management\n- Application code interacting with Ark runtime"
  },
  {
    "path": "sofa-ark-parent/core/api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>sofa-ark-core</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>sofa-ark-api</artifactId>\n    <name>${project.groupId}:${project.artifactId}</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-spi</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-common</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "sofa-ark-parent/core/api/src/main/java/com/alipay/sofa/ark/api/ArkClient.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.api;\n\nimport com.alipay.sofa.ark.common.log.ArkLogger;\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.common.util.AssertUtils;\nimport com.alipay.sofa.ark.common.util.BizIdentityUtils;\nimport com.alipay.sofa.ark.common.util.FileUtils;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.event.biz.AfterBizSwitchEvent;\nimport com.alipay.sofa.ark.spi.event.biz.BeforeBizSwitchEvent;\nimport com.alipay.sofa.ark.spi.model.*;\nimport com.alipay.sofa.ark.spi.replay.Replay;\nimport com.alipay.sofa.ark.spi.replay.ReplayContext;\nimport com.alipay.sofa.ark.spi.service.biz.BizFactoryService;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.event.EventAdminService;\nimport com.alipay.sofa.ark.spi.service.injection.InjectionService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginFactoryService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.text.SimpleDateFormat;\nimport java.util.*;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.AUTO_UNINSTALL_WHEN_FAILED_ENABLE;\n\n/**\n * API used to operate biz\n *\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class ArkClient {\n\n    private static BizManagerService    bizManagerService;\n    private static BizFactoryService    bizFactoryService;\n    private static PluginManagerService pluginManagerService;\n    private static PluginFactoryService pluginFactoryService;\n    private static Biz                  masterBiz;\n    private static InjectionService     injectionService;\n    private static String[]             arguments;\n\n    /**\n     * in some case like multi-tenant jdk, we need to set envs for biz\n     */\n    private static Map<String, String>  envs;\n\n    private static EventAdminService    eventAdminService;\n\n    private static File getBizInstallDirectory() {\n        String configDir = ArkConfigs.getStringValue(Constants.CONFIG_INSTALL_BIZ_DIR);\n        return StringUtils.isEmpty(configDir) ? FileUtils.createTempDir(\"sofa-ark\") : FileUtils\n            .mkdir(configDir);\n    }\n\n    public static File createBizSaveFile(String bizName, String bizVersion, String fileSuffix) {\n        String suffix = new SimpleDateFormat(\"yyyyMMddHHmmssSSS\").format(new Date());\n        if (!StringUtils.isEmpty(fileSuffix)) {\n            suffix = fileSuffix;\n        }\n        File bizInstallDirectory = getBizInstallDirectory();\n        return new File(bizInstallDirectory, bizName + \"-\" + bizVersion + \"-\" + suffix);\n    }\n\n    public static File createBizSaveFile(String bizName, String bizVersion) {\n        return createBizSaveFile(bizName, bizVersion, null);\n    }\n\n    public static InjectionService getInjectionService() {\n        return injectionService;\n    }\n\n    public static void setInjectionService(InjectionService injectionService) {\n        ArkClient.injectionService = injectionService;\n    }\n\n    public static BizManagerService getBizManagerService() {\n        return bizManagerService;\n    }\n\n    public static void setBizManagerService(BizManagerService bizManagerService) {\n        ArkClient.bizManagerService = bizManagerService;\n    }\n\n    public static BizFactoryService getBizFactoryService() {\n        return bizFactoryService;\n    }\n\n    public static void setBizFactoryService(BizFactoryService bizFactoryService) {\n        ArkClient.bizFactoryService = bizFactoryService;\n    }\n\n    public static void setPluginManagerService(PluginManagerService pluginManagerService) {\n        ArkClient.pluginManagerService = pluginManagerService;\n    }\n\n    public static PluginManagerService getPluginManagerService() {\n        return pluginManagerService;\n    }\n\n    public static PluginFactoryService getPluginFactoryService() {\n        return pluginFactoryService;\n    }\n\n    public static void setPluginFactoryService(PluginFactoryService pluginFactoryService) {\n        ArkClient.pluginFactoryService = pluginFactoryService;\n    }\n\n    public static Biz getMasterBiz() {\n        return masterBiz;\n    }\n\n    public static void setMasterBiz(Biz masterBiz) {\n        ArkClient.masterBiz = masterBiz;\n    }\n\n    public static EventAdminService getEventAdminService() {\n        return eventAdminService;\n    }\n\n    public static void setEventAdminService(EventAdminService eventAdminService) {\n        ArkClient.eventAdminService = eventAdminService;\n    }\n\n    public static String[] getArguments() {\n        return arguments;\n    }\n\n    public static void setArguments(String[] arguments) {\n        ArkClient.arguments = arguments;\n    }\n\n    public static Map<String, String> getEnvs() {\n        return envs;\n    }\n\n    public static void setEnvs(Map<String, String> envs) {\n        ArkClient.envs = envs;\n    }\n\n    /**\n     * Install Biz with default arguments and envs throw file\n     *\n     * @param bizFile\n     * @throws Throwable\n     */\n    public static ClientResponse installBiz(File bizFile) throws Throwable {\n        return installBiz(bizFile, arguments, envs);\n    }\n\n    public static ClientResponse installBiz(File bizFile, String[] args) throws Throwable {\n        return installBiz(bizFile, args, null);\n    }\n\n    public static ClientResponse installBiz(File bizFile, String[] args, Map<String, String> envs)\n                                                                                                  throws Throwable {\n        BizConfig bizConfig = new BizConfig();\n        bizConfig.setArgs(args);\n        bizConfig.setEnvs(envs);\n        return doInstallBiz(bizFile, bizConfig);\n    }\n\n    public static ClientResponse installBiz(File bizFile, BizConfig bizConfig) throws Throwable {\n        return doInstallBiz(bizFile, bizConfig);\n    }\n\n    private static ClientResponse doInstallBiz(File bizFile, BizConfig bizConfig) throws Throwable {\n        AssertUtils.assertNotNull(bizFactoryService, \"bizFactoryService must not be null!\");\n        AssertUtils.assertNotNull(bizManagerService, \"bizManagerService must not be null!\");\n        AssertUtils.assertNotNull(bizFile, \"bizFile must not be null!\");\n        AssertUtils.assertNotNull(bizConfig, \"bizConfig must not be null!\");\n\n        long start = System.currentTimeMillis();\n        SimpleDateFormat sdf = new SimpleDateFormat(\"HH:mm:ss,SSS\");\n        String startDate = sdf.format(new Date(start));\n\n        Biz biz = bizFactoryService.createBiz(bizFile, bizConfig);\n        ClientResponse response = new ClientResponse();\n        if (bizManagerService.getBizByIdentity(biz.getIdentity()) != null\n            || !bizManagerService.registerBiz(biz)) {\n            return response.setCode(ResponseCode.REPEAT_BIZ).setMessage(\n                String.format(\"Biz: %s has been installed or registered.\", biz.getIdentity()));\n        }\n\n        try {\n            biz.start(bizConfig.getArgs(), bizConfig.getEnvs());\n            long end = System.currentTimeMillis();\n            response\n                .setCode(ResponseCode.SUCCESS)\n                .setMessage(\n                    String.format(\"Install Biz: %s success, cost: %s ms, started at: %s\",\n                        biz.getIdentity(), end - start, startDate))\n                .setBizInfos(Collections.<BizInfo> singleton(biz));\n            getLogger().info(response.getMessage());\n            return response;\n        } catch (Throwable throwable) {\n            long end = System.currentTimeMillis();\n            response.setCode(ResponseCode.FAILED).setMessage(\n                String.format(\"Install Biz: %s fail,cost: %s ms, started at: %s\",\n                    biz.getIdentity(), end - start, startDate));\n            getLogger().error(response.getMessage(), throwable);\n\n            boolean autoUninstall = Boolean.parseBoolean(ArkConfigs.getStringValue(\n                AUTO_UNINSTALL_WHEN_FAILED_ENABLE, \"true\"));\n            if (autoUninstall) {\n                try {\n                    getLogger().error(\n                        String.format(\"Start Biz: %s failed, try to unInstall this biz.\",\n                            biz.getIdentity()));\n                    biz.stop();\n                } catch (Throwable e) {\n                    getLogger().error(String.format(\"UnInstall Biz: %s fail.\", biz.getIdentity()),\n                        e);\n                }\n            }\n            throw throwable;\n        }\n    }\n\n    /**\n     * Uninstall biz.\n     *\n     * @param bizName\n     * @param bizVersion\n     * @return\n     * @throws Throwable\n     */\n    public static ClientResponse uninstallBiz(String bizName, String bizVersion) throws Throwable {\n        AssertUtils.assertNotNull(bizFactoryService, \"bizFactoryService must not be null!\");\n        AssertUtils.assertNotNull(bizManagerService, \"bizManagerService must not be null!\");\n        AssertUtils.assertNotNull(bizName, \"bizName must not be null!\");\n        AssertUtils.assertNotNull(bizVersion, \"bizVersion must not be null!\");\n\n        // ignore when uninstall master biz\n        if (bizName.equals(ArkConfigs.getStringValue(Constants.MASTER_BIZ))) {\n            return new ClientResponse().setCode(ResponseCode.FAILED).setMessage(\n                \"Master biz must not be uninstalled.\");\n        }\n\n        Biz biz = bizManagerService.getBiz(bizName, bizVersion);\n        ClientResponse response = new ClientResponse().setCode(ResponseCode.NOT_FOUND_BIZ)\n            .setMessage(\n                String.format(\"Uninstall biz: %s not found.\",\n                    BizIdentityUtils.generateBizIdentity(bizName, bizVersion)));\n        if (biz != null) {\n            try {\n                biz.stop();\n            } catch (Throwable throwable) {\n                getLogger().error(String.format(\"UnInstall Biz: %s fail.\", biz.getIdentity()),\n                    throwable);\n                throw throwable;\n            }\n            response.setCode(ResponseCode.SUCCESS).setMessage(\n                String.format(\"Uninstall biz: %s success.\", biz.getIdentity()));\n        }\n        getLogger().info(response.getMessage());\n        return response;\n    }\n\n    /**\n     * Check all {@link com.alipay.sofa.ark.spi.model.BizInfo}\n     *\n     * @return\n     */\n    public static ClientResponse checkBiz() {\n        return checkBiz(null, null);\n    }\n\n    /**\n     * Check all {@link com.alipay.sofa.ark.spi.model.BizInfo} with specified bizName\n     *\n     * @param bizName\n     * @return\n     */\n    public static ClientResponse checkBiz(String bizName) {\n        return checkBiz(bizName, null);\n    }\n\n    /**\n     * Check all {@link com.alipay.sofa.ark.spi.model.BizInfo} with specified bizName and bizVersion\n     *\n     * @param bizName\n     * @param bizVersion\n     * @return\n     */\n    public static ClientResponse checkBiz(String bizName, String bizVersion) {\n        AssertUtils.assertNotNull(bizFactoryService, \"bizFactoryService must not be null!\");\n        AssertUtils.assertNotNull(bizManagerService, \"bizManagerService must not be null!\");\n\n        ClientResponse response = new ClientResponse();\n        Set<BizInfo> bizInfoSet = new HashSet<>();\n        if (bizName != null && bizVersion != null) {\n            Biz biz = bizManagerService.getBiz(bizName, bizVersion);\n            if (biz != null) {\n                bizInfoSet.add(biz);\n            }\n        } else if (bizName != null) {\n            bizInfoSet.addAll(bizManagerService.getBiz(bizName));\n        } else {\n            bizInfoSet.addAll(bizManagerService.getBizInOrder());\n        }\n\n        StringBuilder sb = new StringBuilder();\n        sb.append(String.format(\"Biz count=%d\", bizInfoSet.size())).append(\"\\n\");\n        for (BizInfo bizInfo : bizInfoSet) {\n            sb.append(\n                String.format(\"bizName=%s, bizVersion=%s, bizState=%s\", bizInfo.getBizName(),\n                    bizInfo.getBizVersion(), bizInfo.getBizState())).append(\"\\n\");\n        }\n        response.setCode(ResponseCode.SUCCESS).setBizInfos(bizInfoSet).setMessage(sb.toString());\n        getLogger().info(String.format(\"Check Biz: %s\", response.getMessage()));\n        return response;\n    }\n\n    /**\n     * Active biz with specified bizName and bizVersion\n     *\n     * @param bizName\n     * @param bizVersion\n     * @return\n     */\n    public static ClientResponse switchBiz(String bizName, String bizVersion) {\n        AssertUtils.assertNotNull(bizFactoryService, \"bizFactoryService must not be null!\");\n        AssertUtils.assertNotNull(bizManagerService, \"bizManagerService must not be null!\");\n        AssertUtils.assertNotNull(bizName, \"bizName must not be null!\");\n        AssertUtils.assertNotNull(bizVersion, \"bizVersion must not be null!\");\n        Biz biz = bizManagerService.getBiz(bizName, bizVersion);\n        ClientResponse response = new ClientResponse().setCode(ResponseCode.NOT_FOUND_BIZ)\n            .setMessage(\n                String.format(\"Switch biz: %s not found.\",\n                    BizIdentityUtils.generateBizIdentity(bizName, bizVersion)));\n        if (biz != null) {\n            if (biz.getBizState() != BizState.ACTIVATED\n                && biz.getBizState() != BizState.DEACTIVATED) {\n                response.setCode(ResponseCode.ILLEGAL_STATE_BIZ).setMessage(\n                    String.format(\"Switch Biz: %s's state must not be %s.\", biz.getIdentity(),\n                        biz.getBizState()));\n            } else {\n                eventAdminService.sendEvent(new BeforeBizSwitchEvent(biz));\n                bizManagerService.activeBiz(bizName, bizVersion);\n                eventAdminService.sendEvent(new AfterBizSwitchEvent(biz));\n                response.setCode(ResponseCode.SUCCESS).setMessage(\n                    String.format(\"Switch biz: %s is activated.\", biz.getIdentity()));\n            }\n        }\n        getLogger().info(response.getMessage());\n        return response;\n    }\n\n    public static ClientResponse installOperation(BizOperation bizOperation) throws Throwable {\n        return doInstallOperation(bizOperation, arguments, envs);\n    }\n\n    public static ClientResponse installOperation(BizOperation bizOperation, String[] args)\n                                                                                           throws Throwable {\n        return doInstallOperation(bizOperation, args, null);\n    }\n\n    public static ClientResponse installOperation(BizOperation bizOperation, String[] args,\n                                                  Map<String, String> envs) throws Throwable {\n        return doInstallOperation(bizOperation, args, envs);\n    }\n\n    private static ClientResponse doInstallOperation(BizOperation bizOperation, String[] args,\n                                                     Map<String, String> envs) throws Throwable {\n        AssertUtils.isTrue(\n            BizOperation.OperationType.INSTALL.equals(bizOperation.getOperationType()),\n            \"Operation type must be install\");\n        File bizFile = null;\n        if (bizOperation.getParameters().get(Constants.CONFIG_BIZ_URL) != null) {\n            URL url = new URL(bizOperation.getParameters().get(Constants.CONFIG_BIZ_URL));\n            bizFile = ArkClient.createBizSaveFile(bizOperation.getBizName(),\n                bizOperation.getBizVersion());\n\n            try (InputStream inputStream = url.openStream()) {\n                FileUtils.copyInputStreamToFile(inputStream, bizFile);\n            }\n        }\n\n        // prepare extension urls if necessary\n        URL[] extensionUrls = null;\n        if (bizOperation.getParameters().get(Constants.BIZ_EXTENSION_URLS) != null) {\n            Set<String> extensionLibs = StringUtils.strToSet(\n                bizOperation.getParameters().get(Constants.BIZ_EXTENSION_URLS),\n                Constants.COMMA_SPLIT);\n            List<URL> urlsList = new ArrayList<>();\n            if (!extensionLibs.isEmpty()) {\n                for (String extension : extensionLibs) {\n                    URL url = new URL(extension);\n                    urlsList.add(url);\n                }\n            }\n            extensionUrls = urlsList.toArray(new URL[0]);\n        }\n\n        BizConfig bizConfig = new BizConfig();\n        bizConfig.setExtensionUrls(extensionUrls);\n        bizConfig.setArgs(args);\n        bizConfig.setEnvs(envs);\n        return installBiz(bizFile, bizConfig);\n    }\n\n    public static ClientResponse uninstallOperation(BizOperation bizOperation) throws Throwable {\n        AssertUtils.isTrue(\n            BizOperation.OperationType.UNINSTALL.equals(bizOperation.getOperationType()),\n            \"Operation type must be uninstall\");\n        return uninstallBiz(bizOperation.getBizName(), bizOperation.getBizVersion());\n    }\n\n    public static ClientResponse switchOperation(BizOperation bizOperation) {\n        AssertUtils.isTrue(\n            BizOperation.OperationType.SWITCH.equals(bizOperation.getOperationType()),\n            \"Operation type must be switch\");\n        return switchBiz(bizOperation.getBizName(), bizOperation.getBizVersion());\n    }\n\n    public static ClientResponse checkOperation(BizOperation bizOperation) {\n        AssertUtils.isTrue(\n            BizOperation.OperationType.CHECK.equals(bizOperation.getOperationType()),\n            \"Operation type must be check\");\n        return checkBiz(bizOperation.getBizName(), bizOperation.getBizVersion());\n    }\n\n    public static ClientResponse installPlugin(PluginOperation pluginOperation) throws Exception {\n        AssertUtils.assertNotNull(pluginOperation, \"pluginOperation must not be null\");\n\n        // prepare plugin file\n        File localFile = pluginOperation.getLocalFile();\n        if (localFile == null && !StringUtils.isEmpty(pluginOperation.getUrl())) {\n            URL url = new URL(pluginOperation.getUrl());\n            String pluginDir = ArkConfigs.getStringValue(Constants.CONFIG_INSTALL_PLUGIN_DIR);\n            File pluginDirectory = StringUtils.isEmpty(pluginDir) ? FileUtils\n                .createTempDir(\"sofa-ark\") : FileUtils.mkdir(pluginDir);\n            localFile = new File(pluginDirectory, pluginOperation.getPluginName() + \"-\"\n                                                  + pluginOperation.getPluginVersion() + \"-\"\n                                                  + System.currentTimeMillis());\n            try (InputStream inputStream = url.openStream()) {\n                FileUtils.copyInputStreamToFile(inputStream, localFile);\n            }\n        }\n\n        ClientResponse response = new ClientResponse();\n        if (localFile == null) {\n            response.setCode(ResponseCode.FAILED).setMessage(\n                String.format(\"Install Plugin: %s-%s fail, local file is null.\",\n                    pluginOperation.getPluginName(), pluginOperation.getPluginVersion()));\n            return response;\n        }\n\n        PluginConfig pluginConfig = new PluginConfig();\n        if (!StringUtils.isEmpty(pluginOperation.getPluginName())) {\n            pluginConfig.setSpecifiedName(pluginOperation.getPluginName());\n        }\n        if (!StringUtils.isEmpty(pluginOperation.getPluginVersion())) {\n            pluginConfig.setSpecifiedVersion(pluginOperation.getPluginVersion());\n        }\n\n        // prepare extension urls if necessary\n        List<String> extensionLibs = pluginOperation.getExtensionLibs();\n        List<URL> urlsList = new ArrayList<>();\n        if (extensionLibs != null && !extensionLibs.isEmpty()) {\n            for (String extension : extensionLibs) {\n                URL url = new URL(extension);\n                urlsList.add(url);\n            }\n        }\n        URL[] extensionUrls = urlsList.toArray(new URL[0]);\n        pluginConfig.setExtensionUrls(extensionUrls);\n\n        long start = System.currentTimeMillis();\n        SimpleDateFormat sdf = new SimpleDateFormat(\"HH:mm:ss,SSS\");\n        String startDate = sdf.format(new Date(start));\n\n        // create\n        Plugin plugin = pluginFactoryService.createPlugin(localFile, pluginConfig);\n        // register\n        pluginManagerService.registerPlugin(plugin);\n        // start\n        try {\n            plugin.start();\n            long end = System.currentTimeMillis();\n            response.setCode(ResponseCode.SUCCESS).setMessage(\n                String.format(\"Install Plugin: %s success, cost: %s ms, started at: %s\",\n                    plugin.getPluginName() + \":\" + plugin.getVersion(), end - start, startDate));\n            getLogger().info(response.getMessage());\n        } catch (Throwable throwable) {\n            long end = System.currentTimeMillis();\n            response.setCode(ResponseCode.FAILED).setMessage(\n                String.format(\"Install Plugin: %s fail,cost: %s ms, started at: %s\",\n                    plugin.getPluginName() + \":\" + plugin.getVersion(), end - start, startDate));\n            getLogger().error(response.getMessage(), throwable);\n            throw throwable;\n        }\n        return response;\n    }\n\n    public static ClientResponse checkPlugin() {\n        return checkPlugin(null);\n    }\n\n    public static ClientResponse checkPlugin(String pluginName) {\n        AssertUtils.assertNotNull(pluginFactoryService, \"pluginFactoryService must not be null!\");\n        AssertUtils.assertNotNull(pluginManagerService, \"pluginManagerService must not be null!\");\n\n        ClientResponse response = new ClientResponse();\n        Set<Plugin> plugins = new HashSet<>();\n        if (pluginName != null) {\n            Plugin plugin = pluginManagerService.getPluginByName(pluginName);\n            if (plugin != null) {\n                plugins.add(plugin);\n            }\n        } else {\n            plugins.addAll(pluginManagerService.getPluginsInOrder());\n        }\n\n        StringBuilder sb = new StringBuilder();\n        sb.append(String.format(\"Plugin count=%d\", plugins.size())).append(\"\\n\");\n        for (Plugin plugin : plugins) {\n            sb.append(\n                String.format(\"pluginName=%s, pluginVersion=%s\", plugin.getPluginName(),\n                    plugin.getVersion())).append(\"\\n\");\n        }\n        response.setCode(ResponseCode.SUCCESS).setPluginInfos(plugins).setMessage(sb.toString());\n        getLogger().info(String.format(\"Check Plugin: %s\", response.getMessage()));\n        return response;\n    }\n\n    /**\n     * dynamic invoke by specified version\n     * @param version\n     * @param replay\n     * @return\n     */\n    public static Object invocationReplay(String version, Replay replay) {\n        try {\n            ReplayContext.set(version);\n            return replay.invoke();\n        } finally {\n            ReplayContext.unset();\n        }\n    }\n\n    private static ArkLogger getLogger() {\n        return ArkLoggerFactory.getDefaultLogger();\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/api/src/main/java/com/alipay/sofa/ark/api/ArkConfigs.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.api;\n\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.constant.Constants;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\n/**\n * @author qilong.zql\n * @author GengZhang\n * @since 0.6.0\n */\npublic class ArkConfigs {\n\n    /**\n     * Global Configuration\n     */\n    private final static ConcurrentMap<String, Object> CFG = new ConcurrentHashMap<String, Object>();\n\n    /**\n     * executed only once\n     */\n    public static void init(List<URL> confFiles) {\n        try {\n            // load file configs\n            for (URL url : confFiles) {\n                loadConfigFile(url.openStream());\n            }\n        } catch (Exception e) {\n            throw new ArkRuntimeException(\"Catch Exception when load ArkConfigs\", e);\n        }\n    }\n\n    /**\n     * load conf file\n     *\n     * @param inputStream conf file\n     * @throws IOException loading exception\n     */\n    private static void loadConfigFile(InputStream inputStream) throws IOException {\n        Properties properties = new Properties();\n        properties.load(inputStream);\n        for (Object key : properties.keySet()) {\n            CFG.put((String) key, properties.get(key));\n        }\n    }\n\n    /**\n     * configure system property\n     *\n     * @param key\n     * @param value\n     */\n    public static void setSystemProperty(String key, String value) {\n        System.setProperty(key, value);\n    }\n\n    /**\n     * clear system property\n     *\n     * @param key\n     */\n    public static String getSystemProperty(String key) {\n        return System.getProperty(key);\n    }\n\n    /**\n     * Get string value.\n     *\n     * @param primaryKey the primary key\n     * @return the string value\n     */\n    public static String getStringValue(String primaryKey) {\n        String val = getSystemProperty(primaryKey);\n        if (val == null) {\n            val = (String) CFG.get(primaryKey);\n        }\n        return val;\n    }\n\n    /**\n     * Get string value.\n     *\n     * @param primaryKey the primary key\n     * @param defaultValue\n     * @return the string value\n     */\n    public static String getStringValue(String primaryKey, String defaultValue) {\n        String val = getStringValue(primaryKey);\n        return val == null ? defaultValue : val;\n    }\n\n    /**\n     * Get int value.\n     *\n     * @param primaryKey the primary key\n     * @param defaultValue\n     * @return the int value\n     */\n    public static int getIntValue(String primaryKey, int defaultValue) {\n        String val = getStringValue(primaryKey);\n        return val == null ? defaultValue : Integer.valueOf(val);\n    }\n\n    public static boolean getBooleanValue(String primaryKey, boolean defaultValue) {\n        String val = getStringValue(primaryKey);\n        return val == null ? defaultValue : Boolean.valueOf(val);\n    }\n\n    /**\n     * Get ArkConfigs key set\n     *\n     * @return\n     */\n    public static Set<String> keySet() {\n        Set<String> keySet = new HashSet<>(CFG.keySet());\n        keySet.addAll(new HashMap(System.getProperties()).keySet());\n        return keySet;\n    }\n\n    /**\n     * put string config\n     * @param key\n     * @param value\n     */\n    public static void putStringValue(String key, String value) {\n        CFG.put(key, value);\n    }\n\n    public static boolean isEmbedEnable() {\n        return Boolean.getBoolean(Constants.EMBED_ENABLE);\n    }\n\n    public static void setEmbedEnable(boolean enable) {\n        System.setProperty(Constants.EMBED_ENABLE, enable ? \"true\" : \"false\");\n    }\n\n    public static boolean isEmbedStaticBizEnable() {\n        return Boolean.getBoolean(Constants.EMBED_STATIC_BIZ_ENABLE);\n    }\n\n    public static void setEmbedStaticBizEnable(boolean enable) {\n        System.setProperty(Constants.EMBED_STATIC_BIZ_ENABLE, enable ? \"true\" : \"false\");\n    }\n\n    public static boolean isBizSpecifyDependentPluginsEnable() {\n        return Boolean.getBoolean(Constants.BIZ_SPECIFY_DEPENDENT_PLUGINS_ENABLE);\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core/api/src/main/java/com/alipay/sofa/ark/api/ClientResponse.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.api;\n\nimport com.alipay.sofa.ark.spi.model.BizInfo;\nimport com.alipay.sofa.ark.spi.model.Plugin;\n\nimport java.util.Set;\n\n/**\n * API operation response\n *\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class ClientResponse {\n\n    private String       message;\n    private ResponseCode code;\n    private Set<BizInfo> bizInfos;\n    private Set<Plugin>  pluginInfos;\n\n    public String getMessage() {\n        return message;\n    }\n\n    public ClientResponse setMessage(String message) {\n        this.message = message;\n        return this;\n    }\n\n    public ResponseCode getCode() {\n        return code;\n    }\n\n    public ClientResponse setCode(ResponseCode code) {\n        this.code = code;\n        return this;\n    }\n\n    public Set<BizInfo> getBizInfos() {\n        return bizInfos;\n    }\n\n    public ClientResponse setBizInfos(Set<BizInfo> bizInfos) {\n        this.bizInfos = bizInfos;\n        return this;\n    }\n\n    public Set<Plugin> getPluginInfos() {\n        return pluginInfos;\n    }\n\n    public ClientResponse setPluginInfos(Set<Plugin> pluginInfos) {\n        this.pluginInfos = pluginInfos;\n        return this;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core/api/src/main/java/com/alipay/sofa/ark/api/ResponseCode.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.api;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic enum ResponseCode {\n    SUCCESS, FAILED, REPEAT_BIZ, NOT_FOUND_BIZ, ILLEGAL_STATE_BIZ;\n}"
  },
  {
    "path": "sofa-ark-parent/core/api/src/test/java/com/alipay/sofa/ark/api/ArkConfigsTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.api;\n\nimport org.junit.Test;\n\nimport java.net.URL;\n\nimport static com.alipay.sofa.ark.api.ArkConfigs.getStringValue;\nimport static com.alipay.sofa.ark.api.ArkConfigs.init;\nimport static java.util.Arrays.asList;\nimport static org.junit.Assert.assertEquals;\n\npublic class ArkConfigsTest {\n\n    @Test\n    public void testLoadConfigFile() throws Exception {\n        URL resource = this.getClass().getClassLoader().getResource(\"test.props\");\n        init(asList(resource));\n        assertEquals(\"b123\", getStringValue(\"a123\"));\n        assertEquals(\"d123\", getStringValue(\"c123\"));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/api/src/test/resources/test.props",
    "content": "a123=b123\nc123=d123"
  },
  {
    "path": "sofa-ark-parent/core/common/CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Module Overview\n\n**Artifact ID**: `sofa-ark-common`\n**Package**: `com.alipay.sofa.ark.common`\n\nThis module provides common utilities, logging, and shared functionality used across all SOFAArk modules.\n\n## Purpose\n\n- Utility classes for file operations, strings, assertions\n- Logging infrastructure\n- Common constants and helper methods\n\n## Key Packages\n\n### `common.util`\nUtility classes:\n- `StringUtils` - String manipulation utilities\n- `FileUtils` - File I/O operations\n- `AssertUtils` - Assertion helpers\n- `BizIdentityUtils` - Business module identity parsing (`name:version` format)\n- `ClassUtils` - Class loading utilities\n- `EnvironmentUtils` - Environment variable handling\n\n### `common.log`\nLogging infrastructure:\n- `ArkLogger` - Logger wrapper\n- `ArkLoggerFactory` - Logger factory for creating Ark loggers\n\n## Dependencies\n\n- `sofa-ark-exception` - Exception definitions\n- `log-sofa-boot-starter` - SOFA logging framework\n- `slf4j-api` - Logging facade\n\n## Used By\n\nAlmost all SOFAArk modules depend on this common utilities module:\n- `sofa-ark-api`\n- `sofa-ark-container`\n- `sofa-ark-archive`\n- `sofa-ark-maven-plugin`\n- `sofa-ark-plugin-maven-plugin`"
  },
  {
    "path": "sofa-ark-parent/core/common/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <artifactId>sofa-ark-core</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n    </parent>\n\n    <artifactId>sofa-ark-common</artifactId>\n    <name>${project.groupId}:${project.artifactId}</name>\n\n    <dependencies>\n\n        <!--SOFAArk modules-->\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-spi</artifactId>\n        </dependency>\n\n        <!--third party library-->\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>log-sofa-boot-starter</artifactId>\n            <exclusions>\n                <exclusion>\n                    <groupId>org.springframework.boot</groupId>\n                    <artifactId>spring-boot</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n        <dependency>\n            <groupId>com.google.inject</groupId>\n            <artifactId>guice</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>commons-io</groupId>\n            <artifactId>commons-io</artifactId>\n        </dependency>\n        <!--log-->\n        <dependency>\n            <groupId>ch.qos.logback</groupId>\n            <artifactId>logback-classic</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>ch.qos.logback</groupId>\n            <artifactId>logback-core</artifactId>\n        </dependency>\n\n        <!--test-->\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-inline</artifactId>\n            <version>${mockito.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>commons-beanutils</groupId>\n            <artifactId>commons-beanutils</artifactId>\n            <version>1.9.4</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/java/com/alipay/sofa/ark/common/adapter/ArkLogbackContextSelector.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.adapter;\n\nimport ch.qos.logback.classic.LoggerContext;\nimport ch.qos.logback.classic.selector.ContextSelector;\nimport ch.qos.logback.core.CoreConstants;\nimport com.alipay.sofa.ark.common.util.StringUtils;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\npublic class ArkLogbackContextSelector implements ContextSelector {\n\n    private static final Map<ClassLoader, LoggerContext> CLASS_LOADER_LOGGER_CONTEXT = new HashMap<>();\n\n    private static final String                          BIZ_CLASS_LOADER            = \"com.alipay.sofa.ark.container.service.classloader.BizClassLoader\";\n    private static final String                          CONTAINER_CLASS_LOADER      = \"com.alipay.sofa.ark.bootstrap.ContainerClassLoader\";\n\n    private LoggerContext                                defaultLoggerContext;\n\n    public ArkLogbackContextSelector(LoggerContext loggerContext) {\n        this.defaultLoggerContext = loggerContext;\n    }\n\n    @Override\n    public LoggerContext getLoggerContext() {\n        ClassLoader classLoader = this.findClassLoader();\n        if (classLoader == null) {\n            return defaultLoggerContext;\n        }\n        return getContext(classLoader);\n    }\n\n    private ClassLoader findClassLoader() {\n        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();\n        if (classLoader != null && CONTAINER_CLASS_LOADER.equals(classLoader.getClass().getName())) {\n            return null;\n        }\n        if (classLoader != null && BIZ_CLASS_LOADER.equals(classLoader.getClass().getName())) {\n            return classLoader;\n        }\n\n        Class<?>[] context = new SecurityManager() {\n            @Override\n            public Class<?>[] getClassContext() {\n                return super.getClassContext();\n            }\n        }.getClassContext();\n        if (context == null || context.length == 0) {\n            return null;\n        }\n        for (Class<?> cls : context) {\n            if (cls.getClassLoader() != null\n                && BIZ_CLASS_LOADER.equals(cls.getClassLoader().getClass().getName())) {\n                return cls.getClassLoader();\n            }\n        }\n\n        return null;\n    }\n\n    private LoggerContext getContext(ClassLoader cls) {\n        LoggerContext loggerContext = CLASS_LOADER_LOGGER_CONTEXT.get(cls);\n        if (null == loggerContext) {\n            synchronized (ArkLogbackContextSelector.class) {\n                loggerContext = CLASS_LOADER_LOGGER_CONTEXT.get(cls);\n                if (null == loggerContext) {\n                    loggerContext = new LoggerContext();\n                    loggerContext.setName(CoreConstants.DEFAULT_CONTEXT_NAME);\n                    CLASS_LOADER_LOGGER_CONTEXT.put(cls, loggerContext);\n                }\n            }\n        }\n        return loggerContext;\n    }\n\n    @Override\n    public LoggerContext getLoggerContext(String name) {\n        if (StringUtils.isEmpty(name)) {\n            return defaultLoggerContext;\n        }\n        for (ClassLoader classLoader : CLASS_LOADER_LOGGER_CONTEXT.keySet()) {\n            LoggerContext loggerContext = CLASS_LOADER_LOGGER_CONTEXT.get(classLoader);\n            if (name.equals(loggerContext.getName())) {\n                return loggerContext;\n            }\n        }\n        return defaultLoggerContext;\n    }\n\n    @Override\n    public LoggerContext getDefaultLoggerContext() {\n        return defaultLoggerContext;\n    }\n\n    @Override\n    public LoggerContext detachLoggerContext(String loggerContextName) {\n        if (StringUtils.isEmpty(loggerContextName)) {\n            return null;\n        }\n        for (ClassLoader classLoader : CLASS_LOADER_LOGGER_CONTEXT.keySet()) {\n            LoggerContext loggerContext = CLASS_LOADER_LOGGER_CONTEXT.get(classLoader);\n            if (loggerContextName.equals(loggerContext.getName())) {\n                return removeContext(classLoader);\n            }\n        }\n        return null;\n    }\n\n    public LoggerContext removeContext(ClassLoader cls) {\n        if (cls == null) {\n            return null;\n        }\n        return CLASS_LOADER_LOGGER_CONTEXT.remove(cls);\n    }\n\n    @Override\n    public List<String> getContextNames() {\n        return CLASS_LOADER_LOGGER_CONTEXT.values().stream().map(LoggerContext::getName).collect(Collectors.toList());\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/java/com/alipay/sofa/ark/common/guice/AbstractArkGuiceModule.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.guice;\n\nimport com.google.inject.AbstractModule;\n\n/**\n * Abstract Guice Module for SofaArk\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic abstract class AbstractArkGuiceModule extends AbstractModule {\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/java/com/alipay/sofa/ark/common/log/ArkLogger.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.log;\n\nimport org.slf4j.Logger;\nimport org.slf4j.Marker;\n\n/**\n * Logger Implementation for SOFAArk\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class ArkLogger implements Logger {\n\n    private Logger logger;\n\n    public ArkLogger(Logger logger) {\n        this.logger = logger;\n    }\n\n    @Override\n    public String getName() {\n        return logger.getName();\n    }\n\n    @Override\n    public boolean isTraceEnabled() {\n        return logger.isTraceEnabled();\n    }\n\n    @Override\n    public void trace(String msg) {\n        logger.trace(msg);\n    }\n\n    @Override\n    public void trace(String format, Object arg) {\n        logger.trace(format, arg);\n    }\n\n    @Override\n    public void trace(String format, Object arg1, Object arg2) {\n        logger.trace(format, arg1, arg2);\n    }\n\n    @Override\n    public void trace(String format, Object... arguments) {\n        logger.trace(format, arguments);\n    }\n\n    @Override\n    public void trace(String msg, Throwable t) {\n        logger.trace(msg, t);\n    }\n\n    @Override\n    public boolean isTraceEnabled(Marker marker) {\n        return logger.isTraceEnabled();\n    }\n\n    @Override\n    public void trace(Marker marker, String msg) {\n        logger.trace(marker, msg);\n    }\n\n    @Override\n    public void trace(Marker marker, String format, Object arg) {\n        logger.trace(marker, format, arg);\n    }\n\n    @Override\n    public void trace(Marker marker, String format, Object arg1, Object arg2) {\n        logger.trace(marker, format, arg1, arg2);\n    }\n\n    @Override\n    public void trace(Marker marker, String format, Object... argArray) {\n        logger.trace(marker, format, argArray);\n    }\n\n    @Override\n    public void trace(Marker marker, String msg, Throwable t) {\n        logger.trace(marker, msg, t);\n    }\n\n    @Override\n    public boolean isDebugEnabled() {\n        return logger.isDebugEnabled();\n    }\n\n    @Override\n    public void debug(String msg) {\n        logger.debug(msg);\n    }\n\n    @Override\n    public void debug(String format, Object arg) {\n        logger.debug(format, arg);\n    }\n\n    @Override\n    public void debug(String format, Object arg1, Object arg2) {\n        logger.debug(format, arg1, arg2);\n    }\n\n    @Override\n    public void debug(String format, Object... arguments) {\n        logger.debug(format, arguments);\n    }\n\n    @Override\n    public void debug(String msg, Throwable t) {\n        logger.debug(msg, t);\n    }\n\n    @Override\n    public boolean isDebugEnabled(Marker marker) {\n        return logger.isDebugEnabled(marker);\n    }\n\n    @Override\n    public void debug(Marker marker, String msg) {\n        logger.debug(marker, msg);\n    }\n\n    @Override\n    public void debug(Marker marker, String format, Object arg) {\n        logger.debug(marker, format, arg);\n    }\n\n    @Override\n    public void debug(Marker marker, String format, Object arg1, Object arg2) {\n        logger.debug(marker, format, arg1, arg2);\n    }\n\n    @Override\n    public void debug(Marker marker, String format, Object... arguments) {\n        logger.debug(marker, format, arguments);\n    }\n\n    @Override\n    public void debug(Marker marker, String msg, Throwable t) {\n        logger.debug(marker, msg, t);\n    }\n\n    @Override\n    public boolean isInfoEnabled() {\n        return logger.isInfoEnabled();\n    }\n\n    @Override\n    public void info(String msg) {\n        logger.info(msg);\n    }\n\n    @Override\n    public void info(String format, Object arg) {\n        logger.info(format, arg);\n    }\n\n    @Override\n    public void info(String format, Object arg1, Object arg2) {\n        logger.info(format, arg1, arg2);\n    }\n\n    @Override\n    public void info(String format, Object... arguments) {\n        logger.info(format, arguments);\n    }\n\n    @Override\n    public void info(String msg, Throwable t) {\n        logger.info(msg, t);\n    }\n\n    @Override\n    public boolean isInfoEnabled(Marker marker) {\n        return logger.isInfoEnabled();\n    }\n\n    @Override\n    public void info(Marker marker, String msg) {\n        logger.info(marker, msg);\n    }\n\n    @Override\n    public void info(Marker marker, String format, Object arg) {\n        logger.info(marker, format, arg);\n    }\n\n    @Override\n    public void info(Marker marker, String format, Object arg1, Object arg2) {\n        logger.info(marker, format, arg1, arg2);\n    }\n\n    @Override\n    public void info(Marker marker, String format, Object... arguments) {\n        logger.info(marker, format, arguments);\n    }\n\n    @Override\n    public void info(Marker marker, String msg, Throwable t) {\n        logger.info(marker, msg, t);\n    }\n\n    @Override\n    public boolean isWarnEnabled() {\n        return logger.isWarnEnabled();\n    }\n\n    @Override\n    public void warn(String msg) {\n        logger.warn(msg);\n    }\n\n    @Override\n    public void warn(String format, Object arg) {\n        logger.warn(format, arg);\n    }\n\n    @Override\n    public void warn(String format, Object... arguments) {\n        logger.warn(format, arguments);\n    }\n\n    @Override\n    public void warn(String format, Object arg1, Object arg2) {\n        logger.warn(format, arg1, arg2);\n    }\n\n    @Override\n    public void warn(String msg, Throwable t) {\n        logger.warn(msg, t);\n    }\n\n    @Override\n    public boolean isWarnEnabled(Marker marker) {\n        return logger.isWarnEnabled(marker);\n    }\n\n    @Override\n    public void warn(Marker marker, String msg) {\n        logger.warn(marker, msg);\n    }\n\n    @Override\n    public void warn(Marker marker, String format, Object arg) {\n        logger.warn(marker, format, arg);\n    }\n\n    @Override\n    public void warn(Marker marker, String format, Object arg1, Object arg2) {\n        logger.warn(marker, format, arg1, arg2);\n    }\n\n    @Override\n    public void warn(Marker marker, String format, Object... arguments) {\n        logger.warn(marker, format, arguments);\n    }\n\n    @Override\n    public void warn(Marker marker, String msg, Throwable t) {\n        logger.warn(marker, msg, t);\n    }\n\n    @Override\n    public boolean isErrorEnabled() {\n        return logger.isErrorEnabled();\n    }\n\n    @Override\n    public void error(String msg) {\n        logger.error(msg);\n    }\n\n    @Override\n    public void error(String format, Object arg) {\n        logger.error(format, arg);\n    }\n\n    @Override\n    public void error(String format, Object arg1, Object arg2) {\n        logger.error(format, arg1, arg2);\n    }\n\n    @Override\n    public void error(String format, Object... arguments) {\n        logger.error(format, arguments);\n    }\n\n    @Override\n    public void error(String msg, Throwable t) {\n        logger.error(msg, t);\n    }\n\n    @Override\n    public boolean isErrorEnabled(Marker marker) {\n        return logger.isErrorEnabled(marker);\n    }\n\n    @Override\n    public void error(Marker marker, String msg) {\n        logger.error(marker, msg);\n    }\n\n    @Override\n    public void error(Marker marker, String format, Object arg) {\n        logger.error(marker, format, arg);\n    }\n\n    @Override\n    public void error(Marker marker, String format, Object arg1, Object arg2) {\n        logger.error(marker, format, arg1, arg2);\n    }\n\n    @Override\n    public void error(Marker marker, String format, Object... arguments) {\n        logger.error(marker, format, arguments);\n    }\n\n    @Override\n    public void error(Marker marker, String msg, Throwable t) {\n        logger.error(marker, msg, t);\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/java/com/alipay/sofa/ark/common/log/ArkLoggerFactory.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.log;\n\nimport com.alipay.sofa.common.log.LoggerSpaceManager;\n\n/**\n * LoggerFactory for SOFAArk\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class ArkLoggerFactory {\n\n    public static final String  SOFA_ARK_LOGGER_SPACE        = \"com.alipay.sofa.ark\";\n\n    private static final String SOFA_ARK_DEFAULT_LOGGER_NAME = \"com.alipay.sofa.ark\";\n\n    public static ArkLogger     defaultLogger                = getLogger(SOFA_ARK_DEFAULT_LOGGER_NAME);\n\n    public static ArkLogger getLogger(Class<?> clazz) {\n        if (clazz == null) {\n            return null;\n        }\n        return getLogger(clazz.getCanonicalName());\n    }\n\n    public static ArkLogger getLogger(String name) {\n        if (name == null || name.isEmpty()) {\n            return null;\n        }\n        return new ArkLogger(LoggerSpaceManager.getLoggerBySpace(name, SOFA_ARK_LOGGER_SPACE));\n    }\n\n    public static ArkLogger getDefaultLogger() {\n        return defaultLogger;\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/java/com/alipay/sofa/ark/common/thread/CommonThreadPool.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.thread;\n\nimport com.alipay.sofa.ark.common.util.ThreadPoolUtils;\n\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Execute tasks triggered by ark container and ark plugin.\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic class CommonThreadPool {\n\n    /**\n     * Core size of thread pool\n     *\n     * @see ThreadPoolExecutor#corePoolSize\n     */\n    private int                           corePoolSize           = 10;\n\n    /**\n     * Maximum size of thread pool\n     *\n     * @see ThreadPoolExecutor#corePoolSize\n     */\n    private int                           maximumPoolSize        = 100;\n\n    /**\n     * @see ThreadPoolExecutor#keepAliveTime\n     */\n    private int                           keepAliveTime          = 300000;\n\n    /**\n     * @see ThreadPoolExecutor#getQueue\n     */\n    private int                           queueSize              = 0;\n\n    /**\n     * @see ThreadPoolExecutor#threadFactory#threadPoolName\n     */\n    private String                        threadPoolName         = \"CommonProcessor\";\n\n    /**\n     * @see ThreadPoolExecutor#threadFactory#isDaemon\n     */\n    private boolean                       isDaemon               = false;\n\n    /**\n     * @see ThreadPoolExecutor#allowCoreThreadTimeOut\n     */\n    private boolean                       allowCoreThreadTimeOut = false;\n\n    /**\n     * @see ThreadPoolExecutor#prestartAllCoreThreads\n     */\n    private boolean                       prestartAllCoreThreads = false;\n\n    /**\n     * ThreadPoolExecutor\n     */\n    transient volatile ThreadPoolExecutor executor;\n\n    private void init() {\n        executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime,\n            TimeUnit.MILLISECONDS, ThreadPoolUtils.buildQueue(queueSize), new NamedThreadFactory(\n                threadPoolName, isDaemon));\n        if (allowCoreThreadTimeOut) {\n            executor.allowCoreThreadTimeOut(true);\n        }\n        if (prestartAllCoreThreads) {\n            executor.prestartAllCoreThreads();\n        }\n    }\n\n    public int getCorePoolSize() {\n        return corePoolSize;\n    }\n\n    public CommonThreadPool setCorePoolSize(int corePoolSize) {\n        this.corePoolSize = corePoolSize;\n        return this;\n    }\n\n    public int getMaximumPoolSize() {\n        return maximumPoolSize;\n    }\n\n    public CommonThreadPool setMaximumPoolSize(int maximumPoolSize) {\n        this.maximumPoolSize = maximumPoolSize;\n        return this;\n    }\n\n    public int getKeepAliveTime() {\n        return keepAliveTime;\n    }\n\n    public CommonThreadPool setKeepAliveTime(int keepAliveTime) {\n        this.keepAliveTime = keepAliveTime;\n        return this;\n    }\n\n    public int getQueueSize() {\n        return queueSize;\n    }\n\n    public CommonThreadPool setQueueSize(int queueSize) {\n        this.queueSize = queueSize;\n        return this;\n    }\n\n    public String getThreadPoolName() {\n        return threadPoolName;\n    }\n\n    public CommonThreadPool setThreadPoolName(String threadPoolName) {\n        this.threadPoolName = threadPoolName;\n        return this;\n    }\n\n    public boolean isDaemon() {\n        return isDaemon;\n    }\n\n    public CommonThreadPool setDaemon(boolean daemon) {\n        isDaemon = daemon;\n        return this;\n    }\n\n    public boolean isAllowCoreThreadTimeOut() {\n        return allowCoreThreadTimeOut;\n    }\n\n    public CommonThreadPool setAllowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) {\n        this.allowCoreThreadTimeOut = allowCoreThreadTimeOut;\n        return this;\n    }\n\n    public boolean isPrestartAllCoreThreads() {\n        return prestartAllCoreThreads;\n    }\n\n    public CommonThreadPool setPrestartAllCoreThreads(boolean prestartAllCoreThreads) {\n        this.prestartAllCoreThreads = prestartAllCoreThreads;\n        return this;\n    }\n\n    /**\n     * Gets executor\n     *\n     * @return the executor\n     */\n    public ThreadPoolExecutor getExecutor() {\n        if (executor == null) {\n            synchronized (this) {\n                if (executor == null) {\n                    init();\n                }\n            }\n        }\n        return executor;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/java/com/alipay/sofa/ark/common/thread/NamedThreadFactory.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.thread;\n\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * Common NamedThreadFactory\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic class NamedThreadFactory implements ThreadFactory {\n\n    /**\n     * The global thread pool counter\n     */\n    private static final AtomicInteger POOL_COUNT   = new AtomicInteger();\n\n    /**\n     * The current thread pool counter\n     */\n    private final AtomicInteger        threadCount  = new AtomicInteger(1);\n\n    /**\n     * Thread group\n     */\n    private final ThreadGroup          group;\n\n    /**\n     * Thread name prefix\n     */\n    private final String               namePrefix;\n\n    /**\n     * Thread daemon option\n     */\n    private final boolean              isDaemon;\n\n    /**\n     * The first default prefix of thread name\n     */\n    private final static String        FIRST_PREFIX = \"SOFA-ARK-\";\n\n    /**\n     * specify the second prefix of thread name, default the thread created is non-daemon\n     *\n     * @param secondPrefix second prefix of thread name\n     */\n    public NamedThreadFactory(String secondPrefix) {\n        this(secondPrefix, false);\n    }\n\n    /**\n     * Construct a named thread factory\n     *\n     * @param secondPrefix second prefix of thread name\n     * @param daemon thread daemon option\n     */\n    public NamedThreadFactory(String secondPrefix, boolean daemon) {\n        SecurityManager sm = System.getSecurityManager();\n        group = (sm != null) ? sm.getThreadGroup() : Thread.currentThread().getThreadGroup();\n        namePrefix = FIRST_PREFIX + secondPrefix + \"-\" + POOL_COUNT.getAndIncrement() + \"-T\";\n        isDaemon = daemon;\n    }\n\n    @Override\n    public Thread newThread(Runnable r) {\n        Thread t = new Thread(group, r, namePrefix + threadCount.getAndIncrement(), 0);\n        t.setDaemon(isDaemon);\n        if (t.getPriority() != Thread.NORM_PRIORITY) {\n            t.setPriority(Thread.NORM_PRIORITY);\n        }\n        return t;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/java/com/alipay/sofa/ark/common/thread/ThreadPoolManager.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.thread;\n\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * Thread Pool Manager\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic class ThreadPoolManager {\n\n    /**\n     * Manager group of {@see CommonThreadPool}\n     */\n    private static ConcurrentHashMap<String, CommonThreadPool> threadPoolMap = null;\n\n    /**\n     * Register a thread pool for a unique name.\n     *\n     * @param threadPoolName thread pool name\n     * @param commonThreadPool CommonThreadPool\n     */\n    public static synchronized void registerThreadPool(String threadPoolName,\n                                                       CommonThreadPool commonThreadPool) {\n        if (threadPoolMap == null) {\n            threadPoolMap = new ConcurrentHashMap<>(16);\n        }\n        threadPoolMap.putIfAbsent(threadPoolName, commonThreadPool);\n    }\n\n    /**\n     * un-register a thread pool for a given name\n     *\n     * @param threadPoolName thread pool name\n     */\n    public static synchronized void unRegisterUserThread(String threadPoolName) {\n        if (threadPoolMap != null) {\n            threadPoolMap.remove(threadPoolName);\n        }\n    }\n\n    /**\n     * Retrieve a thread pool for a given name\n     *\n     * @param threadPoolName thread pool name\n     * @return\n     */\n    public static CommonThreadPool getThreadPool(String threadPoolName) {\n        return threadPoolMap == null ? null : threadPoolMap.get(threadPoolName);\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/java/com/alipay/sofa/ark/common/util/AssertUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.util;\n\n/**\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class AssertUtils {\n\n    /**\n     * Validate current object must not be null\n     *\n     * @param instance object instance\n     * @param msg error message\n     * @throws IllegalArgumentException if object instance is null\n     */\n    public static void assertNotNull(Object instance, String msg) {\n        if (instance == null) {\n            throw new IllegalArgumentException(msg);\n        }\n    }\n\n    /**\n     * Validate current object must be null\n     *\n     * @param instance object instance\n     * @param msg error message\n     * @throws IllegalArgumentException if object instance is null\n     */\n    public static void assertNull(Object instance, String msg) {\n        if (instance != null) {\n            throw new IllegalArgumentException(msg);\n        }\n    }\n\n    /**\n     * <p>Validate that the argument condition is {@code true}; otherwise\n     * throwing an exception with the specified message. This method is useful when\n     * validating according to an arbitrary boolean expression, such as validating a\n     * primitive number or using your own custom validation expression.</p>\n     *\n     * @param expression  the boolean expression to check\n     * @param message  the {@link String#format(String, Object...)} exception message if invalid, not null\n     * @param values  the optional values for the formatted exception message, null array not recommended\n     * @throws IllegalArgumentException if expression is {@code false}\n     */\n    public static void isTrue(final boolean expression, final String message,\n                              final Object... values) {\n        if (!expression) {\n            throw new IllegalArgumentException(String.format(message, values));\n        }\n    }\n\n    /**\n     * <p>Validate that the argument condition is {@code false}; otherwise\n     * throwing an exception with the specified message. This method is useful when\n     * validating according to an arbitrary boolean expression, such as validating a\n     * primitive number or using your own custom validation expression.</p>\n     *\n     * @param expression  the boolean expression to check\n     * @param message  the {@link String#format(String, Object...)} exception message if invalid, not null\n     * @param values  the optional values for the formatted exception message, null array not recommended\n     * @throws IllegalArgumentException if expression is {@code false}\n     */\n    public static void isFalse(final boolean expression, final String message,\n                               final Object... values) {\n        if (expression) {\n            throw new IllegalArgumentException(String.format(message, values));\n        }\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/java/com/alipay/sofa/ark/common/util/BizIdentityUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.util;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.model.Biz;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class BizIdentityUtils {\n    public static String generateBizIdentity(Biz biz) {\n        return generateBizIdentity(biz.getBizName(), biz.getBizVersion());\n    }\n\n    public static String generateBizIdentity(String bizName, String bizVersion) {\n        return bizName + Constants.STRING_COLON + bizVersion;\n    }\n\n    public static boolean isValid(String bizIdentity) {\n        if (StringUtils.isEmpty(bizIdentity)) {\n            return false;\n        }\n        String[] str = bizIdentity.split(Constants.STRING_COLON);\n        if (str.length != 2) {\n            return false;\n        }\n        return !StringUtils.isEmpty(str[0]) && !StringUtils.isEmpty(str[1]);\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/java/com/alipay/sofa/ark/common/util/ClassLoaderUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.util;\n\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.management.ManagementFactory;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.security.AccessController;\nimport java.security.PrivilegedAction;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * ClassLoader Util\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class ClassLoaderUtils {\n\n    private static final String   JAVA_AGENT_MARK        = \"-javaagent:\";\n\n    private static final String   JAVA_AGENT_OPTION_MARK = \"=\";\n\n    private static final String   SKYWALKING_AGENT_JAR   = \"skywalking-agent.jar\";\n\n    private static final String[] SKYWALKING_MOUNT_DIR   = { \"plugins\", \"activations\" };\n\n    /**\n     * push ContextClassLoader\n     *\n     * @param newClassLoader new classLoader\n     * @return old classloader\n     */\n    public static ClassLoader pushContextClassLoader(ClassLoader newClassLoader) {\n        ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();\n        Thread.currentThread().setContextClassLoader(newClassLoader);\n        return oldClassLoader;\n    }\n\n    /**\n     * set ContextClassLoader back\n     *\n     * @param oldClassLoader old classLoader\n     */\n    public static void popContextClassLoader(ClassLoader oldClassLoader) {\n        Thread.currentThread().setContextClassLoader(oldClassLoader);\n    }\n\n    public static URL[] getAgentClassPath() {\n        List<String> inputArguments = AccessController\n            .doPrivileged(new PrivilegedAction<List<String>>() {\n                @Override\n                public List<String> run() {\n                    return ManagementFactory.getRuntimeMXBean().getInputArguments();\n                }\n            });\n\n        List<URL> agentPaths = new ArrayList<>();\n        for (String argument : inputArguments) {\n            if (!argument.startsWith(JAVA_AGENT_MARK)) {\n                continue;\n            }\n            argument = argument.substring(JAVA_AGENT_MARK.length());\n            try {\n                String path = argument.split(JAVA_AGENT_OPTION_MARK)[0];\n                URL url = FileUtils.file(path).getCanonicalFile().toURI().toURL();\n                agentPaths.add(url);\n                processSkyWalking(path, agentPaths);\n            } catch (Throwable e) {\n                throw new ArkRuntimeException(\"Failed to create java agent classloader\", e);\n            }\n        }\n        return agentPaths.toArray(new URL[] {});\n\n    }\n\n    /**\n     * process skywalking agent plugins/activations\n     * @param path\n     * @param agentPaths\n     * @throws MalformedURLException\n     */\n    public static void processSkyWalking(final String path, final List<URL> agentPaths) throws MalformedURLException, IOException {\n        if (path.contains(SKYWALKING_AGENT_JAR)) {\n            for (String mountFolder : SKYWALKING_MOUNT_DIR) {\n                File folder = new File(FileUtils.file(path).getCanonicalFile().getParentFile(), mountFolder);\n                if (folder.exists() && folder.isDirectory()) {\n                    String[] jarFileNames = folder.list((dir, name) -> name.endsWith(\".jar\"));\n                    for (String fileName: jarFileNames) {\n                        File jarFile = new File(folder, fileName);\n                        agentPaths.add(jarFile.toURI().toURL());\n                    }\n                }\n            }\n        }\n    }\n\n    @SuppressWarnings({ \"restriction\", \"unchecked\" })\n    public static URL[] getURLs(ClassLoader classLoader) {\n        // https://stackoverflow.com/questions/46519092/how-to-get-all-jars-loaded-by-a-java-application-in-java9\n        if (classLoader instanceof URLClassLoader) {\n            return ((URLClassLoader) classLoader).getURLs();\n        }\n\n        // support jdk9+\n        String classpath = System.getProperty(\"java.class.path\");\n        String[] classpathEntries = classpath.split(System.getProperty(\"path.separator\"));\n        List<URL> classpathURLs = new ArrayList<>();\n        for (String classpathEntry : classpathEntries) {\n            URL url = null;\n            try {\n                url = FileUtils.file(classpathEntry).toURI().toURL();\n            } catch (MalformedURLException e) {\n                e.printStackTrace();\n                throw new ArkRuntimeException(\"Failed to get urls from \" + classLoader, e);\n            }\n            classpathURLs.add(url);\n        }\n\n        return classpathURLs.toArray(new URL[0]);\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/java/com/alipay/sofa/ark/common/util/ClassUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.util;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport org.apache.commons.io.FileUtils;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.security.CodeSource;\nimport java.security.ProtectionDomain;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * @author qilong.zql\n * @since 0.3.0\n */\npublic class ClassUtils {\n\n    /**\n     * Get package name from a specified class name.\n     *\n     * @param className\n     * @return\n     */\n    public static String getPackageName(String className) {\n        AssertUtils.isFalse(StringUtils.isEmpty(className), \"ClassName should not be empty!\");\n        int index = className.lastIndexOf('.');\n        if (index > 0) {\n            return className.substring(0, index);\n        }\n        return Constants.DEFAULT_PACKAGE;\n    }\n\n    /**\n     * find all compiled classes in dir, ignore inner, anonymous and local classes\n     * @param dir directory that stores class files\n     * @return compiled class names\n     */\n    public static List<String> collectClasses(File dir) throws IOException {\n        List<String> classNames = new ArrayList<>();\n        Collection<File> classFiles = FileUtils.listFiles(dir, new String[] { \"class\" }, true);\n        String basePath = dir.getCanonicalPath();\n\n        for (File classFile : classFiles) {\n            // Get the relative file path starting after the classes directory\n            String relativePath = classFile.getCanonicalPath().substring(basePath.length() + 1);\n\n            // Convert file path to class name (replace file separators with dots and remove .class extension)\n            String className = relativePath.replace(File.separatorChar, '.').replaceAll(\n                \"\\\\.class$\", \"\");\n\n            // skip inner, anonymous and local classes\n            if (!className.contains(\"$\")) {\n                classNames.add(className);\n            }\n        }\n        return classNames;\n    }\n\n    public static String getCodeBase(Class<?> cls) {\n\n        if (cls == null) {\n            return null;\n        }\n        ProtectionDomain domain = cls.getProtectionDomain();\n        if (domain == null) {\n            return null;\n        }\n        CodeSource source = domain.getCodeSource();\n        if (source == null) {\n            return null;\n        }\n        URL location = source.getLocation();\n        if (location == null) {\n            return null;\n        }\n        return location.getFile();\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/java/com/alipay/sofa/ark/common/util/EnvironmentUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.util;\n\nimport java.util.Properties;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.TELNET_SERVER_SECURITY_ENABLE;\n\n/**\n * a utils class to get environment properties\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic class EnvironmentUtils {\n\n    private static Properties properties = new Properties();\n\n    public static String getProperty(String key) {\n        String value = properties.getProperty(key);\n        if (value == null) {\n            return System.getProperty(key);\n        }\n        return value;\n    }\n\n    public static String getProperty(String key, String defaultValue) {\n        String value = properties.getProperty(key);\n        if (value == null) {\n            return System.getProperty(key, defaultValue);\n        }\n        return value;\n    }\n\n    public static void setProperty(String key, String value) {\n        properties.setProperty(key, value);\n    }\n\n    public static void setSystemProperty(String key, String value) {\n        System.setProperty(key, value);\n    }\n\n    public static void clearProperty(String key) {\n        properties.remove(key);\n    }\n\n    public static void clearSystemProperty(String key) {\n        System.clearProperty(key);\n    }\n\n    public static boolean isOpenSecurity() {\n        return getProperty(TELNET_SERVER_SECURITY_ENABLE, \"false\").equalsIgnoreCase(\"true\");\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/java/com/alipay/sofa/ark/common/util/FileUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.util;\n\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\n\nimport java.io.*;\nimport java.net.URLDecoder;\nimport java.nio.charset.StandardCharsets;\nimport java.security.DigestInputStream;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Enumeration;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\n\n/**\n * Utilities for manipulating files and directories in ark tooling.\n *\n * @author Dave Syer\n * @author Phillip Webb\n * @author qilong.zql\n * @author GengZhang\n */\npublic class FileUtils {\n\n    /**\n     * Generate a SHA.1 Hash for a given file.\n     * @param file the file to hash\n     * @return the hash value as a String\n     * @throws IOException if the file cannot be read\n     */\n    public static String sha1Hash(File file) throws IOException {\n        try {\n            DigestInputStream inputStream = new DigestInputStream(new FileInputStream(file),\n                MessageDigest.getInstance(\"SHA-1\"));\n            try {\n                byte[] buffer = new byte[4098];\n                while (inputStream.read(buffer) != -1) { //NOPMD\n                    // Read the entire stream\n                }\n                return bytesToHex(inputStream.getMessageDigest().digest());\n            } finally {\n                inputStream.close();\n            }\n        } catch (NoSuchAlgorithmException ex) {\n            throw new IllegalStateException(ex);\n        }\n    }\n\n    private static String bytesToHex(byte[] bytes) {\n        StringBuilder hex = new StringBuilder();\n        for (byte b : bytes) {\n            hex.append(String.format(\"%02x\", b));\n        }\n        return hex.toString();\n    }\n\n    /**\n     * Atomically creates a new directory somewhere beneath the system's\n     * temporary directory (as defined by the {@code java.io.tmpdir} system\n     */\n    public static synchronized File createTempDir(String subPath) {\n        File baseDir = FileUtils.file(System.getProperty(\"java.io.tmpdir\"));\n        File tempDir = new File(baseDir, subPath);\n        if (tempDir.exists()) {\n            return tempDir;\n        } else if (tempDir.mkdir()) {\n            return tempDir;\n        }\n        throw new ArkRuntimeException(\"Failed to create temp file\");\n    }\n\n    /**\n     * {@link org.apache.commons.io.FileUtils#copyInputStreamToFile(InputStream, File)}\n     * @param source\n     * @param destination\n     * @throws IOException\n     */\n    public static void copyInputStreamToFile(final InputStream source, final File destination)\n                                                                                              throws IOException {\n        org.apache.commons.io.FileUtils.copyInputStreamToFile(source, destination);\n    }\n\n    /**\n     *\n     * @param path\n     * @return\n     */\n    public static String getCompatiblePath(String path) {\n        if (System.getProperty(\"os.name\").toLowerCase().indexOf(\"window\") > -1) {\n            return path.replace(\"\\\\\", \"/\");\n        }\n        return path;\n    }\n\n    public static File unzip(File root, String targetPath) throws IOException {\n        ZipFile zipFile = null;\n        try {\n            zipFile = new ZipFile(root);\n            Enumeration<? extends ZipEntry> entries = zipFile.entries();\n            while (entries.hasMoreElements()) {\n                ZipEntry entry = entries.nextElement();\n                if (entry.isDirectory()) {\n                    String dirPath = targetPath + File.separator + entry.getName();\n                    File dir = FileUtils.file(dirPath);\n                    dir.mkdirs();\n                } else {\n                    InputStream inputStream = null;\n                    FileOutputStream fileOutputStream = null;\n                    try {\n                        inputStream = zipFile.getInputStream(entry);\n                        File file = FileUtils.file(targetPath + File.separator + entry.getName());\n                        if (!file.exists()) {\n                            File fileParent = file.getParentFile();\n                            if (!fileParent.exists()) {\n                                fileParent.mkdirs();\n                            }\n                        }\n                        file.createNewFile();\n                        fileOutputStream = new FileOutputStream(file);\n                        int count;\n                        byte[] buf = new byte[8192];\n                        while ((count = inputStream.read(buf)) != -1) {\n                            fileOutputStream.write(buf, 0, count);\n                        }\n\n                    } finally {\n                        if (fileOutputStream != null) {\n                            fileOutputStream.flush();\n                            fileOutputStream.close();\n                        }\n                        if (inputStream != null) {\n                            inputStream.close();\n                        }\n                    }\n                }\n            }\n            return FileUtils.file(targetPath);\n        } finally {\n            if (zipFile != null) {\n                zipFile.close();\n            }\n        }\n    }\n\n    /**\n     * creates a new directory for given path\n     *\n     * @param dirPath dest path\n     * @return Dir\n     */\n    public static File mkdir(String dirPath) {\n        if (StringUtils.isEmpty(dirPath)) {\n            return null;\n        }\n        File dir = FileUtils.file(dirPath);\n        if (!dir.exists()) {\n            // Recursive creation\n            dir.mkdirs();\n        }\n        return dir;\n    }\n\n    /**\n     * decode the given path if the path has spaces\n     *\n     * @param path dest path\n     * @return decoded path\n     */\n    public static String decodePath(String path) {\n        try {\n            return URLDecoder.decode(path, StandardCharsets.UTF_8.name());\n        } catch (UnsupportedEncodingException e) {\n            // return source string when occur an exception\n            return path;\n        }\n    }\n\n    /**\n     * creates a new file for given path.\n     * first, we check if the path is encoded before creating the File object\n     *\n     * @param path file path\n     * @return new File\n     */\n    public static File file(String path) {\n        return new File(decodePath(path));\n    }\n\n    /**\n     * creates a new file for given path.\n     *\n     * @param parent parent path\n     * @param path child path\n     * @return new File\n     */\n    public static File file(String parent, String path) {\n        return new File(decodePath(parent), path);\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/java/com/alipay/sofa/ark/common/util/OrderComparator.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.util;\n\nimport com.alipay.sofa.ark.spi.service.PriorityOrdered;\n\nimport java.util.Comparator;\n\n/**\n * {@link Comparator} implementation for {@link PriorityOrdered} objects, sorting\n * by order value ascending.\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic class OrderComparator implements Comparator<PriorityOrdered> {\n    @Override\n    public int compare(PriorityOrdered o1, PriorityOrdered o2) {\n        return Integer.compare(o1.getPriority(), o2.getPriority());\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/java/com/alipay/sofa/ark/common/util/ParseUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.util;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\n\nimport java.util.LinkedHashSet;\nimport java.util.Set;\n\n/**\n *\n * @author ruoshan\n * @since 1.0.0\n */\npublic class ParseUtils {\n\n    /**\n     * Parse package node(exactly match) and stem(with *)\n     * @param candidates candidate packages\n     * @param stems with * packages\n     * @param nodes exactly match packages\n     */\n    public static void parsePackageNodeAndStem(Set<String> candidates, Set<String> stems,\n                                               Set<String> nodes) {\n        for (String pkgPattern : candidates) {\n            if (pkgPattern.endsWith(Constants.PACKAGE_PREFIX_MARK)) {\n                stems.add(ClassUtils.getPackageName(pkgPattern));\n            } else {\n                nodes.add(pkgPattern);\n            }\n        }\n    }\n\n    /**\n     * Parse resource exactly match and stem(with *)\n     * @param candidates candidate resources\n     * @param prefixStems with xxx/* resources match by prefix\n     * @param suffixStems with *.xxx  *\\/xxx resources match by suffix\n     * @param resources exactly match resources\n     */\n    public static void parseResourceAndStem(Set<String> candidates, Set<String> prefixStems,\n                                            Set<String> suffixStems, Set<String> resources) {\n        for (String candidate : candidates) {\n            // do not support export *\n            if (candidate.equals(Constants.RESOURCE_STEM_MARK)) {\n                continue;\n            }\n            if (candidate.endsWith(Constants.RESOURCE_STEM_MARK)) {\n                prefixStems.add(candidate.substring(0, candidate.length()\n                                                       - Constants.RESOURCE_STEM_MARK.length()));\n            } else if (candidate.startsWith(Constants.RESOURCE_STEM_MARK)) {\n                suffixStems.add(candidate.substring(Constants.RESOURCE_STEM_MARK.length()));\n            } else {\n                resources.add(candidate);\n            }\n        }\n    }\n\n    public static void parseExcludeConf(LinkedHashSet<String> targetSet, String origin,\n                                        String confKey) {\n        if (origin.startsWith(confKey + Constants.EQUAL_SPLIT)) {\n            String confVal = origin.split(Constants.EQUAL_SPLIT)[1];\n            if (!StringUtils.isEmpty(confVal) && !targetSet.contains(confVal)) {\n                targetSet.add(confVal);\n            }\n        }\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/java/com/alipay/sofa/ark/common/util/PortSelectUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.util;\n\nimport java.io.IOException;\nimport java.net.ServerSocket;\n\n/**\n * @author qilong.zql\n * @author khotyn\n * @since 0.5.0\n */\npublic class PortSelectUtils {\n\n    /**\n     * The minimum candidate port number of IPv4\n     */\n    public static final int MIN_PORT_NUMBER = 1100;\n\n    /**\n     * The maximum candidate port number of IPv4\n     */\n    public static final int MAX_PORT_NUMBER = 65535;\n\n    /**\n     * Select appropriate port among specify interval\n     *\n     * @param defaultPort specify the starting port\n     * @param maxLength specify the size of interval\n     * @return return available port\n     */\n    public synchronized static int selectAvailablePort(int defaultPort, int maxLength) {\n        for (int i = defaultPort; i < defaultPort + maxLength; i++) {\n            try {\n                if (available(i)) {\n                    return i;\n                }\n            } catch (IllegalArgumentException e) {\n                // Ignore and continue\n            }\n        }\n\n        return -1;\n    }\n\n    private static boolean available(int port) {\n        if (port < MIN_PORT_NUMBER || port > MAX_PORT_NUMBER) {\n            throw new IllegalArgumentException(\"Invalid port: \" + port);\n        }\n        try (ServerSocket ss = new ServerSocket(port)) {\n            ss.setReuseAddress(true);\n            return true;\n        } catch (IOException e) {\n            // Do nothing\n        }\n        return false;\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/java/com/alipay/sofa/ark/common/util/ReflectionUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.util;\n\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\nimport java.net.URL;\nimport java.security.CodeSource;\nimport java.security.ProtectionDomain;\n\n/**\n * @author qilong.zql\n * @author GengZhang\n * @since 0.4.0\n */\npublic class ReflectionUtils {\n\n    /**\n     * Callback interface invoked on each field in the hierarchy.\n     */\n    public interface FieldCallback {\n\n        /**\n         * Perform an operation using the given field.\n         * @param field the field to operate on\n         * @throws ArkRuntimeException throw exception when handle with field\n         */\n        void doWith(Field field) throws ArkRuntimeException;\n    }\n\n    public static void doWithFields(Class<?> clazz, FieldCallback fc) {\n        AssertUtils.assertNotNull(clazz, \"Class should not be null\");\n        Class<?> searchType = clazz;\n        do {\n            Field[] fields = searchType.getDeclaredFields();\n            for (Field field : fields) {\n                fc.doWith(field);\n            }\n            searchType = searchType.getSuperclass();\n        } while (searchType != null && searchType != Object.class);\n    }\n\n    public static void makeAccessible(Field field) {\n        if ((!Modifier.isPublic(field.getModifiers())\n             || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier\n            .isFinal(field.getModifiers())) && !field.isAccessible()) {\n            field.setAccessible(true);\n        }\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/java/com/alipay/sofa/ark/common/util/SimpleByteBuffer.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.util;\n\n/**\n * A helper class, which buffers one line of input. It provides for simple line editing, e.g.\n * insertion, deletion, backspace, left and right movement\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic class SimpleByteBuffer {\n\n    private final static int BUFFER_CHUNK = 20;\n\n    private byte[]           buffer;\n\n    private int              pos          = 0;\n\n    private int              size         = 0;\n\n    public SimpleByteBuffer() {\n        buffer = new byte[BUFFER_CHUNK];\n    }\n\n    private void resize() {\n        final byte[] next = new byte[buffer.length << 1];\n        System.arraycopy(buffer, 0, next, 0, buffer.length);\n        buffer = next;\n    }\n\n    public void add(byte b) {\n        if (size >= buffer.length) {\n            resize();\n        }\n        buffer[size++] = b;\n    }\n\n    public void insert(byte b) {\n        if (size >= buffer.length) {\n            resize();\n        }\n        final int gap = size - pos;\n        if (gap > 0) {\n            System.arraycopy(buffer, pos, buffer, pos + 1, gap);\n        }\n        buffer[pos++] = b;\n        size++;\n    }\n\n    public byte goRight() {\n        if (pos < size) {\n            return buffer[pos++];\n        }\n        return -1;\n    }\n\n    public boolean goLeft() {\n        if (pos > 0) {\n            pos--;\n            return true;\n        }\n        return false;\n    }\n\n    public void backSpace() {\n        if (pos > 0) {\n            System.arraycopy(buffer, pos, buffer, pos - 1, size - pos);\n            pos--;\n            size--;\n        }\n    }\n\n    public void delete() {\n        if (pos < size) {\n            System.arraycopy(buffer, pos + 1, buffer, pos, size - pos);\n            size--;\n        }\n    }\n\n    public byte[] getBuffer() {\n        byte[] data = new byte[size];\n        System.arraycopy(buffer, 0, data, 0, size);\n        return data;\n    }\n\n    public byte[] getAndClearBuffer() {\n        byte[] data = new byte[size];\n        System.arraycopy(buffer, 0, data, 0, size);\n        size = 0;\n        pos = 0;\n        return data;\n    }\n\n    public int getPos() {\n        return pos;\n    }\n\n    public int getSize() {\n        return size;\n    }\n\n    public int getGap() {\n        return size - pos;\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/java/com/alipay/sofa/ark/common/util/StringUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.util;\n\nimport java.util.*;\n\n/**\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class StringUtils {\n\n    public static final String EMPTY_STRING = \"\";\n    public static final int    CHAR_A       = 'A';\n    public static final int    CHAR_Z       = 'Z';\n    public static final int    CASE_GAP     = 32;\n    public static final String CR           = \"\\r\";\n\n    /**\n     * <p>Checks if a String is empty (\"\") or null.</p>\n     *\n     * @param str  the String to check, may be null\n     * @return <code>true</code> if the String is empty or null\n     */\n    public static boolean isEmpty(String str) {\n        return str == null || str.trim().length() == 0;\n    }\n\n    /**\n     * <p>Checks whether two String are equal.</p>\n     * @param a comparator string a\n     * @param b comparator string b\n     * @return whether two string equals\n     */\n    public static boolean isSameStr(String a, String b) {\n        if (a == null && b != null) {\n            return false;\n        }\n\n        if (a == null && b == null) {\n            return true;\n        }\n\n        return a.equals(b);\n    }\n\n    public static String setToStr(Set<String> stringSet, String delimiter) {\n        return setToStr(stringSet, delimiter, EMPTY_STRING);\n    }\n\n    public static boolean contains(String sourceStr, String searchStr) {\n        if (sourceStr != null && searchStr != null) {\n            return sourceStr.contains(searchStr);\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * <p>Transform a string set to a long string separated by delimiter</p>\n     * @param stringSet\n     * @param delimiter\n     * @param defaultRet if stringSet is empty, return defaultRet\n     * @return\n     */\n    public static String setToStr(Set<String> stringSet, String delimiter, String defaultRet) {\n        if (stringSet == null || stringSet.isEmpty()) {\n            return defaultRet;\n        }\n\n        AssertUtils.assertNotNull(delimiter, \"Delimiter should not be null.\");\n\n        StringBuilder sb = new StringBuilder();\n        for (String str : stringSet) {\n            sb.append(str.trim()).append(delimiter);\n        }\n\n        return sb.toString().substring(0, sb.length() - delimiter.length());\n    }\n\n    public static Set<String> strToSet(String str, String delimiter) {\n        return new LinkedHashSet<>(strToList(str, delimiter));\n    }\n\n    public static List<String> strToList(String str, String delimiter) {\n        if (str == null || str.isEmpty()) {\n            return Collections.emptyList();\n        }\n        List<String> stringList = new ArrayList<>();\n        for (String s : str.split(delimiter)) {\n            stringList.add(s.trim());\n        }\n        return stringList;\n    }\n\n    /**\n     * Check whether start with anotherString when transformed to lower case.\n     *\n     * @param thisString\n     * @param anotherString\n     * @return\n     */\n    public static boolean startWithToLowerCase(String thisString, String anotherString) {\n        AssertUtils.assertNotNull(thisString, \"Param must not be null!\");\n        AssertUtils.assertNotNull(anotherString, \"Param must not be null!\");\n\n        if (thisString.length() < anotherString.length()) {\n            return false;\n        }\n\n        boolean ret;\n        int index = 0;\n        do {\n            if (thisString.charAt(index) > CHAR_Z || thisString.charAt(index) < CHAR_A) {\n                ret = thisString.charAt(index) == anotherString.charAt(index);\n            } else {\n                ret = thisString.charAt(index) + CASE_GAP == anotherString.charAt(index);\n            }\n        } while (ret && ++index < anotherString.length());\n        return ret;\n    }\n\n    public static String removeCR(String originalStr) {\n        return removeSpcChar(originalStr, CR);\n    }\n\n    /**\n     * <p>Remove Special Characters, Such as \\t、\\n、\\r</p>\n     *\n     * @param originalStr  the String to deal\n     * @param spcChar      Special char\n     * @return removed string\n     */\n    public static String removeSpcChar(String originalStr, String spcChar) {\n        AssertUtils.assertNotNull(spcChar, \"SpcChar must not be null!\");\n        if (originalStr == null || originalStr.isEmpty()) {\n            return originalStr;\n        }\n        return originalStr.replaceAll(String.format(\"[%s]\", spcChar), EMPTY_STRING);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/java/com/alipay/sofa/ark/common/util/ThreadPoolUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.util;\n\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.LinkedBlockingDeque;\nimport java.util.concurrent.PriorityBlockingQueue;\nimport java.util.concurrent.SynchronousQueue;\n\n/**\n * Thread pool utils\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic class ThreadPoolUtils {\n\n    /**\n     * Build Queue\n     *\n     * @param size size of queue\n     * @return queue\n     */\n    public static BlockingQueue<Runnable> buildQueue(int size) {\n        return buildQueue(size, false);\n    }\n\n    /**\n     * Build Queue\n     *\n     * @param size size of queue\n     * @param isPriority whether use priority queue or not\n     * @return queue\n     */\n    public static BlockingQueue<Runnable> buildQueue(int size, boolean isPriority) {\n        BlockingQueue<Runnable> queue;\n        if (size == 0) {\n            queue = new SynchronousQueue<>();\n        } else {\n            if (isPriority) {\n                queue = size < 0 ? new PriorityBlockingQueue<Runnable>()\n                    : new PriorityBlockingQueue<Runnable>(size);\n            } else {\n                queue = size < 0 ? new LinkedBlockingDeque<Runnable>()\n                    : new LinkedBlockingDeque<Runnable>(size);\n            }\n        }\n        return queue;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/resources/com/alipay/sofa/ark/log/log4j/log-conf.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE log4j:configuration SYSTEM \"log4j.dtd\">\n\n<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\">\n    <appender name=\"ERROR-APPENDER\" class=\"org.apache.log4j.DailyRollingFileAppender\">\n        <param name=\"file\" value=\"${logging.path}/sofa-ark/common-error.log\"/>\n        <param name=\"append\" value=\"true\"/>\n        <param name=\"encoding\" value=\"${file.encoding}\"/>\n        <param name=\"threshold\" value=\"error\"/>\n        <layout class=\"org.apache.log4j.PatternLayout\">\n            <param name=\"ConversionPattern\" value=\"%d %-5p %-32t - %m%n\"/>\n        </layout>\n    </appender>\n\n    <appender name=\"SOFA-APPENDER\" class=\"org.apache.log4j.DailyRollingFileAppender\">\n        <param name=\"file\" value=\"${logging.path}/sofa-ark/common-default.log\"/>\n        <param name=\"append\" value=\"true\"/>\n        <param name=\"encoding\" value=\"${file.encoding}\"/>\n        <param name=\"threshold\" value=\"${logging.level.com.alipay.sofa.ark}\"/>\n        <layout class=\"org.apache.log4j.PatternLayout\">\n            <param name=\"ConversionPattern\" value=\"%d %-5p %-32t - %m%n\"/>\n        </layout>\n    </appender>\n\n    <appender name=\"CONFIG-APPENDER\" class=\"org.apache.log4j.DailyRollingFileAppender\">\n        <param name=\"file\" value=\"${logging.path}/sofa-ark/config-manage.log\"/>\n        <param name=\"append\" value=\"true\"/>\n        <param name=\"encoding\" value=\"${file.encoding}\"/>\n        <param name=\"threshold\" value=\"${logging.level.com.alipay.sofa.ark}\"/>\n        <layout class=\"org.apache.log4j.PatternLayout\">\n            <param name=\"ConversionPattern\" value=\"%d %-5p %-32t - %m%n\"/>\n        </layout>\n    </appender>\n\n    <logger name=\"com.alipay.sofa.ark\" additivity=\"false\">\n        <level value=\"${logging.level.com.alipay.sofa.ark}\"/>\n        <appender-ref ref=\"SOFA-APPENDER\"/>\n        <appender-ref ref=\"ERROR-APPENDER\"/>\n    </logger>\n\n    <logger name=\"com.alipay.sofa.ark.config\" additivity=\"false\">\n        <level value=\"${logging.level.com.alipay.sofa.ark}\"/>\n        <appender-ref ref=\"CONFIG-APPENDER\"/>\n        <appender-ref ref=\"ERROR-APPENDER\"/>\n    </logger>\n\n    <root>\n        <level value=\"${ARK_LOG_LEVEL}\"/>\n        <appender-ref ref=\"SOFA-APPENDER\"/>\n        <appender-ref ref=\"ERROR-APPENDER\"/>\n    </root>\n</log4j:configuration>\n"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/resources/com/alipay/sofa/ark/log/log4j2/log-conf.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration status=\"OFF\">\n    <Properties>\n        <Property name=\"ARK_LOG_LEVEL\">${sys:logging.level.com.alipay.sofa.ark}</Property>\n        <Property name=\"ARK_LOG_PATH\">${sys:logging.path}</Property>\n        <Property name=\"ARK_FILE_ENCODING\">${sys:file.encoding}</Property>\n    </Properties>\n\n    <Appenders>\n        <RollingFile name=\"ERROR-APPENDER\" fileName=\"${ARK_LOG_PATH}/sofa-ark/common-error.log\" append=\"true\"\n                     filePattern=\"${ARK_LOG_PATH}/sofa-ark/common-error.log.%d{yyyy-MM-dd}\">\n            <ThresholdFilter level=\"error\" onMatch=\"ACCEPT\" onMismatch=\"DENY\"/>\n            <PatternLayout charset=\"${ARK_FILE_ENCODING}\">\n                <pattern>%d %-5p %-32t - %m%n</pattern>\n            </PatternLayout>\n            <Policies>\n                <TimeBasedTriggeringPolicy interval=\"1\" modulate=\"true\"/>\n            </Policies>\n        </RollingFile>\n\n        <RollingFile name=\"SOFA-APPENDER\" fileName=\"${ARK_LOG_PATH}/sofa-ark/common-default.log\" append=\"true\"\n                     filePattern=\"${ARK_LOG_PATH}/sofa-ark/common-default.log.%d{yyyy-MM-dd}\">\n            <ThresholdFilter level=\"${ARK_LOG_LEVEL}\" onMatch=\"ACCEPT\" onMismatch=\"DENY\"/>\n            <PatternLayout charset=\"${ARK_FILE_ENCODING}\">\n                <pattern>%d %-5p %-32t - %m%n</pattern>\n            </PatternLayout>\n            <Policies>\n                <TimeBasedTriggeringPolicy interval=\"1\" modulate=\"true\"/>\n            </Policies>\n        </RollingFile>\n\n        <RollingFile name=\"CONFIG-APPENDER\" fileName=\"${ARK_LOG_PATH}/sofa-ark/config-manager.log\" append=\"true\"\n                     filePattern=\"${ARK_LOG_PATH}/sofa-ark/config-manager.log.%d{yyyy-MM-dd}\">\n            <ThresholdFilter level=\"${ARK_LOG_LEVEL}\" onMatch=\"ACCEPT\" onMismatch=\"DENY\"/>\n            <PatternLayout charset=\"${ARK_FILE_ENCODING}\">\n                <pattern>%d %-5p %-32t %c - %m%n</pattern>\n            </PatternLayout>\n            <Policies>\n                <TimeBasedTriggeringPolicy interval=\"1\" modulate=\"true\"/>\n            </Policies>\n        </RollingFile>\n    </Appenders>\n\n    <Loggers>\n        <Logger name=\"com.alipay.sofa.ark\" level=\"${ARK_LOG_LEVEL}\" additivity=\"false\">\n            <appender-ref ref=\"SOFA-APPENDER\"/>\n            <appender-ref ref=\"ERROR-APPENDER\"/>\n        </Logger>\n\n        <Logger name=\"com.alipay.sofa.ark.config\" level=\"${ARK_LOG_LEVEL}\" additivity=\"false\">\n            <appender-ref ref=\"CONFIG-APPENDER\"/>\n            <appender-ref ref=\"ERROR-APPENDER\"/>\n        </Logger>\n\n        <Root level=\"${ARK_LOG_LEVEL}\">\n            <appender-ref ref=\"SOFA-APPENDER\"/>\n            <appender-ref ref=\"ERROR-APPENDER\"/>\n        </Root>\n    </Loggers>\n</configuration>\n"
  },
  {
    "path": "sofa-ark-parent/core/common/src/main/resources/com/alipay/sofa/ark/log/logback/log-conf.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Licensed to the Apache Software Foundation (ASF) under one or more\n  ~ contributor license agreements.  See the NOTICE file distributed with\n  ~ this work for additional information regarding copyright ownership.\n  ~ The ASF licenses this file to You under the Apache License, Version 2.0\n  ~ (the \"License\"); you may not use this file except in compliance with\n  ~ the License.  You may obtain a copy of the License at\n  ~\n  ~     http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<configuration>\n    <appender name=\"ERROR-APPENDER\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <append>true</append>\n        <filter class=\"ch.qos.logback.classic.filter.ThresholdFilter\">\n            <level>error</level>\n            <onMatch>ACCEPT</onMatch>\n            <onMismatch>DENY</onMismatch>\n        </filter>\n        <file>${logging.path}/sofa-ark/common-error.log</file>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\n            <FileNamePattern>${logging.path}/sofa-ark/common-error.log.%d{yyyy-MM-dd}</FileNamePattern>\n            <MaxHistory>30</MaxHistory>\n        </rollingPolicy>\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>%d %-5p %-32t - %m%n</pattern>\n            <charset>${file.encoding}</charset>\n        </encoder>\n    </appender>\n\n    <appender name=\"SOFA-APPENDER\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <append>true</append>\n        <filter class=\"ch.qos.logback.classic.filter.ThresholdFilter\">\n            <level>${logging.level.com.alipay.sofa.ark}</level>\n            <onMatch>ACCEPT</onMatch>\n            <onMismatch>DENY</onMismatch>\n        </filter>\n        <file>${logging.path}/sofa-ark/common-default.log</file>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\n            <FileNamePattern>${logging.path}/sofa-ark/common-default.log.%d{yyyy-MM-dd}</FileNamePattern>\n            <MaxHistory>30</MaxHistory>\n        </rollingPolicy>\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>%d %-5p %-32t - %m%n</pattern>\n            <charset>${file.encoding}</charset>\n        </encoder>\n    </appender>\n\n    <appender name=\"CONFIG-APPENDER\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <append>true</append>\n        <filter class=\"ch.qos.logback.classic.filter.ThresholdFilter\">\n            <level>${logging.level.com.alipay.sofa.ark}</level>\n            <onMatch>ACCEPT</onMatch>\n            <onMismatch>DENY</onMismatch>\n        </filter>\n        <file>${logging.path}/sofa-ark/config-manage.log</file>\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\n            <FileNamePattern>${logging.path}/sofa-ark/config-manage.log.%d{yyyy-MM-dd}</FileNamePattern>\n            <MaxHistory>30</MaxHistory>\n        </rollingPolicy>\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>%d %-5p %-32t - %m%n</pattern>\n            <charset>${file.encoding}</charset>\n        </encoder>\n    </appender>\n\n    <logger name=\"com.alipay.sofa.ark\" level=\"${logging.level.com.alipay.sofa.ark}\" additivity=\"false\">\n        <appender-ref ref=\"SOFA-APPENDER\"/>\n        <appender-ref ref=\"ERROR-APPENDER\"/>\n    </logger>\n\n    <logger name=\"com.alipay.sofa.ark.config\" level=\"${logging.level.com.alipay.sofa.ark}\" additivity=\"false\">\n        <appender-ref ref=\"CONFIG-APPENDER\"/>\n        <appender-ref ref=\"ERROR-APPENDER\"/>\n    </logger>\n\n    <root level=\"${ARK_LOG_LEVEL}\">\n        <appender-ref ref=\"SOFA-APPENDER\"/>\n        <appender-ref ref=\"ERROR-APPENDER\"/>\n    </root>\n</configuration>\n"
  },
  {
    "path": "sofa-ark-parent/core/common/src/test/java/com/alipay/sofa/ark/common/adapter/ArkLogbackContextSelectorTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.adapter;\n\nimport ch.qos.logback.classic.ClassicConstants;\nimport ch.qos.logback.classic.LoggerContext;\nimport ch.qos.logback.classic.selector.ContextSelector;\nimport ch.qos.logback.classic.util.ContextSelectorStaticBinder;\nimport ch.qos.logback.core.CoreConstants;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.slf4j.ILoggerFactory;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.net.URL;\nimport java.net.URLClassLoader;\n\n/**\n * @author:     yuanyuan\n * @date:    2023/12/12 5:02 下午\n */\npublic class ArkLogbackContextSelectorTest {\n\n    @Test\n    public void testContextSelector() throws NoSuchMethodException, InvocationTargetException,\n                                     IllegalAccessException {\n        System.setProperty(ClassicConstants.LOGBACK_CONTEXT_SELECTOR,\n            \"com.alipay.sofa.ark.common.adapter.ArkLogbackContextSelector\");\n        Logger logger = LoggerFactory.getLogger(ArkLogbackContextSelectorTest.class);\n        System.clearProperty(ClassicConstants.LOGBACK_CONTEXT_SELECTOR);\n\n        ILoggerFactory iLoggerFactory = LoggerFactory.getILoggerFactory();\n        LoggerContext loggerContext = (LoggerContext) iLoggerFactory;\n        ContextSelectorStaticBinder selectorStaticBinder = ContextSelectorStaticBinder\n            .getSingleton();\n        ContextSelector contextSelector = selectorStaticBinder.getContextSelector();\n        Assert.assertTrue(contextSelector instanceof ArkLogbackContextSelector);\n        Assert.assertEquals(loggerContext, contextSelector.getDefaultLoggerContext());\n\n        URL url = ArkLogbackContextSelectorTest.class.getClassLoader().getResource(\"\");\n        URLClassLoader loader = new URLClassLoader(new URL[] { url }, null);\n        String contextName = CoreConstants.DEFAULT_CONTEXT_NAME;\n\n        Method getContext = ArkLogbackContextSelector.class.getDeclaredMethod(\"getContext\",\n            ClassLoader.class);\n        getContext.setAccessible(true);\n        Object invoke = getContext.invoke(contextSelector, loader);\n        Assert.assertNotNull(invoke);\n        Assert.assertEquals(invoke, contextSelector.getLoggerContext(contextName));\n        Assert.assertTrue(contextSelector.getContextNames().contains(contextName));\n\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/common/src/test/java/com/alipay/sofa/ark/common/thread/CommonThreadPoolTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.thread;\n\nimport org.junit.Test;\n\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.LinkedBlockingDeque;\nimport java.util.concurrent.PriorityBlockingQueue;\n\nimport static com.alipay.sofa.ark.common.thread.ThreadPoolManager.*;\nimport static com.alipay.sofa.ark.common.util.ThreadPoolUtils.buildQueue;\nimport static java.lang.Integer.MAX_VALUE;\nimport static org.apache.commons.beanutils.BeanUtils.copyProperties;\nimport static org.junit.Assert.*;\n\npublic class CommonThreadPoolTest {\n\n    private CommonThreadPool commonThreadPool;\n\n    @Test\n    public void testCommonThreadPool() throws Exception {\n\n        CommonThreadPool commonThreadPool = new CommonThreadPool();\n        copyProperties(commonThreadPool, commonThreadPool);\n        commonThreadPool.setPrestartAllCoreThreads(true);\n        assertNotNull(commonThreadPool.getExecutor());\n\n        registerThreadPool(\"a\", commonThreadPool);\n        assertNotNull(getThreadPool(\"a\"));\n        unRegisterUserThread(\"a\");\n        assertNull(getThreadPool(\"a\"));\n    }\n\n    @Test\n    public void testBuildQueue() {\n\n        BlockingQueue<Runnable> queue = buildQueue(5, true);\n        assertEquals(PriorityBlockingQueue.class, queue.getClass());\n        assertEquals(MAX_VALUE, queue.remainingCapacity());\n\n        queue = buildQueue(-1, true);\n        assertEquals(PriorityBlockingQueue.class, queue.getClass());\n        assertEquals(0, queue.size());\n\n        queue = buildQueue(5, false);\n        assertEquals(LinkedBlockingDeque.class, queue.getClass());\n        assertEquals(5, queue.remainingCapacity());\n\n        queue = buildQueue(-1, false);\n        assertEquals(LinkedBlockingDeque.class, queue.getClass());\n        assertEquals(0, queue.size());\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/common/src/test/java/com/alipay/sofa/ark/common/util/AssertUtilsTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.util;\n\nimport org.junit.Test;\n\nimport java.io.File;\n\nimport static com.alipay.sofa.ark.common.util.AssertUtils.*;\nimport static com.alipay.sofa.ark.common.util.FileUtils.file;\nimport static java.lang.System.getProperty;\nimport static org.junit.Assert.assertTrue;\n\n/**\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class AssertUtilsTest {\n\n    public static File getTmpDir() {\n        String tmpPath = getProperty(\"java.io.tmpdir\");\n        return file(tmpPath);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testAssertNotNullNull() {\n        String msg = \"object is null\";\n        try {\n            assertNotNull(null, msg);\n        } catch (Exception e) {\n            assertTrue(e instanceof IllegalArgumentException);\n            assertTrue(e.getMessage().contains(msg));\n            throw e;\n        }\n    }\n\n    @Test\n    public void testAssertNotNullNotNull() {\n        assertNotNull(new Object(), \"object is null\");\n    }\n\n    @Test\n    public void testAssertIsTrue() {\n        isTrue(true, \"Exception %s\", \"error\");\n        try {\n            isTrue(false, \"Exception %s\", \"error\");\n        } catch (IllegalArgumentException ex) {\n            assertTrue(\"Exception error\".equals(ex.getMessage()));\n        }\n    }\n\n    @Test\n    public void testAssertIsFalse() {\n        isFalse(false, \"Exception %s\", \"error\");\n        try {\n            isFalse(true, \"Exception %s\", \"error\");\n        } catch (IllegalArgumentException ex) {\n            assertTrue(\"Exception error\".equals(ex.getMessage()));\n        }\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void assertNull() {\n        AssertUtils.assertNull(new Object(), \"should be nul!\");\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/common/src/test/java/com/alipay/sofa/ark/common/util/BizIdentityUtilsTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.util;\n\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.mockito.Mockito;\n\nimport static org.mockito.Mockito.when;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class BizIdentityUtilsTest {\n    @Test\n    public void testBizIdentity() {\n        Biz biz = Mockito.mock(Biz.class);\n        when(biz.getBizName()).thenReturn(\"biz-demo\");\n        when(biz.getBizVersion()).thenReturn(\"1.0.0\");\n\n        String bizIdentity = BizIdentityUtils.generateBizIdentity(biz);\n        Assert.assertEquals(\"biz-demo:1.0.0\", bizIdentity);\n\n        Assert.assertFalse(BizIdentityUtils.isValid(null));\n        Assert.assertFalse(BizIdentityUtils.isValid(\"\"));\n        Assert.assertFalse(BizIdentityUtils.isValid(\"name:1.0:1.0\"));\n        Assert.assertTrue(BizIdentityUtils.isValid(\"name:1.0\"));\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core/common/src/test/java/com/alipay/sofa/ark/common/util/ClassLoaderUtilTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.util;\n\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.mockito.MockedStatic;\nimport org.mockito.Mockito;\n\nimport java.lang.management.ManagementFactory;\nimport java.lang.management.RuntimeMXBean;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport static com.alipay.sofa.ark.common.util.EnvironmentUtils.*;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNull;\nimport static org.mockito.Mockito.when;\n\n/**\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class ClassLoaderUtilTest {\n\n    private class MockClassLoader extends ClassLoader {\n\n        private final URLClassPath ucp;\n\n        private MockClassLoader(URL[] urls) {\n            ucp = new URLClassPath(urls);\n        }\n\n        private URL[] getURLs() {\n            return ucp.getURLs();\n        }\n\n        private class URLClassPath {\n            private ArrayList<URL> path = new ArrayList<>();\n\n            private URLClassPath(URL[] urls) {\n                Collections.addAll(path, urls);\n            }\n\n            private URL[] getURLs() {\n                return this.path.toArray(new URL[0]);\n            }\n        }\n    }\n\n    @Test\n    public void testPushContextClassLoader() {\n        ClassLoader classLoader = new URLClassLoader(new URL[] {});\n        ClassLoaderUtils.pushContextClassLoader(classLoader);\n        Assert.assertEquals(classLoader, Thread.currentThread().getContextClassLoader());\n    }\n\n    @Test\n    public void testPopContextClassLoader() {\n        ClassLoader classLoader = new URLClassLoader(new URL[] {});\n        ClassLoaderUtils.popContextClassLoader(classLoader);\n        Assert.assertEquals(classLoader, Thread.currentThread().getContextClassLoader());\n    }\n\n    @Test\n    @SuppressWarnings({ \"restriction\", \"unchecked\" })\n    public void testGetURLs() {\n        ClassLoader urlClassLoader = new URLClassLoader(new URL[] {});\n        Assert.assertArrayEquals(((URLClassLoader) urlClassLoader).getURLs(),\n            ClassLoaderUtils.getURLs(urlClassLoader));\n\n        ClassLoader appClassLoader = this.getClass().getClassLoader();\n        URL[] urls = null;\n        if (appClassLoader instanceof URLClassLoader) {\n            urls = ((URLClassLoader) appClassLoader).getURLs();\n            Assert.assertArrayEquals(urls, ClassLoaderUtils.getURLs(appClassLoader));\n        } else {\n            String classpath = System.getProperty(\"java.class.path\");\n            String[] classpathEntries = classpath.split(System.getProperty(\"path.separator\"));\n            List<URL> classpathURLs = new ArrayList<>();\n            for (String classpathEntry : classpathEntries) {\n                URL url = null;\n                try {\n                    url = FileUtils.file(classpathEntry).toURI().toURL();\n                } catch (MalformedURLException e) {\n                    e.printStackTrace();\n                    throw new ArkRuntimeException(\"Failed to get urls from \" + appClassLoader, e);\n                }\n                classpathURLs.add(url);\n            }\n            urls = classpathURLs.toArray(new URL[0]);\n        }\n        Assert.assertArrayEquals(urls, ClassLoaderUtils.getURLs(appClassLoader));\n\n        URL[] urLs = ClassLoaderUtils.getURLs(null);\n        Assert.assertNotNull(urLs);\n\n    }\n\n    @Test\n    public void testGetAgentClassPath() {\n        List<String> mockArguments = new ArrayList<>();\n        String workingPath = this.getClass().getClassLoader()\n                .getResource(\"\").getPath();\n        mockArguments.add(String.format(\"-javaagent:%s\", workingPath));\n\n        RuntimeMXBean runtimeMXBean = Mockito.mock(RuntimeMXBean.class);\n        when(runtimeMXBean.getInputArguments()).thenReturn(mockArguments);\n\n        MockedStatic<ManagementFactory> managementFactoryMockedStatic = Mockito.mockStatic(ManagementFactory.class);\n        managementFactoryMockedStatic.when(ManagementFactory::getRuntimeMXBean).thenReturn(runtimeMXBean);\n\n        URL[] agentUrl = ClassLoaderUtils.getAgentClassPath();\n        Assert.assertEquals(1, agentUrl.length);\n\n        managementFactoryMockedStatic.close();\n    }\n\n    @Test\n    public void testParseSkyWalkingAgentPath() {\n        List<String> mockArguments = new ArrayList<>();\n        String workingPath = this.getClass().getClassLoader()\n                .getResource(\"sample-skywalking-agent.jar\").getPath();\n        mockArguments.add(String.format(\"-javaagent:%s\", workingPath));\n        RuntimeMXBean runtimeMXBean = Mockito.mock(RuntimeMXBean.class);\n        when(runtimeMXBean.getInputArguments()).thenReturn(mockArguments);\n\n        MockedStatic<ManagementFactory> managementFactoryMockedStatic = Mockito.mockStatic(ManagementFactory.class);\n        managementFactoryMockedStatic.when(ManagementFactory::getRuntimeMXBean).thenReturn(runtimeMXBean);\n\n        URL[] agentUrl = ClassLoaderUtils.getAgentClassPath();\n        Assert.assertEquals(2, agentUrl.length);\n\n        managementFactoryMockedStatic.close();\n    }\n\n    @Test\n    public void testEnvironmentUtils() {\n        assertNull(getProperty(\"not_exists_prop\"));\n        setSystemProperty(\"not_exists_prop\", \"aaa\");\n        assertEquals(\"aaa\", getProperty(\"not_exists_prop\"));\n        clearSystemProperty(\"not_exists_prop\");\n        assertNull(getProperty(\"not_exists_prop\"));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/common/src/test/java/com/alipay/sofa/ark/common/util/ClassUtilsTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.util;\n\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport static com.alipay.sofa.ark.common.util.ClassUtils.collectClasses;\nimport static com.alipay.sofa.ark.common.util.ClassUtils.getPackageName;\nimport static com.alipay.sofa.ark.spi.constant.Constants.DEFAULT_PACKAGE;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * @author qilong.zql\n * @since 0.3.0\n */\npublic class ClassUtilsTest {\n\n    @Test\n    public void testGetPackageName() {\n        assertEquals(\"a.b\", getPackageName(\"a.b.C\"));\n        assertEquals(DEFAULT_PACKAGE, getPackageName(\"C\"));\n    }\n\n    @Test\n    public void testCollectClasses() throws Exception {\n\n        File dir = new File(\"target/classes\");\n        // fix mvn test fail issues\n        File dir2 = new File(dir.getAbsolutePath());\n        if (!dir2.exists()) {\n            return;\n        }\n\n        Set<String> classNames = new HashSet<>(collectClasses(dir2));\n        assertTrue(classNames.contains(\"com.alipay.sofa.ark.common.util.ClassUtils\"));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/common/src/test/java/com/alipay/sofa/ark/common/util/FileUtilsTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.util;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\n\nimport static com.alipay.sofa.ark.common.util.FileUtils.*;\nimport static java.lang.System.getProperty;\nimport static java.lang.System.setProperty;\nimport static org.apache.commons.io.FileUtils.deleteQuietly;\nimport static org.apache.commons.io.FileUtils.touch;\nimport static org.junit.Assert.*;\n\n/**\n * @author guolei.sgl (guolei.sgl@antfin.com) 2019/7/28 11:24 PM\n * @since\n **/\npublic class FileUtilsTest {\n\n    private static final String ORIGIN = getProperty(\"os.name\");\n\n    @Before\n    public void before() {\n        setProperty(\"os.name\", \"windows\");\n    }\n\n    @After\n    public void after() {\n        setProperty(\"os.name\", ORIGIN);\n    }\n\n    @Test\n    public void testGetCompatiblePath() {\n        String winPath = getCompatiblePath(\"C:\\\\a\\\\b\\\\c\");\n        assertTrue(winPath.contains(\"/\"));\n        String macPath = getCompatiblePath(\"/a/b/c\");\n        assertTrue(winPath.contains(macPath));\n    }\n\n    @Test\n    public void testSHA1Hash() throws IOException {\n        URL url = this.getClass().getResource(this.getClass().getSimpleName() + \".class\");\n        assertNotNull(sha1Hash(file(url.getFile())));\n    }\n\n    @Test\n    public void testUnzip() throws IOException {\n        URL sampleBiz = this.getClass().getClassLoader().getResource(\"sample-biz.jar\");\n        File file = file(sampleBiz.getFile());\n        assertNotNull(unzip(file, file.getAbsolutePath() + \"-unpack\"));\n    }\n\n    @Test\n    public void testMkdir() {\n        assertNull(mkdir(\"\"));\n        // test recursive creation\n        File newDir = mkdir(\"C:\\\\a\\\\b\\\\c\");\n        assertNotNull(newDir);\n        // test for exist path\n        assertNotNull(mkdir(\"C:\\\\a\\\\b\\\\c\"));\n        // del the dir\n        deleteQuietly(newDir);\n    }\n\n    @Test\n    public void testDecodePath() {\n        String path = \"C:\\\\temp dir\\\\b\\\\c\";\n        String encodedPath = \"C:\\\\temp%20dir\\\\b\\\\c\";\n        assertEquals(path, decodePath(path));\n        assertEquals(path, decodePath(encodedPath));\n    }\n\n    @Test\n    public void testNewFile() throws IOException {\n\n        String dir = \"C:\\\\temp dir\\\\b\\\\c\";\n        String encodedPath = \"C:\\\\temp%20dir\\\\b\\\\c\";\n        mkdir(dir);\n        touch(new File(dir, \"test.txt\"));\n        File file = file(encodedPath, \"test.txt\");\n        assertNotNull(file);\n        assertTrue(file.exists());\n\n        file = new File(encodedPath, \"test.txt\");\n        assertFalse(file.exists());\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/common/src/test/java/com/alipay/sofa/ark/common/util/ParseUtilsTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.util;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * @author guolei.sgl (guolei.sgl@antfin.com) 2019/11/23 11:18 AM\n * @since\n **/\npublic class ParseUtilsTest {\n\n    Set<String> candidates  = new HashSet<>();\n\n    Set<String> resources   = new HashSet<>();\n\n    Set<String> stems       = new HashSet<>();\n\n    Set<String> suffixStems = new HashSet<>();\n\n    @Before\n    public void before() {\n        candidates.add(\"spring.xsd\");\n        candidates.add(\"*.xsd\");\n        candidates.add(\"spring/*\");\n        candidates.add(Constants.PACKAGE_PREFIX_MARK);\n    }\n\n    @Test\n    public void testParseUtils() {\n        ParseUtils.parseResourceAndStem(candidates, stems, suffixStems, resources);\n        Assert.assertTrue(stems.size() == 1 && stems.contains(\"spring/\"));\n        Assert.assertTrue(resources.size() == 1 && resources.contains(\"spring.xsd\"));\n        Assert.assertTrue(suffixStems.size() == 1 && suffixStems.contains(\".xsd\"));\n    }\n\n    @After\n    public void after() {\n        candidates.clear();\n        resources.clear();\n        stems.clear();\n        suffixStems.clear();\n        candidates = null;\n        resources = null;\n        suffixStems = null;\n        stems = null;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/common/src/test/java/com/alipay/sofa/ark/common/util/PortSelectUtilsTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.util;\n\nimport org.junit.Test;\n\nimport java.io.IOException;\nimport java.net.ServerSocket;\n\n/**\n * @author qilong.zql\n * @since 0.5.0\n */\npublic class PortSelectUtilsTest {\n\n    @Test\n    public void selectMinimumPort() {\n        int port = PortSelectUtils.selectAvailablePort(1050, 100);\n        AssertUtils.isTrue(port == PortSelectUtils.MIN_PORT_NUMBER, \"Select Error Port.\");\n    }\n\n    @Test\n    public void selectUnusedPort() {\n        int port = PortSelectUtils.selectAvailablePort(1234, 100);\n        AssertUtils.isTrue(port == 1234, \"Select Error Port.\");\n    }\n\n    @Test\n    public void selectUsedPort() {\n        try (ServerSocket ss = new ServerSocket(1234)) {\n            int port = PortSelectUtils.selectAvailablePort(1234, 100);\n            AssertUtils.isTrue(port == 1235, \"Select Error Port.\");\n        } catch (IOException ex) {\n            // ignore\n        }\n    }\n\n    @Test\n    public void selectSinglePort() {\n        try (ServerSocket ss = new ServerSocket(1234)) {\n            int port = PortSelectUtils.selectAvailablePort(1234, 1);\n            AssertUtils.isTrue(port == -1, \"Select Error Port.\");\n        } catch (IOException ex) {\n            // ignore\n        }\n    }\n\n    @Test\n    public void selectMaximumPort() {\n        int port = PortSelectUtils.selectAvailablePort(65555, 10);\n        AssertUtils.isTrue(port == -1, \"Select Error Port.\");\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/common/src/test/java/com/alipay/sofa/ark/common/util/SimpleByteBufferTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.util;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\n\npublic class SimpleByteBufferTest {\n\n    private SimpleByteBuffer simpleByteBuffer = new SimpleByteBuffer();\n\n    @Test\n    public void testSimpleByteBuffer() {\n\n        simpleByteBuffer.backSpace();\n        simpleByteBuffer.delete();\n        assertEquals(-1, simpleByteBuffer.goRight());\n        assertEquals(false, simpleByteBuffer.goLeft());\n\n        for (int i = 0; i <= 20; i++) {\n            simpleByteBuffer.add((byte) i);\n        }\n\n        assertEquals(0, simpleByteBuffer.goRight());\n        assertEquals(1, simpleByteBuffer.goRight());\n        assertEquals(true, simpleByteBuffer.goLeft());\n\n        for (int i = 0; i <= 40; i++) {\n            simpleByteBuffer.insert((byte) i);\n        }\n\n        assertEquals(1, simpleByteBuffer.goRight());\n        assertEquals(2, simpleByteBuffer.goRight());\n        assertEquals(true, simpleByteBuffer.goLeft());\n\n        simpleByteBuffer.backSpace();\n        assertEquals(2, simpleByteBuffer.goRight());\n\n        simpleByteBuffer.delete();\n        assertEquals(4, simpleByteBuffer.goRight());\n\n        assertEquals(60, simpleByteBuffer.getBuffer().length);\n        assertEquals(60, simpleByteBuffer.getSize());\n        assertEquals(44, simpleByteBuffer.getPos());\n        assertEquals(16, simpleByteBuffer.getGap());\n\n        assertEquals(60, simpleByteBuffer.getAndClearBuffer().length);\n        assertEquals(0, simpleByteBuffer.getSize());\n        assertEquals(0, simpleByteBuffer.getPos());\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/common/src/test/java/com/alipay/sofa/ark/common/util/StringUtilsTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.common.util;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.*;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class StringUtilsTest {\n\n    @Test\n    public void testEmpty() {\n        Assert.assertTrue(StringUtils.isEmpty(\"\"));\n        Assert.assertTrue(StringUtils.isEmpty(null));\n        Assert.assertFalse(StringUtils.isEmpty(\"aaa\"));\n    }\n\n    @Test\n    public void testSameStr() {\n        Assert.assertTrue(StringUtils.isSameStr(null, null));\n        Assert.assertFalse(StringUtils.isSameStr(null, \"\"));\n        Assert.assertFalse(StringUtils.isSameStr(null, \"a\"));\n\n        Assert.assertFalse(StringUtils.isSameStr(\"aa\", null));\n        Assert.assertFalse(StringUtils.isSameStr(\"aa\", \"\"));\n        Assert.assertFalse(StringUtils.isSameStr(\"aa\", \"a\"));\n        Assert.assertTrue(StringUtils.isSameStr(\"aa\", \"aa\"));\n    }\n\n    @Test\n    @SuppressWarnings(\"unchecked\")\n    public void testListToStr() {\n        Assert.assertTrue(\"\".equals(StringUtils.setToStr(null, \",\")));\n        Assert.assertTrue(\"\".equals(StringUtils.setToStr(Collections.<String> emptySet(), \",\")));\n        Assert.assertTrue(\"ast\".equals(StringUtils.setToStr(Collections.singleton(\"ast\"), \"&&\")));\n\n        LinkedHashSet linkedHashSet = new LinkedHashSet();\n\n        linkedHashSet.add(\"ab\");\n        linkedHashSet.add(\"  ba  \");\n        Assert.assertTrue(\"ab,ba\".equals(StringUtils.setToStr(linkedHashSet, \",\")));\n\n        linkedHashSet.add(\"cb\");\n        Assert.assertTrue(\"ab&&ba&&cb\".equals(StringUtils.setToStr(linkedHashSet, \"&&\")));\n\n        Assert.assertTrue(\"abbacb\".equals(StringUtils.setToStr(linkedHashSet, \"\")));\n    }\n\n    @Test\n    public void testContains() {\n        String a = \"This is my test source string\";\n        Assert.assertTrue(StringUtils.contains(a, \"my\"));\n        Assert.assertTrue(StringUtils.contains(a, \"\"));\n        Assert.assertFalse(StringUtils.contains(a, null));\n        Assert.assertFalse(StringUtils.contains(null, null));\n        Assert.assertFalse(StringUtils.contains(null, \"my\"));\n    }\n\n    @Test\n    public void testStrToList() {\n        List<String> list = StringUtils.strToList(\"ab,ba,cb\", \",\");\n        Assert.assertTrue(list.size() == 3);\n        Assert.assertTrue(list.get(0).equals(\"ab\"));\n        Assert.assertTrue(list.get(1).equals(\"ba\"));\n        Assert.assertTrue(list.get(2).equals(\"cb\"));\n    }\n\n    @Test\n    public void testStrToSet() {\n        Set<String> set = StringUtils.strToSet(null, \"&&\");\n        Assert.assertTrue(set.isEmpty());\n\n        set = StringUtils.strToSet(\"ab&&ba&&cb&&cb\", \"&&\");\n        Assert.assertTrue(set.size() == 3);\n\n        Object[] array = set.toArray();\n        Assert.assertTrue(array[0].equals(\"ab\"));\n        Assert.assertTrue(array[1].equals(\"ba\"));\n        Assert.assertTrue(array[2].equals(\"cb\"));\n    }\n\n    @Test\n    public void testStartWithToLowerCase() {\n        Assert.assertFalse(StringUtils.startWithToLowerCase(\"ab\", \"abc\"));\n        Assert.assertTrue(StringUtils.startWithToLowerCase(\"AbC\", \"abc\"));\n        Assert.assertTrue(StringUtils.startWithToLowerCase(\"aB#\", \"ab#\"));\n        Assert.assertTrue(StringUtils.startWithToLowerCase(\"~Ab\", \"~ab\"));\n    }\n\n    @Test\n    public void testRemoveCR() {\n        Assert.assertNull(StringUtils.removeCR(null));\n        Assert.assertEquals(\"\", StringUtils.removeCR(\"\"));\n        Assert.assertEquals(\"com.alipay.sofa:biz-child1-child1:1.0.0:jar:test\\n\",\n            StringUtils.removeCR(\"com.alipay.sofa:biz-child1-child1:1.0.0:jar:test\\r\\n\"));\n    }\n\n    @Test\n    public void testRemoveSpcChar() {\n        Assert.assertThrows(IllegalArgumentException.class, () -> {\n            StringUtils.removeSpcChar(\"\", null);\n        });\n        // 顺序无关\n        Assert.assertEquals(\"com.alipay.sofa:biz-child1-child1:1.0.0:jar:test\", StringUtils.removeSpcChar(\"com.alipay.sofa:biz-child1-child1:1.0.0:jar:test\\r\\n\\t\", \"\\t\\n\\r\"));\n        Assert.assertEquals(\"com.alipay.sofa:biz-child1-child1:1.0.0:jar:test\", StringUtils.removeSpcChar(\"com.alipay.sofa:biz-child1-child1:1.0.0:jar:test\\r\\n\", \"\\r\\n\"));\n        Assert.assertEquals(\"com.alipay.sofa:biz-child1-child1:1.0.0:jar:test\\r\", StringUtils.removeSpcChar(\"com.alipay.sofa:biz-child1-child1:1.0.0:jar:test\\r\\n\", \"\\n\"));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/exception/CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Module Overview\n\n**Artifact ID**: `sofa-ark-exception`\n**Package**: `com.alipay.sofa.ark.exception`\n\nThis module defines all exception types used in the SOFAArk project.\n\n## Purpose\n\n- Centralized exception definitions\n- Consistent error handling across the project\n\n## Key Classes\n\n### `ArkException`\nBase exception class for SOFAArk. Extends `RuntimeException`.\n\n### `ArkRuntimeException`\nRuntime exception for non-recoverable errors during Ark container operation.\n\n## Usage\n\nThrow these exceptions when:\n- Ark container fails to start\n- Plugin or Biz fails to load/start\n- Configuration errors\n- ClassLoader errors\n\n```java\nthrow new ArkRuntimeException(\"Failed to start Ark container\");\n```\n\n## Used By\n\nAll SOFAArk modules use these exception types for consistent error handling."
  },
  {
    "path": "sofa-ark-parent/core/exception/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <artifactId>sofa-ark-core</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n    </parent>\n\n    <artifactId>sofa-ark-exception</artifactId>\n    <name>${project.groupId}:${project.artifactId}</name>\n\n</project>\n"
  },
  {
    "path": "sofa-ark-parent/core/exception/src/main/java/com/alipay/sofa/ark/exception/ArkLoaderException.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.exception;\n\n/**\n * SOFAArk ClassLoader Exception\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class ArkLoaderException extends ClassNotFoundException {\n\n    private Throwable cause;\n\n    public ArkLoaderException(String s) {\n        super(s);\n    }\n\n    public ArkLoaderException(String s, Throwable ex) {\n        super(s);\n        this.cause = ex;\n    }\n\n    @Override\n    public Throwable fillInStackTrace() {\n        // dont fill stack trace, avoid cpu hotspot\n        return this;\n    }\n\n    public Throwable getCause() {\n        return cause;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core/exception/src/main/java/com/alipay/sofa/ark/exception/ArkRuntimeException.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.exception;\n\n/**\n * SOFAArk Runtime Exception\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class ArkRuntimeException extends RuntimeException {\n\n    public ArkRuntimeException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public ArkRuntimeException(Throwable cause) {\n        super(cause);\n    }\n\n    public ArkRuntimeException(String message) {\n        super(message);\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <artifactId>sofa-ark-parent</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n    </parent>\n\n    <artifactId>sofa-ark-core</artifactId>\n    <name>${project.groupId}:${project.artifactId}</name>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>spi</module>\n        <module>common</module>\n        <module>exception</module>\n        <module>api</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Module Overview\n\n**Artifact ID**: `sofa-ark-spi`\n**Package**: `com.alipay.sofa.ark.spi`\n\nThis module defines the Service Provider Interfaces (SPI) for SOFAArk. It contains all the core interfaces, models, and extension points that allow customization and integration with the Ark container.\n\n## Purpose\n\n- Define core interfaces for Ark container services\n- Define data models for Biz, Plugin, and Archive\n- Provide extension points for custom implementations\n- Define event system for lifecycle notifications\n\n## Key Packages\n\n### `spi.model`\nCore data models:\n- `Biz` / `BizInfo` / `BizState` / `BizConfig` / `BizOperation` - Business module model\n- `Plugin` / `PluginContext` / `PluginConfig` / `PluginOperation` - Plugin model\n\n### `spi.service`\nService interfaces:\n- `biz.BizManagerService` - Manage business modules lifecycle\n- `biz.BizFactoryService` - Create business module instances\n- `plugin.PluginManagerService` - Manage plugins\n- `plugin.PluginFactoryService` - Create plugin instances\n- `classloader.ClassLoaderService` - Manage classloaders\n- `event.EventAdminService` - Event publishing/subscribing\n- `injection.InjectionService` - Dependency injection\n- `extension.ArkServiceLoader` - SPI extension loading\n\n### `spi.archive`\nArchive interfaces:\n- `Archive` - Base archive interface\n- `BizArchive` - Business module archive\n- `PluginArchive` - Plugin archive\n- `ExecutableArchive` - Executable Ark archive\n- `ContainerArchive` - Container archive\n\n### `spi.pipeline`\nStartup pipeline interfaces:\n- `Pipeline` - Pipeline that processes startup stages\n- `PipelineStage` - Individual stage in the startup process\n- `PipelineContext` - Context passed between stages\n\n### `spi.event`\nEvent types for lifecycle notifications:\n- `biz.*` - Business module events (Before/After Install, Uninstall, Start, Stop, Switch)\n- `plugin.*` - Plugin events\n\n### `spi.constant.Constants`\nConstant definitions used throughout SOFAArk.\n\n## Extension Points\n\nImplement these interfaces to extend SOFAArk:\n1. `PipelineStage` - Add custom startup stages\n2. `PluginActivator` - Custom plugin activation logic\n3. `AddBizToStaticDeployHook` - Hook for adding biz during static deployment\n4. `EmbeddedServerService` - Custom embedded server implementation\n\n## Dependencies\n\n- `sofa-ark-exception` - Exception definitions\n\n## Used By\n\n- `sofa-ark-api` - API layer sits on top of SPI\n- `sofa-ark-container` - Implements all SPI services\n- `sofa-ark-archive` - Implements archive interfaces\n- All other SOFAArk modules depend on SPI"
  },
  {
    "path": "sofa-ark-parent/core/spi/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <artifactId>sofa-ark-core</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n    </parent>\n\n    <artifactId>sofa-ark-spi</artifactId>\n    <name>${project.groupId}:${project.artifactId}</name>\n\n    <dependencies>\n        <!--SOFAArk modules-->\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-exception</artifactId>\n        </dependency>\n\n        <!--test-->\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-beans</artifactId>\n            <version>5.3.29</version>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/archive/AbstractArchive.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.archive;\n\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * implementation of {@link ContainerArchive}, {@link PluginArchive}, {@link BizArchive}\n * should extends this\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic abstract class AbstractArchive implements Archive {\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public List<Archive> getNestedArchives(EntryFilter filter) throws IOException {\n        List<Archive> nestedArchives = new ArrayList();\n        for (Entry entry : this) {\n            if (filter.matches(entry)) {\n                nestedArchives.add(getNestedArchive(entry));\n            }\n        }\n        return Collections.unmodifiableList(nestedArchives);\n    }\n\n    public URL[] getUrls(EntryFilter entryFilter) throws IOException {\n        List<Archive> archives = getNestedArchives(entryFilter);\n\n        List<URL> urls = new ArrayList<>(archives.size() + 1);\n        urls.add(getUrl());\n\n        for (Archive archive : archives) {\n            urls.add(archive.getUrl());\n        }\n\n        return urls.toArray(new URL[urls.size()]);\n    }\n\n    public boolean isEntryExist(EntryFilter filter) {\n        for (Entry entry : this) {\n            if (filter.matches(entry)) {\n                return true;\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/archive/Archive.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.archive;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.List;\nimport java.util.jar.Manifest;\nimport java.util.zip.ZipEntry;\n\n/**\n * An archive that can be parsed in uniform style\n *\n * @author Phillip Webb\n */\npublic interface Archive extends Iterable<Archive.Entry> {\n\n    /**\n     * Returns a URL that can be used to load the archive.\n     * @return the archive URL\n     * @throws MalformedURLException if the URL is malformed\n     */\n    URL getUrl() throws MalformedURLException;\n\n    /**\n     * Returns the manifest of the archive.\n     * @return the manifest\n     * @throws IOException if the manifest cannot be read\n     */\n    Manifest getManifest() throws IOException;\n\n    /**\n     * Returns nested {@link Archive}s for entries that match the specified filter.\n     * @param filter the filter used to limit entries\n     * @return nested archives\n     * @throws IOException if nested archives cannot be read\n     */\n    List<Archive> getNestedArchives(EntryFilter filter) throws IOException;\n\n    /**\n     * getNestedArchive\n     * @param entry\n     * @return\n     * @throws IOException\n     */\n    Archive getNestedArchive(Entry entry) throws IOException;\n\n    /**\n     * getInputStream\n     * @param zipEntry\n     * @return\n     * @throws IOException\n     */\n    InputStream getInputStream(ZipEntry zipEntry) throws IOException;\n\n    /**\n     * Represents a single entry in the archive.\n     */\n    interface Entry {\n\n        /**\n         * Returns {@code true} if the entry represents a directory.\n         * @return if the entry is a directory\n         */\n        boolean isDirectory();\n\n        /**\n         * Returns the name of the entry.\n         * @return the name of the entry\n         */\n        String getName();\n\n    }\n\n    /**\n     * Strategy interface to filter {@link Entry Entries}.\n     */\n    interface EntryFilter {\n\n        /**\n         * Apply the jar entry filter.\n         * @param entry the entry to filter\n         * @return {@code true} if the filter matches\n         */\n        boolean matches(Entry entry);\n\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/archive/BizArchive.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.archive;\n\nimport java.io.IOException;\nimport java.net.URL;\n\n/**\n * An archive represent an ark-biz-module\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic interface BizArchive extends Archive {\n\n    /**\n     * fetch classpath of archive to startup ark-biz module\n     *\n     * @return the classpath contained in ark-biz archive\n     * @throws IOException throw io exception when get biz classpath\n     */\n    URL[] getUrls() throws IOException;\n\n    /**\n     * check whether the entry satisfy the given {@link com.alipay.sofa.ark.spi.archive.Archive.EntryFilter}\n     * exists or not\n     *\n     * @param filter\n     * @return\n     */\n    boolean isEntryExist(EntryFilter filter);\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/archive/ContainerArchive.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.archive;\n\nimport java.io.IOException;\nimport java.net.URL;\n\n/**\n * An archive represents an ark-container\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic interface ContainerArchive extends Archive {\n\n    /**\n     * fetch classpath of archive to startup ark-container\n     *\n     * @return the classpath contained in ark-container archive\n     * @throws IOException throw exception when meets error\n     */\n    URL[] getUrls() throws IOException;\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/archive/ExecutableArchive.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.archive;\n\nimport java.net.URL;\nimport java.util.List;\n\n/**\n * An archive represents an executable fat jar with sofa-ark\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic interface ExecutableArchive extends Archive {\n\n    /**\n     * Get Ark Container Archiver\n     * @return\n     * @throws Exception throw exception when meets error\n     */\n    ContainerArchive getContainerArchive() throws Exception;\n\n    /**\n     * Get all Biz Archiver\n     * @return\n     * @throws Exception throw exception when meets error\n     */\n    List<BizArchive> getBizArchives() throws Exception;\n\n    /**\n     * Get all Plugin Archiver\n     * @return\n     * @throws Exception throw exception when meets error\n     */\n    List<PluginArchive> getPluginArchives() throws Exception;\n\n    /**\n     * Get ark conf class path\n     *\n     * @return return ark conf class path\n     * @throws Exception throw exception when meets error\n     */\n    List<URL> getConfClasspath() throws Exception;\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/archive/PluginArchive.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.archive;\n\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.Set;\n\n/**\n * An archive represents an ark-plugin\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic interface PluginArchive extends Archive {\n\n    /**\n     * fetch classpath of archive to startup ark-plugin\n     *\n     * @return the classpath contained in ark-plugin archive\n     * @throws IOException throw exception when meets error\n     */\n    URL[] getUrls() throws IOException;\n\n    /**\n     * setExtensionUrls\n     * @param extensionUrls\n     */\n    void setExtensionUrls(URL[] extensionUrls);\n\n    /**\n     * check whether the entry satisfy the given {@link com.alipay.sofa.ark.spi.archive.Archive.EntryFilter}\n     * exists or not\n     *\n     * @param filter\n     * @return\n     */\n    boolean isEntryExist(EntryFilter filter);\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/argument/CommandArgument.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.argument;\n\n/**\n * SOFAArk command-line arguments\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic interface CommandArgument {\n\n    /**\n     * command-line arguments received by ark container.\n     * pattern: -A[key]=[value]\n     */\n    String ARK_CONTAINER_ARGUMENTS_MARK   = \"-A\";\n\n    String CLASSPATH_ARGUMENT_KEY         = \"classpath\";\n\n    String FAT_JAR_ARGUMENT_KEY           = \"jar\";\n\n    String CLASSPATH_SPLIT                = \",\";\n\n    String PROFILE                        = \"profile\";\n    String VM_PROFILE                     = \"ark.profile\";\n\n    String PROFILE_SPLIT                  = \",\";\n\n    /**\n     * command-line arguments received by bootstrap ark biz when execute in IDE.\n     * pattern: -B[key]=[value]\n     */\n    String ARK_BIZ_ARGUMENTS_MARK         = \"-B\";\n\n    String ENTRY_CLASS_NAME_ARGUMENT_KEY  = \"className\";\n\n    String ENTRY_METHOD_NAME_ARGUMENT_KEY = \"methodName\";\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/argument/LaunchCommand.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.argument;\n\nimport java.io.File;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static com.alipay.sofa.ark.spi.argument.CommandArgument.ARK_BIZ_ARGUMENTS_MARK;\nimport static com.alipay.sofa.ark.spi.argument.CommandArgument.ARK_CONTAINER_ARGUMENTS_MARK;\nimport static com.alipay.sofa.ark.spi.argument.CommandArgument.CLASSPATH_ARGUMENT_KEY;\nimport static com.alipay.sofa.ark.spi.argument.CommandArgument.CLASSPATH_SPLIT;\nimport static com.alipay.sofa.ark.spi.argument.CommandArgument.ENTRY_CLASS_NAME_ARGUMENT_KEY;\nimport static com.alipay.sofa.ark.spi.argument.CommandArgument.ENTRY_METHOD_NAME_ARGUMENT_KEY;\nimport static com.alipay.sofa.ark.spi.argument.CommandArgument.FAT_JAR_ARGUMENT_KEY;\nimport static com.alipay.sofa.ark.spi.argument.CommandArgument.PROFILE;\nimport static com.alipay.sofa.ark.spi.argument.CommandArgument.PROFILE_SPLIT;\nimport static com.alipay.sofa.ark.spi.argument.CommandArgument.VM_PROFILE;\nimport static com.alipay.sofa.ark.spi.constant.Constants.DEFAULT_PROFILE;\n\n/**\n * command argument parsed as a launchCommand\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class LaunchCommand {\n\n    private URL      executableArkBizJar;\n\n    private URL[]    classpath;\n\n    /**\n     * the following two configs are mainly used by bootstrap ark biz at startup of IDE.\n     */\n    private String   entryClassName;\n    private String   entryMethodName;\n\n    private String[] launchArgs;\n\n    private String[] profiles;\n\n    public boolean isExecutedByCommandLine() {\n        return executableArkBizJar != null;\n    }\n\n    public URL getExecutableArkBizJar() {\n        return executableArkBizJar;\n    }\n\n    public LaunchCommand setExecutableArkBizJar(URL executableArkBizJar) {\n        this.executableArkBizJar = executableArkBizJar;\n        return this;\n    }\n\n    public URL[] getClasspath() {\n        return classpath;\n    }\n\n    public LaunchCommand setClasspath(URL[] classpath) {\n        this.classpath = classpath;\n        return this;\n    }\n\n    public String getEntryMethodName() {\n        return entryMethodName;\n    }\n\n    public LaunchCommand setEntryMethodName(String entryMethodName) {\n        this.entryMethodName = entryMethodName;\n        return this;\n    }\n\n    public String getEntryClassName() {\n        return entryClassName;\n    }\n\n    public LaunchCommand setEntryClassName(String entryClassName) {\n        this.entryClassName = entryClassName;\n        return this;\n    }\n\n    public String[] getLaunchArgs() {\n        return launchArgs;\n    }\n\n    public LaunchCommand setLaunchArgs(String[] launchArgs) {\n        this.launchArgs = launchArgs;\n        return this;\n    }\n\n    public String[] getProfiles() {\n        if (profiles != null) {\n            return profiles;\n        }\n        String profileVMArgs = System.getProperty(VM_PROFILE);\n        return profileVMArgs == null ? new String[] { DEFAULT_PROFILE } : profileVMArgs\n            .split(PROFILE_SPLIT);\n    }\n\n    public LaunchCommand setProfiles(String[] profiles) {\n        this.profiles = profiles;\n        return this;\n    }\n\n    public static LaunchCommand parse(String[] args) throws MalformedURLException {\n        LaunchCommand launchCommand = new LaunchCommand();\n\n        String arkJarPrefix = String.format(\"%s%s=\", ARK_CONTAINER_ARGUMENTS_MARK,\n            FAT_JAR_ARGUMENT_KEY);\n        String arkClasspathPrefix = String.format(\"%s%s=\", ARK_CONTAINER_ARGUMENTS_MARK,\n            CLASSPATH_ARGUMENT_KEY);\n        String entryClassNamePrefix = String.format(\"%s%s=\", ARK_BIZ_ARGUMENTS_MARK,\n            ENTRY_CLASS_NAME_ARGUMENT_KEY);\n        String entryMethodNamePrefix = String.format(\"%s%s=\", ARK_BIZ_ARGUMENTS_MARK,\n            ENTRY_METHOD_NAME_ARGUMENT_KEY);\n        String arkConfigProfilePrefix = String.format(\"%s%s=\", ARK_CONTAINER_ARGUMENTS_MARK,\n            PROFILE);\n\n        List<String> arguments = new ArrayList<>();\n        for (String arg : args) {\n            arg = arg.trim();\n            if (arg.startsWith(arkJarPrefix)) {\n                String fatJarUrl = arg.substring(arkJarPrefix.length());\n                launchCommand.setExecutableArkBizJar(new URL(fatJarUrl));\n            } else if (arg.startsWith(entryClassNamePrefix)) {\n                String entryClassName = arg.substring(entryClassNamePrefix.length());\n                launchCommand.setEntryClassName(entryClassName);\n            } else if (arg.startsWith(entryMethodNamePrefix)) {\n                String entryMethodName = arg.substring(entryMethodNamePrefix.length());\n                launchCommand.setEntryMethodName(entryMethodName);\n            } else if (arg.startsWith(arkClasspathPrefix)) {\n                String classpath = arg.substring(arkClasspathPrefix.length());\n                List<URL> urlList = new ArrayList<>();\n                for (String url : classpath.split(CLASSPATH_SPLIT)) {\n                    if (url.isEmpty()) {\n                        continue;\n                    }\n                    urlList.add(new URL(url));\n                }\n                launchCommand.setClasspath(urlList.toArray(new URL[urlList.size()]));\n            } else if (arg.startsWith(arkConfigProfilePrefix)) {\n                String profile = arg.substring(arkConfigProfilePrefix.length());\n                launchCommand.setProfiles(profile.split(PROFILE_SPLIT));\n            } else {\n                // -A and -B argument would not passed into biz main method.\n                arguments.add(arg);\n            }\n        }\n        return launchCommand.setLaunchArgs(arguments.toArray(new String[] {}));\n    }\n\n    public static String toString(String[] args) {\n        StringBuilder sb = new StringBuilder();\n        for (String arg : args) {\n            sb.append(arg);\n        }\n        return sb.toString();\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/command/Command.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.command;\n\n/**\n * @author qilong.zql\n * @since 0.5.0\n */\npublic interface Command {\n\n    /**\n     * Get Command Prefix Marker.\n     * @return\n     */\n    String getCommandMarker();\n\n    /**\n     * Get Command Help message, usually command schema.\n     * @return\n     */\n    String getCommandHelp();\n\n    /**\n     * Process Command\n     * @return\n     * @throws Throwable\n     */\n    String process() throws Throwable;\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/constant/Constants.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.constant;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class Constants {\n    /**\n     * String Constants\n     */\n    public final static String       SPACE_SPLIT                                   = \"\\\\s+\";\n    public final static String       STRING_COLON                                  = \":\";\n    public final static String       STRING_SEMICOLON                              = \";\";\n    public final static String       TELNET_STRING_END                             = new String(\n                                                                                       new byte[] {\n            (byte) 13, (byte) 10                                                      });\n    public final static String       COMMA_SPLIT                                   = \",\";\n    public final static String       EMPTY_STR                                     = \"\";\n    public final static String       AMPERSAND_SPLIT                               = \"&\";\n    public final static String       EQUAL_SPLIT                                   = \"=\";\n    public final static String       QUESTION_MARK_SPLIT                           = \"?\";\n    public final static String       ROOT_WEB_CONTEXT_PATH                         = \"/\";\n\n    /**\n     * ark conf\n     */\n    public final static String       CONF_BASE_DIR                                 = \"conf/\";\n    public final static String       ARK_CONF_BASE_DIR                             = \"conf/ark\";\n    public final static String       ARK_CONF_FILE                                 = \"bootstrap.properties\";\n    public final static String       ARK_CONF_FILE_FORMAT                          = \"bootstrap-%s.properties\";\n\n    public final static String       ARK_CONF_YAML_FILE                            = \"bootstrap.yml\";\n\n    public final static String       PLUGIN_EXTENSION_FORMAT                       = \"PLUGIN-EXPORT[%s]\";\n\n    public final static String       DEFAULT_PROFILE                               = EMPTY_STR;\n\n    public final static String       LOCAL_HOST                                    = \"localhost\";\n\n    /**\n     * ark classloader cache conf\n     */\n    public final static String       ARK_CLASSLOADER_CACHE_CLASS_SIZE_INITIAL      = \"ark.classloader.cache.class.size.initial\";\n    public final static String       ARK_CLASSLOADER_CACHE_CLASS_SIZE_MAX          = \"ark.classloader.cache.class.size.max\";\n    public final static String       ARK_CLASSLOADER_CACHE_CONCURRENCY_LEVEL       = \"ark.classloader.cache.concurrencylevel\";\n    /**\n     * plugin conf, multi value is split by comma.\n     */\n    public final static String       PLUGIN_ACTIVE_INCLUDE                         = \"ark.plugin.active.include\";\n    public final static String       PLUGIN_ACTIVE_EXCLUDE                         = \"ark.plugin.active.exclude\";\n\n    /**\n     * biz conf, multi value is split by comma.\n     */\n    public final static String       BIZ_ACTIVE_INCLUDE                            = \"ark.biz.active.include\";\n    public final static String       BIZ_ACTIVE_EXCLUDE                            = \"ark.biz.active.exclude\";\n\n    /**\n     * Archiver Marker\n     */\n    public final static String       ARK_CONTAINER_MARK_ENTRY                      = \"com/alipay/sofa/ark/container/mark\";\n\n    public final static String       ARK_PLUGIN_MARK_ENTRY                         = \"com/alipay/sofa/ark/plugin/mark\";\n\n    public final static String       ARK_BIZ_MARK_ENTRY                            = \"com/alipay/sofa/ark/biz/mark\";\n\n    /**\n     * Ark Plugin Attribute\n     */\n    public final static String       PRIORITY_ATTRIBUTE                            = \"priority\";\n    public final static String       GROUP_ID_ATTRIBUTE                            = \"groupId\";\n    public final static String       ARTIFACT_ID_ATTRIBUTE                         = \"artifactId\";\n    public final static String       PLUGIN_NAME_ATTRIBUTE                         = \"pluginName\";\n    public final static String       PLUGIN_VERSION_ATTRIBUTE                      = \"version\";\n    public final static String       ACTIVATOR_ATTRIBUTE                           = \"activator\";\n    public final static String       WEB_CONTEXT_PATH                              = \"web-context-path\";\n    public final static String       IMPORT_CLASSES_ATTRIBUTE                      = \"import-classes\";\n    public final static String       IMPORT_PACKAGES_ATTRIBUTE                     = \"import-packages\";\n\n    public final static String       EXPORT_MODE                                   = \"export-mode\";\n    public final static String       EXPORT_CLASSES_ATTRIBUTE                      = \"export-classes\";\n    public final static String       EXPORT_PACKAGES_ATTRIBUTE                     = \"export-packages\";\n\n    /**\n     * Ark Biz Attribute\n     */\n    public final static String       MAIN_CLASS_ATTRIBUTE                          = \"Main-Class\";\n    public final static String       START_CLASS_ATTRIBUTE                         = \"Start-Class\";\n    public final static String       ARK_BIZ_NAME                                  = \"Ark-Biz-Name\";\n    public final static String       ARK_BIZ_VERSION                               = \"Ark-Biz-Version\";\n    public final static String       DENY_IMPORT_CLASSES                           = \"deny-import-classes\";\n    public final static String       DENY_IMPORT_PACKAGES                          = \"deny-import-packages\";\n    public final static String       DENY_IMPORT_RESOURCES                         = \"deny-import-resources\";\n    public final static String       INJECT_PLUGIN_DEPENDENCIES                    = \"inject-plugin-dependencies\";\n    public final static String       INJECT_EXPORT_PACKAGES                        = \"inject-export-packages\";\n    public final static String       DECLARED_LIBRARIES                            = \"declared-libraries\";\n    public final static String       DEPENDENT_PLUGINS                             = \"dependent-plugins\";\n\n    public static final String       BRANCH                                        = \"commit-branch\";\n    public static final String       COMMIT_ID                                     = \"commit-id\";\n    public static final String       BUILD_USER                                    = \"build-user\";\n    public static final String       BUILD_EMAIL                                   = \"build-email\";\n    public static final String       BUILD_TIME                                    = \"build-time\";\n    public static final String       COMMIT_AUTHOR_NAME                            = \"commit-user-name\";\n    public static final String       COMMIT_AUTHOR_EMAIL                           = \"commit-user-email\";\n    public static final String       COMMIT_TIMESTAMP                              = \"commit-timestamp\";\n    public static final String       COMMIT_TIME                                   = \"commit-time\";\n    public static final String       REMOTE_ORIGIN_URL                             = \"remote-origin-url\";\n\n    public static final String       DATE_FORMAT                                   = \"yyyy-MM-dd'T'HH:mm:ssZ\";\n\n    public final static String       PACKAGE_PREFIX_MARK                           = \"*\";\n\n    public final static String       PACKAGE_PREFIX_MARK_2                         = \".*\";\n    public final static String       DEFAULT_PACKAGE                               = \".\";\n    public final static String       MANIFEST_VALUE_SPLIT                          = COMMA_SPLIT;\n    public final static String       RESOURCE_STEM_MARK                            = \"*\";\n\n    public final static String       IMPORT_RESOURCES_ATTRIBUTE                    = \"import-resources\";\n    public final static String       EXPORT_RESOURCES_ATTRIBUTE                    = \"export-resources\";\n\n    public final static String       SUREFIRE_BOOT_CLASSPATH                       = \"Class-Path\";\n    public final static String       SUREFIRE_BOOT_CLASSPATH_SPLIT                 = \" \";\n    public final static String       SUREFIRE_BOOT_JAR                             = \"surefirebooter\";\n\n    /**\n     * Telnet Server\n     */\n    public final static String       TELNET_SERVER_ENABLE                          = \"sofa.ark.telnet.server.enable\";\n    public final static String       TELNET_SERVER_SECURITY_ENABLE                 = \"sofa.ark.telnet.security.enable\";\n    public final static String       CONFIG_SERVER_ENABLE                          = \"sofa.ark.config.server.enable\";\n\n    /**\n     * 配置中心支持, 默认使用zookeeper\n     * value值为com.alipay.sofa.ark.config.ConfigTypeEnum枚举的name()\n     */\n    public final static String       CONFIG_SERVER_TYPE                            = \"sofa.ark.config.server.type\";\n    /**\n     * 使用apollo的namespace\n     */\n    public final static String       CONFIG_APOLLO_NAMESPACE                       = \"sofa-ark\";\n    /**\n     * apollo的namespace下动态命名对应的key\n     */\n    public final static String       APOLLO_MASTER_BIZ_KEY                         = \"masterBiz\";\n    public final static String       TELNET_PORT_ATTRIBUTE                         = \"sofa.ark.telnet.port\";\n    public final static int          DEFAULT_TELNET_PORT                           = 1234;\n    public final static int          DEFAULT_SELECT_PORT_SIZE                      = 100;\n    public final static String       TELNET_SERVER_WORKER_THREAD_POOL_NAME         = \"telnet-server-worker\";\n    public final static String       TELNET_SESSION_PROMPT                         = \"sofa-ark>\";\n    public final static String       TELNET_COMMAND_THREAD_POOL_NAME               = \"telnet-command\";\n\n    /**\n     * Event\n     */\n    public final static String       BIZ_EVENT_TOPIC_AFTER_INVOKE_ALL_BIZ_START    = \"AFTER-INVOKE-ALL-BIZ-START\";\n    public final static String       BIZ_EVENT_TOPIC_AFTER_INVOKE_BIZ_START        = \"AFTER-INVOKE-BIZ-START\";\n    public final static String       BIZ_EVENT_TOPIC_AFTER_BIZ_FAILED              = \"BIZ_EVENT_TOPIC_AFTER_BIZ_FAILED\";\n    public final static String       BIZ_EVENT_TOPIC_AFTER_INVOKE_BIZ_STOP         = \"AFTER-INVOKE-BIZ-STOP\";\n    public final static String       BIZ_EVENT_TOPIC_AFTER_INVOKE_BIZ_STOP_FAILED  = \"AFTER-INVOKE-BIZ-STOP-FAILED\";\n\n    public final static String       BIZ_EVENT_TOPIC_BEFORE_RECYCLE_BIZ            = \"BEFORE-RECYCLE-BIZ\";\n    public final static String       BIZ_EVENT_TOPIC_BEFORE_INVOKE_BIZ_START       = \"BEFORE-INVOKE-BIZ-START\";\n    public final static String       BIZ_EVENT_TOPIC_BEFORE_INVOKE_BIZ_STOP        = \"BEFORE-INVOKE-BIZ-STOP\";\n\n    public final static String       PLUGIN_EVENT_TOPIC_AFTER_INVOKE_PLUGIN_START  = \"AFTER-INVOKE-PLUGIN-START\";\n    public final static String       PLUGIN_EVENT_TOPIC_AFTER_INVOKE_PLUGIN_STOP   = \"AFTER-INVOKE-PLUGIN-STOP\";\n    public final static String       PLUGIN_EVENT_TOPIC_BEFORE_INVOKE_PLUGIN_START = \"BEFORE-INVOKE-PLUGIN-START\";\n    public final static String       PLUGIN_EVENT_TOPIC_BEFORE_INVOKE_PLUGIN_STOP  = \"BEFORE-INVOKE-PLUGIN-STOP\";\n\n    public final static String       BIZ_EVENT_TOPIC_BEFORE_INVOKE_BIZ_SWITCH      = \"BEFORE-INVOKE-BIZ-SWITCH\";\n    public final static String       BIZ_EVENT_TOPIC_AFTER_INVOKE_BIZ_SWITCH       = \"AFTER-INVOKE-BIZ-SWITCH\";\n\n    public final static String       ARK_EVENT_TOPIC_AFTER_FINISH_STARTUP_STAGE    = \"AFTER-FINISH-STARTUP-STAGE\";\n    public final static String       ARK_EVENT_TOPIC_AFTER_FINISH_DEPLOY_STAGE     = \"AFTER-FINISH-DEPLOY-STAGE\";\n\n    /**\n     * Environment Properties\n     */\n    public final static String       SPRING_BOOT_ENDPOINTS_JMX_ENABLED             = \"endpoints.jmx.enabled\";\n    public final static String       LOG4J_IGNORE_TCL                              = \"log4j.ignoreTCL\";\n    public final static String       RESOLVE_PARENT_CONTEXT_SERIALIZER_FACTORY     = \"hessian.parent.context.create\";\n    public final static String       EMBED_ENABLE                                  = \"sofa.ark.embed.enable\";\n    public final static String       PLUGIN_EXPORT_CLASS_ENABLE                    = \"sofa.ark.plugin.export.class.enable\";\n    public final static String       EMBED_STATIC_BIZ_ENABLE                       = \"sofa.ark.embed.static.biz.enable\";\n\n    public final static String       EMBED_STATIC_BIZ_IN_RESOURCE_ENABLE           = \"sofa.ark.embed.static.biz.in.resource.enable\";\n    public final static String       ACTIVATE_NEW_MODULE                           = \"activate.new.module\";\n    public final static String       BIZ_MAIN_CLASS                                = \"sofa.ark.biz.main.class\";\n    public final static String       PLUGIN_CLASS_ISOLATION_ENABLE                 = \"sofa.ark.plugin.class.isolation.enable\";\n    public final static String       BIZ_SPECIFY_DEPENDENT_PLUGINS_ENABLE          = \"sofa.ark.biz.specify.dependent.plugins.enable\";\n\n    /**\n     * uninstall the biz if it starts up failed\n     */\n    public final static String       AUTO_UNINSTALL_WHEN_FAILED_ENABLE             = \"sofa.ark.auto.uninstall.when.failed.enable\";\n\n    /**\n     * unpack the biz when install\n     */\n    public final static String       UNPACK_BIZ_WHEN_INSTALL                       = \"sofa.ark.unpack.biz.when.install\";\n\n    /**\n     * support multiple version biz as activated\n     */\n    public final static String       ACTIVATE_MULTI_BIZ_VERSION_ENABLE             = \"sofa.ark.activate.multi.biz.version.enable\";\n\n    /**\n     * auto remove the biz instance in BizManagerService if it stops failed\n     */\n    public final static String       REMOVE_BIZ_INSTANCE_AFTER_STOP_FAILED         = \"sofa.ark.remove.biz.instance.when.stop.failed.enable\";\n\n    /**\n     * Command Provider\n     */\n    public final static String       PLUGIN_COMMAND_UNIQUE_ID                      = \"plugin-command-provider\";\n    public final static String       BIZ_COMMAND_UNIQUE_ID                         = \"biz-command-provider\";\n\n    /**\n     * Ark SPI extension\n     */\n    public final static String       EXTENSION_FILE_DIR                            = \"META-INF/services/sofa-ark/\";\n    public final static String       PLUGIN_CLASS_LOADER_HOOK                      = \"plugin-classloader-hook\";\n    public final static String       BIZ_CLASS_LOADER_HOOK                         = \"biz-classloader-hook\";\n    public final static String       BIZ_CLASS_LOADER_HOOK_DIR                     = \"com.alipay.sofa.ark.biz.classloader.hook.dir\";\n    public final static String       BIZ_TEMP_WORK_DIR_RECYCLE_FILE_SUFFIX         = \"deleted\";\n\n    /**\n     * Multiply biz name\n     */\n    public final static String       MASTER_BIZ                                    = \"com.alipay.sofa.ark.master.biz\";\n\n    public final static String       SOFA_ARK_MODULE                               = \"SOFA-ARK/biz/\";\n\n    /**\n     * Config Server\n     */\n    public final static String       CONFIG_SERVER_ADDRESS                         = \"com.alipay.sofa.ark.config.address\";\n    public final static String       CONFIG_SERVER_ENVIRONMENT                     = \"com.alipay.sofa.ark.config.env\";\n    public final static String       CONFIG_PROTOCOL_ZOOKEEPER                     = \"zookeeper\";\n    public final static String       CONFIG_PROTOCOL_ZOOKEEPER_HEADER              = \"zookeeper://\";\n    public final static String       ZOOKEEPER_CONTEXT_SPLIT                       = \"/\";\n    public final static String       CONFIG_INSTALL_BIZ_DIR                        = \"com.alipay.sofa.ark.biz.dir\";\n    public final static String       CONFIG_INSTALL_PLUGIN_DIR                     = \"com.alipay.sofa.ark.plugin.dir\";\n    public final static String       CONFIG_BIZ_URL                                = \"bizUrl\";\n    public final static String       BIZ_EXTENSION_URLS                            = \"bizExtensionUrls\";\n\n    public final static String       CONFIG_CONNECT_TIMEOUT                        = \"com.alipay.sofa.ark.config.connect.timeout\";\n    public final static int          DEFAULT_CONFIG_CONNECT_TIMEOUT                = 20000;\n\n    /**\n     * Test ClassLoader\n     */\n    public final static String       FORCE_DELEGATE_TO_TEST_CLASSLOADER            = \"com.alipay.sofa.ark.delegate.to.testClassLoader\";\n    public final static String       FORCE_DELEGATE_TO_APP_CLASSLOADER             = \"com.alipay.sofa.ark.delegate.to.appClassLoader\";\n\n    public final static String       EXTENSION_EXCLUDES                            = \"excludes\";\n    public final static String       EXTENSION_EXCLUDES_GROUPIDS                   = \"excludeGroupIds\";\n    public final static String       EXTENSION_EXCLUDES_ARTIFACTIDS                = \"excludeArtifactIds\";\n\n    public final static String       EXTENSION_INCLUDES                            = \"includes\";\n    public final static String       EXTENSION_INCLUDES_GROUPIDS                   = \"includeGroupIds\";\n    public final static String       EXTENSION_INCLUDES_ARTIFACTIDS                = \"includeArtifactIds\";\n\n    public final static String       DECLARED_LIBRARIES_WHITELIST                  = \"declared.libraries.whitelist\";\n\n    public static final List<String> CHANNEL_QUIT                                  = new ArrayList<>();\n\n    static {\n        CHANNEL_QUIT.add(\"quit\");\n        CHANNEL_QUIT.add(\"q\");\n        CHANNEL_QUIT.add(\"exit\");\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/event/AbstractArkEvent.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.event;\n\n/**\n * @author guolei.sgl (guolei.sgl@antfin.com) 2019/11/1 10:45 AM\n * @since\n **/\npublic abstract class AbstractArkEvent<T> implements ArkEvent {\n\n    protected transient T source;\n    protected String      topic;\n    protected long        timestamp;\n\n    public AbstractArkEvent(T source) {\n        this.source = source;\n        this.timestamp = System.currentTimeMillis();\n    }\n\n    public T getSource() {\n        return source;\n    }\n\n    public void setSource(T source) {\n        this.source = source;\n    }\n\n    @Override\n    public String getTopic() {\n        return topic;\n    }\n\n    public void setTopic(String topic) {\n        this.topic = topic;\n    }\n\n    public long getTimestamp() {\n        return timestamp;\n    }\n\n    public void setTimestamp(long timestamp) {\n        this.timestamp = timestamp;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/event/AfterFinishDeployEvent.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.event;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\n\n/**\n * @author guolei.sgl (guolei.sgl@antfin.com) 2019/11/4 3:31 PM\n * @since\n **/\npublic class AfterFinishDeployEvent extends AbstractArkEvent<String> {\n\n    public AfterFinishDeployEvent() {\n        super(Constants.EMPTY_STR);\n        this.topic = Constants.ARK_EVENT_TOPIC_AFTER_FINISH_DEPLOY_STAGE;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/event/AfterFinishStartupEvent.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.event;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\n\n/**\n * @author guolei.sgl (guolei.sgl@antfin.com) 2019/11/4 3:31 PM\n * @since\n **/\npublic class AfterFinishStartupEvent extends AbstractArkEvent<String> {\n\n    public AfterFinishStartupEvent() {\n        super(Constants.EMPTY_STR);\n        this.topic = Constants.ARK_EVENT_TOPIC_AFTER_FINISH_STARTUP_STAGE;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/event/ArkEvent.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.event;\n\n/**\n * An Event\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic interface ArkEvent {\n    /**\n     * Returns the topic of event\n     *\n     * @return String return event topic\n     */\n    String getTopic();\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/event/biz/AfterAllBizStartupEvent.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.event.biz;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.event.AbstractArkEvent;\nimport com.alipay.sofa.ark.spi.model.Biz;\n\n/**\n * @author lvjing2 (leojames.googol@gmail.com) 2024/12/1 6:17 PM\n * @since v2.2.16\n * this only used when the base restart to recovery all the bizs which had been installed before the restart\n **/\npublic class AfterAllBizStartupEvent extends AbstractArkEvent<Biz> {\n\n    public AfterAllBizStartupEvent() {\n        super(null);\n        this.topic = Constants.BIZ_EVENT_TOPIC_AFTER_INVOKE_ALL_BIZ_START;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/event/biz/AfterBizStartupEvent.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.event.biz;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.event.AbstractArkEvent;\nimport com.alipay.sofa.ark.spi.model.Biz;\n\n/**\n * @author guolei.sgl (guolei.sgl@antfin.com) 2019/11/1 6:17 PM\n * @since\n **/\npublic class AfterBizStartupEvent extends AbstractArkEvent<Biz> {\n\n    public AfterBizStartupEvent(Biz source) {\n        super(source);\n        this.topic = Constants.BIZ_EVENT_TOPIC_AFTER_INVOKE_BIZ_START;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/event/biz/AfterBizStartupFailedEvent.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.event.biz;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.event.AbstractArkEvent;\nimport com.alipay.sofa.ark.spi.model.Biz;\n\n/**\n * @author lianglipeng.llp@alibaba-inc.com\n * @since 2.2.9\n */\npublic class AfterBizStartupFailedEvent extends AbstractArkEvent<Biz> {\n\n    private Throwable throwable;\n\n    public AfterBizStartupFailedEvent(Biz source, Throwable e) {\n        super(source);\n        this.throwable = e;\n        this.topic = Constants.BIZ_EVENT_TOPIC_AFTER_BIZ_FAILED;\n    }\n\n    public Throwable getThrowable() {\n        return throwable;\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/event/biz/AfterBizStopEvent.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.event.biz;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.event.AbstractArkEvent;\nimport com.alipay.sofa.ark.spi.model.Biz;\n\n/**\n * @author guolei.sgl (guolei.sgl@antfin.com) 2019/11/1 6:21 PM\n * @since\n **/\npublic class AfterBizStopEvent extends AbstractArkEvent<Biz> {\n\n    public AfterBizStopEvent(Biz source) {\n        super(source);\n        this.topic = Constants.BIZ_EVENT_TOPIC_AFTER_INVOKE_BIZ_STOP;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/event/biz/AfterBizStopFailedEvent.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.event.biz;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.event.AbstractArkEvent;\nimport com.alipay.sofa.ark.spi.model.Biz;\n\n/**\n * @author guolei.sgl (guolei.sgl@antfin.com) 2019/11/1 6:21 PM\n * @since\n **/\npublic class AfterBizStopFailedEvent extends AbstractArkEvent<Biz> {\n\n    private Throwable throwable;\n\n    public AfterBizStopFailedEvent(Biz source, Throwable t) {\n        super(source);\n        this.throwable = t;\n        this.topic = Constants.BIZ_EVENT_TOPIC_AFTER_INVOKE_BIZ_STOP_FAILED;\n    }\n\n    public Throwable getThrowable() {\n        return throwable;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/event/biz/AfterBizSwitchEvent.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.event.biz;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.event.AbstractArkEvent;\nimport com.alipay.sofa.ark.spi.model.Biz;\n\n/**\n * @author guolei.sgl (guolei.sgl@antfin.com) 2019/11/4 4:29 PM\n * @since\n **/\npublic class AfterBizSwitchEvent extends AbstractArkEvent<Biz> {\n\n    public AfterBizSwitchEvent(Biz source) {\n        super(source);\n        this.topic = Constants.BIZ_EVENT_TOPIC_AFTER_INVOKE_BIZ_SWITCH;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/event/biz/BeforeBizRecycleEvent.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.event.biz;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.event.AbstractArkEvent;\nimport com.alipay.sofa.ark.spi.model.Biz;\n\n/**\n * @author guolei.sgl (guolei.sgl@antfin.com) 2019/11/1 6:21 PM\n * @since\n **/\npublic class BeforeBizRecycleEvent extends AbstractArkEvent<Biz> {\n\n    public BeforeBizRecycleEvent(Biz source) {\n        super(source);\n        this.topic = Constants.BIZ_EVENT_TOPIC_BEFORE_RECYCLE_BIZ;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/event/biz/BeforeBizStartupEvent.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.event.biz;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.event.AbstractArkEvent;\nimport com.alipay.sofa.ark.spi.model.Biz;\n\n/**\n * @author guolei.sgl (guolei.sgl@antfin.com) 2019/11/1 10:56 AM\n * @since\n **/\npublic class BeforeBizStartupEvent extends AbstractArkEvent<Biz> {\n\n    public BeforeBizStartupEvent(Biz source) {\n        super(source);\n        this.topic = Constants.BIZ_EVENT_TOPIC_BEFORE_INVOKE_BIZ_START;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/event/biz/BeforeBizStopEvent.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.event.biz;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.event.AbstractArkEvent;\nimport com.alipay.sofa.ark.spi.model.Biz;\n\n/**\n * @author guolei.sgl (guolei.sgl@antfin.com) 2019/11/1 6:20 PM\n * @since\n **/\npublic class BeforeBizStopEvent extends AbstractArkEvent<Biz> {\n\n    public BeforeBizStopEvent(Biz source) {\n        super(source);\n        this.topic = Constants.BIZ_EVENT_TOPIC_BEFORE_INVOKE_BIZ_STOP;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/event/biz/BeforeBizSwitchEvent.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.event.biz;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.event.AbstractArkEvent;\nimport com.alipay.sofa.ark.spi.model.Biz;\n\n/**\n * @author guolei.sgl (guolei.sgl@antfin.com) 2019/11/4 4:28 PM\n * @since\n **/\npublic class BeforeBizSwitchEvent extends AbstractArkEvent<Biz> {\n\n    public BeforeBizSwitchEvent(Biz source) {\n        super(source);\n        this.topic = Constants.BIZ_EVENT_TOPIC_BEFORE_INVOKE_BIZ_SWITCH;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/event/plugin/AfterPluginStartupEvent.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.event.plugin;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.event.AbstractArkEvent;\nimport com.alipay.sofa.ark.spi.model.Plugin;\n\n/**\n * @author guolei.sgl (guolei.sgl@antfin.com) 2019/11/1 6:17 PM\n * @since\n **/\npublic class AfterPluginStartupEvent extends AbstractArkEvent<Plugin> {\n\n    public AfterPluginStartupEvent(Plugin source) {\n        super(source);\n        this.topic = Constants.PLUGIN_EVENT_TOPIC_AFTER_INVOKE_PLUGIN_START;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/event/plugin/AfterPluginStopEvent.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.event.plugin;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.event.AbstractArkEvent;\nimport com.alipay.sofa.ark.spi.model.Plugin;\n\n/**\n * @author guolei.sgl (guolei.sgl@antfin.com) 2019/11/1 6:21 PM\n * @since\n **/\npublic class AfterPluginStopEvent extends AbstractArkEvent<Plugin> {\n\n    public AfterPluginStopEvent(Plugin source) {\n        super(source);\n        this.topic = Constants.PLUGIN_EVENT_TOPIC_AFTER_INVOKE_PLUGIN_STOP;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/event/plugin/BeforePluginStartupEvent.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.event.plugin;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.event.AbstractArkEvent;\nimport com.alipay.sofa.ark.spi.model.Plugin;\n\n/**\n * @author guolei.sgl (guolei.sgl@antfin.com) 2019/11/1 10:56 AM\n * @since\n **/\npublic class BeforePluginStartupEvent extends AbstractArkEvent<Plugin> {\n\n    public BeforePluginStartupEvent(Plugin source) {\n        super(source);\n        this.topic = Constants.PLUGIN_EVENT_TOPIC_BEFORE_INVOKE_PLUGIN_START;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/event/plugin/BeforePluginStopEvent.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.event.plugin;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.event.AbstractArkEvent;\nimport com.alipay.sofa.ark.spi.model.Plugin;\n\n/**\n * @author guolei.sgl (guolei.sgl@antfin.com) 2019/11/1 6:20 PM\n * @since\n **/\npublic class BeforePluginStopEvent extends AbstractArkEvent<Plugin> {\n\n    public BeforePluginStopEvent(Plugin source) {\n        super(source);\n        this.topic = Constants.PLUGIN_EVENT_TOPIC_BEFORE_INVOKE_PLUGIN_STOP;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/ext/ExtResponse.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.ext;\n\npublic class ExtResponse<T> {\n\n    private boolean success;\n    private String  errorMsg;\n    private String  errorCode;\n    private T       data;\n\n    public boolean isSuccess() {\n        return success;\n    }\n\n    public void setSuccess(boolean success) {\n        this.success = success;\n    }\n\n    public String getErrorMsg() {\n        return errorMsg;\n    }\n\n    public void setErrorMsg(String errorMsg) {\n        this.errorMsg = errorMsg;\n    }\n\n    public String getErrorCode() {\n        return errorCode;\n    }\n\n    public void setErrorCode(String errorCode) {\n        this.errorCode = errorCode;\n    }\n\n    public T getData() {\n        return data;\n    }\n\n    public void setData(T data) {\n        this.data = data;\n    }\n\n    @Override\n    public String toString() {\n        return \"ExtResponse{\" + \"success=\" + success + \", errorMsg='\" + errorMsg + '\\''\n               + \", errorCode='\" + errorCode + '\\'' + \", data=\" + data + '}';\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/ext/ExtServiceProvider.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.ext;\n\npublic interface ExtServiceProvider {\n\n    ExtResponse invokeService(String action, String param);\n\n    String getType();\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/model/Biz.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.model;\n\nimport com.alipay.sofa.ark.spi.service.PriorityOrdered;\n\nimport java.net.URL;\nimport java.util.Map;\n\n/**\n * Ark Biz Model Interface\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic interface Biz extends BizInfo, PriorityOrdered {\n    /**\n     * start Biz\n     * @param args\n     * @throws Throwable\n     */\n    void start(String[] args) throws Throwable;\n\n    /**\n     * start Biz with args and envs\n     * @param args\n     * @param envs\n     * @throws Throwable\n     */\n    void start(String[] args, Map<String, String> envs) throws Throwable;\n\n    /**\n     * stop Biz\n     * @throws Throwable\n     */\n    void stop() throws Throwable;\n\n    /**\n     * check resource whether declared in this biz classLoader.\n     * @param url\n     */\n    boolean isDeclared(URL url, String resourceName);\n\n    /**\n     * check whether this biz is declared mode.\n     * declared mode means this biz can only delegate declared class and resources\n     * in the pom of this biz to other classloader like plugin or master Biz.\n     * @return\n     */\n    boolean isDeclaredMode();\n\n    /**\n     * allow to dynamic update biz name\n     * @param bizName\n     */\n    void setCustomBizName(String bizName);\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/model/BizConfig.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.model;\n\nimport java.net.URL;\nimport java.util.List;\nimport java.util.Map;\n\npublic class BizConfig {\n\n    /**\n     * Biz部署参数指定version\n     */\n    private String       specifiedVersion;\n\n    /**\n     * Biz扩展classpath\n     */\n    private URL[]        extensionUrls;\n\n    /**\n     * Biz依赖的plugins\n     */\n    private List<String> dependentPlugins;\n\n    String[]             args;\n\n    Map<String, String>  envs;\n\n    public String getSpecifiedVersion() {\n        return specifiedVersion;\n    }\n\n    public void setSpecifiedVersion(String specifiedVersion) {\n        this.specifiedVersion = specifiedVersion;\n    }\n\n    public URL[] getExtensionUrls() {\n        return extensionUrls;\n    }\n\n    public void setExtensionUrls(URL[] extensionUrls) {\n        this.extensionUrls = extensionUrls;\n    }\n\n    public List<String> getDependentPlugins() {\n        return dependentPlugins;\n    }\n\n    public void setDependentPlugins(List<String> dependentPlugins) {\n        this.dependentPlugins = dependentPlugins;\n    }\n\n    public String[] getArgs() {\n        return args;\n    }\n\n    public void setArgs(String[] args) {\n        this.args = args;\n    }\n\n    public Map<String, String> getEnvs() {\n        return envs;\n    }\n\n    public void setEnvs(Map<String, String> envs) {\n        this.envs = envs;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/model/BizInfo.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.model;\n\nimport java.net.URL;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TimeZone;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic interface BizInfo {\n    /**\n     * get Biz Name\n     * @return biz name\n     */\n    String getBizName();\n\n    /**\n     * get Biz Version\n     */\n    String getBizVersion();\n\n    /**\n     * get identity id in runtime, an unique-id of ark biz\n     * @return\n     */\n    String getIdentity();\n\n    /**\n     * get Biz Main Entry Class Name\n     * @return main class name\n     */\n    String getMainClass();\n\n    /**\n     * get Biz Class Path\n     * @return biz classpath\n     */\n    URL[] getClassPath();\n\n    /**\n    * get biz url\n    */\n    URL getBizUrl();\n\n    /**\n     * get denied imported packages config\n     * @return\n     */\n    Set<String> getDenyImportPackages();\n\n    /**\n     * get biz deny import package which is exactly matched\n     * @return\n     */\n    Set<String> getDenyImportPackageNodes();\n\n    /**\n     * get biz deny import package which is matched by prefix\n     * @return\n     */\n    Set<String> getDenyImportPackageStems();\n\n    /**\n     * get denied imported classes\n     * @return\n     */\n    Set<String> getDenyImportClasses();\n\n    /**\n     * get denied imported resources\n     * @return\n     */\n    Set<String> getDenyImportResources();\n\n    /**\n     * get denied imported resource stems by prefix\n     * @return denied imported resource stems\n     */\n    Set<String> getDenyPrefixImportResourceStems();\n\n    /**\n     * get denied imported resource stems by suffix\n     * @return denied imported resource stems\n     */\n    Set<String> getDenySuffixImportResourceStems();\n\n    /**\n     * get Biz Classloader\n     * @return biz classloader\n     */\n    ClassLoader getBizClassLoader();\n\n    /**\n     * get Biz State\n     */\n    BizState getBizState();\n\n    /**\n     * get web context path\n     */\n    String getWebContextPath();\n\n    /**\n     * get Biz attributes\n     * @return\n     */\n    Map<String, String> getAttributes();\n\n    /**\n     * get getBizStateChangeLog\n     * @since 2.2.9\n     * @return java.util.concurrent.CopyOnWriteArrayList<com.alipay.sofa.ark.spi.model.BizInfo.BizStateChangeInfo>\n     */\n    List<BizStateRecord> getBizStateRecords();\n\n    class BizStateRecord {\n        private final Date                    changeTime;\n        private final BizState                state;\n\n        private final StateChangeReason       reason;\n\n        private final String                  message;\n\n        private static final SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss.SSS\");\n\n        static {\n            sdf.setTimeZone(TimeZone.getDefault());\n        }\n\n        public BizStateRecord(Date changeTime, BizState state) {\n            this.changeTime = changeTime;\n            this.state = state;\n            this.reason = StateChangeReason.UNDEFINE;\n            this.message = \"\";\n        }\n\n        public BizStateRecord(Date changeTime, BizState state, StateChangeReason reason,\n                              String message) {\n            this.changeTime = changeTime;\n            this.state = state;\n            this.reason = reason;\n            this.message = message;\n        }\n\n        @Override\n        public String toString() {\n            String date = sdf.format(changeTime);\n            return String.format(\"%s -> %s with reason: %s and message: %s\", date, state, reason,\n                message);\n        }\n\n        public Date getChangeTime() {\n            return changeTime;\n        }\n\n        public BizState getState() {\n            return state;\n        }\n\n        public StateChangeReason getReason() {\n            return reason;\n        }\n\n        public String getMessage() {\n            return message;\n        }\n    }\n\n    enum StateChangeReason {\n        /**\n         * 模块被创建\n         */\n        CREATED(\"Created\"),\n\n        /**\n         * 模块启动成功\n         */\n        STARTED(\"Started\"),\n\n        /**\n         * 模块启动失败\n         */\n        INSTALL_FAILED(\"Install Failed\"),\n\n        /**\n         * 模块卸载失败\n         */\n        UN_INSTALL_FAILED(\"Uninstall Failed\"),\n\n        /**\n         * 模块被切换为 ACTIVATED 或 DEACTIVATED 状态\n         */\n        SWITCHED(\"Switched\"),\n\n        /**\n         * 模块正在停止\n         */\n        KILLING(\"Killing\"),\n\n        /**\n         * 模块已停止\n         */\n        STOPPED(\"Stopped\"),\n\n        /**\n         * 默认值：未定义\n         */\n        UNDEFINE(\"Undefine\");\n\n        private final String reason;\n\n        StateChangeReason(String reason) {\n            this.reason = reason;\n        }\n\n        public String getReason() {\n            return reason;\n        }\n\n        @Override\n        public String toString() {\n            return getReason();\n        }\n\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/model/BizOperation.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.model;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class BizOperation {\n    private String              bizName;\n    private String              bizVersion;\n    private OperationType       operationType;\n    private Map<String, String> parameters = new HashMap<>();\n\n    public boolean isValid() {\n        return operationType == OperationType.UNKNOWN;\n    }\n\n    public String getBizName() {\n        return bizName;\n    }\n\n    public BizOperation setBizName(String bizName) {\n        this.bizName = bizName;\n        return this;\n    }\n\n    public String getBizVersion() {\n        return bizVersion;\n    }\n\n    public BizOperation setBizVersion(String bizVersion) {\n        this.bizVersion = bizVersion;\n        return this;\n    }\n\n    public OperationType getOperationType() {\n        return operationType;\n    }\n\n    public BizOperation setOperationType(OperationType operationType) {\n        this.operationType = operationType;\n        return this;\n    }\n\n    public Map<String, String> getParameters() {\n        return parameters;\n    }\n\n    public BizOperation setParameters(Map<String, String> parameters) {\n        this.parameters = parameters;\n        return this;\n    }\n\n    public BizOperation putParameter(String key, String value) {\n        this.parameters.put(key, value);\n        return this;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (obj == this) {\n            return true;\n        }\n        if (obj == null || !(obj instanceof BizOperation)) {\n            return false;\n        }\n        BizOperation that = (BizOperation) obj;\n        if (!Objects.equals(this.getBizName(), that.getBizName())) {\n            return false;\n        }\n        if (!Objects.equals(this.getBizVersion(), that.getBizVersion())) {\n            return false;\n        }\n        if (!Objects.equals(this.getOperationType(), that.getOperationType())) {\n            return false;\n        }\n        return true;\n    }\n\n    public static BizOperation createBizOperation() {\n        return new BizOperation();\n    }\n\n    public enum OperationType {\n        INSTALL, UNINSTALL, SWITCH, CHECK, UNKNOWN\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/model/BizState.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.model;\n\n/**\n * Biz State\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic enum BizState {\n    /**\n     * not init or not start install yet\n     */\n    UNRESOLVED(\"unresolved\"),\n    /**\n     * installing\n     */\n    RESOLVED(\"resolved\"),\n\n    /**\n     * install succeed, and start serving\n     */\n    ACTIVATED(\"activated\"),\n\n    /**\n     * install succeed, but stop serving, usually caused by a new version installed\n     */\n    DEACTIVATED(\"deactivated\"),\n\n    /**\n     * install or uninstall failed.\n     */\n    BROKEN(\"broken\"),\n\n    /**\n     * uninstall succeed\n     */\n    STOPPED(\"stopped\");\n\n    private String state;\n\n    BizState(String state) {\n        this.state = state;\n    }\n\n    public String getBizState() {\n        return state;\n    }\n\n    @Override\n    public String toString() {\n        return getBizState();\n    }\n\n    public static BizState of(String state) {\n        if (UNRESOLVED.name().equalsIgnoreCase(state)) {\n            return UNRESOLVED;\n        } else if (RESOLVED.name().equalsIgnoreCase(state)) {\n            return RESOLVED;\n        } else if (ACTIVATED.name().equalsIgnoreCase(state)) {\n            return ACTIVATED;\n        } else if (DEACTIVATED.name().equalsIgnoreCase(state)) {\n            return DEACTIVATED;\n        } else if (STOPPED.name().equalsIgnoreCase(state)) {\n            return STOPPED;\n        } else {\n            return BROKEN;\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/model/Plugin.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.model;\n\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.service.PriorityOrdered;\n\nimport java.net.URL;\nimport java.util.Set;\n\n/**\n * Ark Plugin Module Interface\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic interface Plugin extends PriorityOrdered {\n    /**\n     * get Plugin Name\n     * @return plugin name\n     */\n    String getPluginName();\n\n    /**\n     * get Plugin GroupId\n     * @return plugin groupId\n     */\n    String getGroupId();\n\n    /**\n     * get Plugin ArtifactId\n     * @return plugin artifactId\n     */\n    String getArtifactId();\n\n    /**\n     * get Plugin Version\n     * @return version\n     */\n    String getVersion();\n\n    /**\n     * get Plugin Priority, mainly used in following 3 scenarios:\n     * 1. Plugin start up order\n     * 2. Plugin export class load priority\n     * 3. Plugin Service\n     * priority is higher as the number is smaller\n     * @return plugin priority\n     */\n    int getPriority();\n\n    /**\n     * get Plugin Activator\n     * @return plugin activator\n     */\n    String getPluginActivator();\n\n    /**\n     * get Plugin Class Path\n     * @return plugin class path\n     */\n    URL[] getClassPath();\n\n    /**\n     * get Plugin ClassLoader\n     * @return plugin classLoader\n     */\n    ClassLoader getPluginClassLoader();\n\n    /**\n     * get Plugin Runtime Context\n     * @return plugin context\n     */\n    PluginContext getPluginContext();\n\n    /**\n     * get Plugin Export Mode\n     * default exportMode = classLoader, means export class to load in this plugin classLoader\n     * exportMode = override, means export class to file only, and usually will be reload in another classLoader\n     * @return\n     */\n    String getExportMode();\n\n    /**\n     * get Plugin Export Packages Config\n     * @return plugin export packages\n     */\n    Set<String> getExportPackages();\n\n    /**\n     * get plugin export package which is exactly matched\n     * @return\n     */\n    Set<String> getExportPackageNodes();\n\n    /**\n     * get plugin export package which is matched by prefix\n     * @return\n     */\n    Set<String> getExportPackageStems();\n\n    /**\n     * get plugin Export Classes\n     * @return plugin export classes\n     */\n    Set<String> getExportClasses();\n\n    /**\n     * get Plugin Import Packages Config\n     * @return plugin import packages\n     */\n    Set<String> getImportPackages();\n\n    /**\n     * get plugin import package which is exactly matched\n     * @return\n     */\n    Set<String> getImportPackageNodes();\n\n    /**\n     * get plugin import package which is matched by prefix\n     * @return\n     */\n    Set<String> getImportPackageStems();\n\n    /**\n     * get Plugin Import Classes\n     * @return plugin import classes\n     */\n    Set<String> getImportClasses();\n\n    /**\n     * get Plugin Import Resources\n     * @return plugin import resources\n     */\n    Set<String> getImportResources();\n\n    /**\n     * get Plugin Import Resources matched by prefix\n     * @return plugin Import Resources matched by prefix\n     */\n    Set<String> getImportPrefixResourceStems();\n\n    /**\n     * get Plugin Import Resources matched by suffix\n     * @return plugin Import Resources matched by suffix\n     */\n    Set<String> getImportSuffixResourceStems();\n\n    /**\n     * get Plugin Export Resources\n     * @return get plugin export resources\n     */\n    Set<String> getExportResources();\n\n    /**\n     * get plugin export resources matched by prefix\n     * @return plugin export resources matched by prefix\n     */\n    Set<String> getExportPrefixResourceStems();\n\n    /**\n     * get plugin export resources matched by suffix\n     * @return get plugin export resources matched by suffix\n     */\n    Set<String> getExportSuffixResourceStems();\n\n    /**\n     * get Plugin Archive URL\n     * @return plugin archive url\n     */\n    URL getPluginURL();\n\n    /**\n     * start Plugin\n     * @throws ArkRuntimeException\n     */\n    void start() throws ArkRuntimeException;\n\n    /**\n     * stop Plugin\n     * @throws ArkRuntimeException\n     */\n    void stop() throws ArkRuntimeException;\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/model/PluginConfig.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.model;\n\nimport java.net.URL;\n\npublic class PluginConfig {\n\n    /**\n     * Plugin部署参数指定name\n     */\n    private String specifiedName;\n\n    /**\n     * Plugin部署参数指定version\n     */\n    private String specifiedVersion;\n\n    /**\n     * Plugin扩展classpath\n     */\n    private URL[]  extensionUrls;\n\n    public String getSpecifiedName() {\n        return specifiedName;\n    }\n\n    public void setSpecifiedName(String specifiedName) {\n        this.specifiedName = specifiedName;\n    }\n\n    public String getSpecifiedVersion() {\n        return specifiedVersion;\n    }\n\n    public void setSpecifiedVersion(String specifiedVersion) {\n        this.specifiedVersion = specifiedVersion;\n    }\n\n    public URL[] getExtensionUrls() {\n        return extensionUrls;\n    }\n\n    public void setExtensionUrls(URL[] extensionUrls) {\n        this.extensionUrls = extensionUrls;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/model/PluginContext.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.model;\n\nimport com.alipay.sofa.ark.spi.registry.ServiceFilter;\nimport com.alipay.sofa.ark.spi.registry.ServiceReference;\n\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * SOFAArk Plugin Runtime Context\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic interface PluginContext {\n\n    /**\n     * get Plugin\n     * @return plugin\n     */\n    Plugin getPlugin();\n\n    /**\n     * get Plugin by Plugin Name\n     * @param pluginName plugin name\n     * @return plugin\n     */\n    Plugin getPlugin(String pluginName);\n\n    /**\n     * get All Plugin Names\n     * @return\n     */\n    Set<String> getPluginNames();\n\n    /**\n     * get Plugin ClassLoader\n     * @return plugin classloader\n     */\n    ClassLoader getClassLoader();\n\n    /**\n     * Publish Plugin Service\n     * @param ifClass service interface\n     * @param implObject service implement object\n     * @param <T>\n     * @return\n     */\n    <T> ServiceReference<T> publishService(Class<T> ifClass, T implObject);\n\n    /**\n     * Publish Plugin Service\n     * @param ifClass service interface\n     * @param implObject service implement object\n     * @param uniqueId service implementation id\n     * @param <T>\n     * @return\n     */\n    <T> ServiceReference<T> publishService(Class<T> ifClass, T implObject, String uniqueId);\n\n    /**\n     * Get Service publish by plugin, when there are multiple services, return the highest priority plugin service\n     * @param ifClass service interface\n     * @param <T>\n     * @return service reference\n     */\n    <T> ServiceReference<T> referenceService(Class<T> ifClass);\n\n    /**\n     * Get Service publish  by one specific plugin\n     * @param ifClass service interface\n     * @param <T>\n     * @param uniqueId service implementation\n     * @return service reference\n     */\n    <T> ServiceReference<T> referenceService(Class<T> ifClass, String uniqueId);\n\n    /**\n     * Get Service List publish by plugin\n     * @param serviceFilter service filter\n     * @return\n     */\n    List<ServiceReference> referenceServices(ServiceFilter serviceFilter);\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/model/PluginOperation.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.model;\n\nimport java.io.File;\nimport java.util.List;\n\npublic class PluginOperation {\n\n    private String        pluginName;\n\n    private String        pluginVersion;\n\n    private OperationType operationType;\n\n    private String        url;\n\n    private File          localFile;\n\n    private List<String>  extensionLibs;\n\n    public String getPluginName() {\n        return pluginName;\n    }\n\n    public void setPluginName(String pluginName) {\n        this.pluginName = pluginName;\n    }\n\n    public String getPluginVersion() {\n        return pluginVersion;\n    }\n\n    public void setPluginVersion(String pluginVersion) {\n        this.pluginVersion = pluginVersion;\n    }\n\n    public OperationType getOperationType() {\n        return operationType;\n    }\n\n    public void setOperationType(OperationType operationType) {\n        this.operationType = operationType;\n    }\n\n    public String getUrl() {\n        return url;\n    }\n\n    public void setUrl(String url) {\n        this.url = url;\n    }\n\n    public File getLocalFile() {\n        return localFile;\n    }\n\n    public void setLocalFile(File localFile) {\n        this.localFile = localFile;\n    }\n\n    public List<String> getExtensionLibs() {\n        return extensionLibs;\n    }\n\n    public void setExtensionLibs(List<String> extensionLibs) {\n        this.extensionLibs = extensionLibs;\n    }\n\n    public enum OperationType {\n        INSTALL, UNINSTALL\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/pipeline/Pipeline.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.pipeline;\n\n/**\n * Pipeline Interface\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic interface Pipeline extends PipelineStage {\n\n    /**\n     * Add pipeline stage in pipeline\n     * @param pipelineStage pipeline stage\n     * @return pipeline\n     */\n    Pipeline addPipelineStage(PipelineStage pipelineStage);\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/pipeline/PipelineContext.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.pipeline;\n\nimport com.alipay.sofa.ark.spi.archive.ExecutableArchive;\nimport com.alipay.sofa.ark.spi.argument.LaunchCommand;\n\n/**\n * Pipeline Context\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class PipelineContext {\n\n    private ExecutableArchive executableArchive;\n\n    private LaunchCommand     launchCommand;\n\n    public ExecutableArchive getExecutableArchive() {\n        return executableArchive;\n    }\n\n    public void setExecutableArchive(ExecutableArchive executableArchive) {\n        this.executableArchive = executableArchive;\n    }\n\n    public LaunchCommand getLaunchCommand() {\n        return launchCommand;\n    }\n\n    public void setLaunchCommand(LaunchCommand launchCommand) {\n        this.launchCommand = launchCommand;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/pipeline/PipelineStage.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.pipeline;\n\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\n\n/**\n * Pipeline stage interface\n * \n * @author ruoshan\n * @since 0.1.0\n */\npublic interface PipelineStage {\n\n    /**\n     * Process current pipeline stage\n     * @param pipelineContext pipeline context\n     * @throws ArkRuntimeException\n     */\n    void process(PipelineContext pipelineContext) throws ArkRuntimeException;\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/registry/ServiceFilter.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.registry;\n\n/**\n * Service Filter Interface\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic interface ServiceFilter<T> {\n\n    /**\n     * Filter ServiceReference\n     *\n     * @param serviceReference whether the given serviceReference\n     * match {@code ServiceFilter}\n     * @return true if match; false if not match\n     */\n    boolean match(ServiceReference serviceReference);\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/registry/ServiceMetadata.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.registry;\n\n/**\n * Service Metadata which contains Service metadata information\n * Service is unique by service anme and plugin name\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic interface ServiceMetadata {\n\n    /**\n     * get service id, different service implementation of same interface can be\n     * recognised by uniqueId\n     *\n     * @return service name\n     */\n    String getUniqueId();\n\n    /**\n     * get Service Interface Class\n     * @return interface class\n     */\n    Class<?> getInterfaceClass();\n\n    /**\n     * get ServiceProvider\n     * @return\n     */\n    ServiceProvider getServiceProvider();\n\n    /**\n     * Service name, generally speaking, it's combined by {@link ServiceMetadata#getUniqueId()} and\n     * {@link ServiceMetadata#getInterfaceClass}\n     *\n     * @return\n     */\n    String getServiceName();\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/registry/ServiceProvider.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.registry;\n\nimport com.alipay.sofa.ark.spi.service.PriorityOrdered;\n\n/**\n * Service Provider Interface\n * @author ruoshan\n * @since 0.1.0\n */\npublic interface ServiceProvider extends PriorityOrdered {\n\n    /**\n     * Get Service Provider Type, see {@link ServiceProviderType}\n     * @return service provider type\n     */\n    ServiceProviderType getServiceProviderType();\n\n    /**\n     * Get Service Provider Description\n     * @return service provider Description\n     */\n    String getServiceProviderDesc();\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/registry/ServiceProviderType.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.registry;\n\n/**\n * Define Service Provider Type\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic enum ServiceProviderType {\n\n    ARK_PLUGIN(\"Ark plugin\"), ARK_CONTAINER(\"Ark Container\"), ARK_MASTER_BIZ(\"Ark Master Biz\");\n\n    private String desc;\n\n    ServiceProviderType(String desc) {\n        this.desc = desc;\n    }\n\n    public String getDesc() {\n        return desc;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/registry/ServiceReference.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.registry;\n\nimport com.alipay.sofa.ark.spi.service.PriorityOrdered;\n\n/**\n * Service Reference which maintain Service and Service Metadata\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic interface ServiceReference<T> extends PriorityOrdered {\n\n    /**\n     * get Service Object\n     * @return service\n     */\n    T getService();\n\n    /**\n     * get Service Metadata\n     * @return\n     */\n    ServiceMetadata getServiceMetadata();\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/replay/Replay.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.replay;\n\n/**\n * @author qilong.zql 17/11/10-11:52\n * @author guolei.sgl 19/7/21 16:48\n */\npublic interface Replay {\n    Object invoke();\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/replay/ReplayContext.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.replay;\n\nimport java.util.Stack;\n\n/**\n * @author qilong.zql 17/11/10-12:15\n * @author guolei.sgl 19/7/21 16:48\n */\npublic class ReplayContext {\n\n    private static final ThreadLocal<Stack<String>> moduleVersions = new ThreadLocal<Stack<String>>() {\n                                                                       @Override\n                                                                       protected Stack<String> initialValue() {\n                                                                           return new Stack<>();\n                                                                       }\n                                                                   };\n\n    public static final String                      PLACEHOLDER    = \"__call_placeholder__\";\n\n    public static void set(String version) {\n        moduleVersions.get().push(version);\n    }\n\n    public static void unset() {\n        moduleVersions.get().pop();\n    }\n\n    public static String get() {\n        if (moduleVersions.get().size() == 0) {\n            return null;\n        }\n        return moduleVersions.get().peek();\n    }\n\n    public static void setPlaceHolder() {\n        // PLACEHOLDER needs to be placed when the link is currently invoked at the version number\n        if (moduleVersions.get().size() != 0) {\n            moduleVersions.get().push(PLACEHOLDER);\n        }\n    }\n\n    public static void clearPlaceHolder() {\n        if (PLACEHOLDER.equals(get())) {\n            moduleVersions.get().pop();\n        }\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/ArkInject.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service;\n\nimport java.lang.annotation.ElementType;\n\n/**\n * Any plugin can publish a service, in this service,\n *\n * @author qilong.zql\n * @since 0.4.0\n */\n@java.lang.annotation.Target(ElementType.FIELD)\n@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)\n@java.lang.annotation.Documented\npublic @interface ArkInject {\n    /**\n     * ark service interface\n     * @return\n     */\n    Class<?> interfaceType() default void.class;\n\n    /**\n     * ark service uniqueId\n     *\n     * @return return reference unique-id\n     */\n    String uniqueId() default \"\";\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/ArkService.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service;\n\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\n\n/**\n * Ark Service lifecycle, when a service need init/dispose action, it should implement this interface and register by guice\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic interface ArkService extends PriorityOrdered {\n\n    /**\n     * Ark Service init\n     * @throws ArkRuntimeException\n     */\n    void init() throws ArkRuntimeException;\n\n    /**\n     * Ark Service dispose\n     * @throws ArkRuntimeException\n     */\n    void dispose() throws ArkRuntimeException;\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/PluginActivator.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service;\n\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.model.PluginContext;\n\n/**\n * Plugin Activator which defines Plugin Entry\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic interface PluginActivator {\n\n    /**\n     * Start Plugin\n     * @param context plugin context\n     * @throws ArkRuntimeException\n     */\n    void start(PluginContext context);\n\n    /**\n     * Stop Plugin\n     * @param context\n     * @throws ArkRuntimeException\n     */\n    void stop(PluginContext context);\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/PriorityOrdered.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service;\n\n/**\n * {@code PriorityOrdered} is an interface that can be implemented by objects that\n * should be ordered.\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic interface PriorityOrdered {\n\n    /**\n     * Useful constant for the highest precedence value.\n     * @see java.lang.Integer#MIN_VALUE\n     */\n    int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;\n\n    /**\n     * Useful constant for the lowest precedence value.\n     * @see java.lang.Integer#MAX_VALUE\n     */\n    int LOWEST_PRECEDENCE  = Integer.MAX_VALUE;\n\n    /**\n     * Default priority\n     */\n    int DEFAULT_PRECEDENCE = 100;\n\n    /**\n     * Get the order value of this object. Higher values are interpreted as lower\n     * priority. As a consequence, the object with the lowest value has the highest\n     * priority.\n     * @return\n     */\n    int getPriority();\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/biz/AddBizToStaticDeployHook.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service.biz;\n\nimport com.alipay.sofa.ark.spi.archive.BizArchive;\nimport com.alipay.sofa.ark.spi.service.extension.Extensible;\n\nimport java.util.List;\n\n/**\n * @author lianglipeng.llp@alibaba-inc.com\n * @version $Id: BeforeStaticDeployBizHook.java, v 0.1 2024年06月27日 01:57 立蓬 Exp $\n */\n@Extensible\npublic interface AddBizToStaticDeployHook {\n\n    List<BizArchive> getStaticBizToAdd() throws Exception;\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/biz/BizDeployService.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service.biz;\n\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.service.ArkService;\n\n/**\n * Service to deploy Biz\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic interface BizDeployService extends ArkService {\n\n    /**\n     * Deploy all ark biz\n     * @param args biz startup arguments\n     * @throws ArkRuntimeException throw exception when meets error\n     */\n    void deploy(String[] args) throws ArkRuntimeException;\n\n    /**\n     * Un-deploy all ark ark biz\n     * @throws ArkRuntimeException\n     */\n    void unDeploy() throws ArkRuntimeException;\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/biz/BizDeployer.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service.biz;\n\n/**\n * BizDeployer to deploy Biz\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic interface BizDeployer {\n    /**\n     * Initialize biz deployer\n     *\n     * @param args command line arguments\n     */\n    void init(String[] args);\n\n    /**\n     * Start to deploy biz\n     */\n    void deploy();\n\n    /**\n     * un-deploy biz, whose resources and service would be unloaded.\n     */\n    void unDeploy();\n\n    /**\n     * Get description of biz deployer\n     *\n     * @return description\n     */\n    String getDesc();\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/biz/BizFactoryService.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service.biz;\n\nimport com.alipay.sofa.ark.spi.archive.BizArchive;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.BizConfig;\nimport com.alipay.sofa.ark.spi.model.BizOperation;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\n\n/**\n * Create Biz according to {@link File} and {@link BizArchive}\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic interface BizFactoryService {\n\n    /**\n     * @param bizArchive\n     * @return\n     * @throws IOException\n     */\n    Biz createBiz(BizArchive bizArchive) throws IOException;\n\n    /**\n     * Create Biz Model according to {@link BizArchive}\n     *\n     * @param bizArchive the {@link BizArchive} model\n     * @return Biz\n     * @throws IOException throw io exception when {@link BizArchive} is invalid.\n     */\n    Biz createBiz(BizArchive bizArchive, URL[] extensionUrls) throws IOException;\n\n    /**\n     * Create Biz Model according to {@link File}\n     *\n     * @param file the ark biz file\n     * @return Biz\n     * @throws IOException throw io exception when {@link File} is invalid.\n     */\n    Biz createBiz(File file) throws IOException;\n\n    /**\n     * @param file\n     * @param extensionUrls\n     * @return\n     * @throws IOException\n     */\n    Biz createBiz(File file, URL[] extensionUrls) throws IOException;\n\n    /**\n     * @param bizOperation\n     * @param file\n     * @return\n     */\n    Biz createBiz(BizOperation bizOperation, File file) throws IOException;\n\n    /**\n     * @param file\n     * @param bizConfig\n     * @return\n     * @throws IOException\n     */\n    Biz createBiz(File file, BizConfig bizConfig) throws IOException;\n\n    /**\n     * @param bizArchive\n     * @param bizConfig\n     * @return\n     * @throws IOException\n     */\n    Biz createBiz(BizArchive bizArchive, BizConfig bizConfig) throws IOException;\n\n    /**\n     * Create Biz Model according to master biz\n     * @return\n     */\n    Biz createEmbedMasterBiz(ClassLoader masterClassLoader);\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/biz/BizManagerService.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service.biz;\n\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.BizState;\n\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.locks.ReentrantLock;\n\n/**\n * Service to manage biz\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic interface BizManagerService {\n\n    /**\n     * Register Biz\n     *\n     * @param biz\n     * @return\n     */\n    boolean registerBiz(Biz biz);\n\n    /**\n     * Un-Register Biz, it requires the biz state must be {@link BizState#ACTIVATED}\n     * or {@link BizState#DEACTIVATED} or {@link BizState#BROKEN}\n     * @param bizName Biz Name\n     * @param bizVersion Biz Version\n     * @return Biz\n     */\n    Biz unRegisterBiz(String bizName, String bizVersion);\n\n    /**\n     * Un-Register Biz in strict mode, it ignores the biz state, generally invoked when install biz failed.\n     * @param bizName Biz Name\n     * @param bizVersion Biz Version\n     * @return Biz\n     */\n    Biz unRegisterBizStrictly(String bizName, String bizVersion);\n\n    /**\n     * Get Biz List by name\n     *\n     * @param bizName\n     * @return\n     */\n    List<Biz> getBiz(String bizName);\n\n    /**\n     * Get Biz determined by bizName and BizVersion\n     *\n     * @param bizName Biz Name\n     * @param bizVersion Biz Version\n     * @return\n     */\n    Biz getBiz(String bizName, String bizVersion);\n\n    /**\n     * Get Biz by identity id, an identity is usually consist of\n     * biz name and biz version.\n     *\n     * @param bizIdentity\n     * @return\n     */\n    Biz getBizByIdentity(String bizIdentity);\n\n    /**\n     * Get Biz by biz ClassLoader\n     * @param classLoader\n     * @return\n     */\n    Biz getBizByClassLoader(ClassLoader classLoader);\n\n    /**\n     * get All biz names\n     *\n     * @return\n     */\n    Set<String> getAllBizNames();\n\n    Set<String> getAllBizIdentities();\n\n    /**\n     * Get all biz in priority PriorityOrdered\n     * @return\n     */\n    List<Biz> getBizInOrder();\n\n    /**\n     * Get active biz with given biz name whose state\n     * is {@link BizState#ACTIVATED}\n     *\n     * @param bizName\n     * @return\n     */\n    Biz getActiveBiz(String bizName);\n\n    /**\n     * Check whether the biz specified with a given name and a given version\n     * is active {@link BizState#ACTIVATED}\n     *\n     * @param bizName\n     * @param bizVersion\n     * @return\n     */\n    boolean isActiveBiz(String bizName, String bizVersion);\n\n    /**\n     * Active biz with specified biz name and biz version.\n     * @param bizName\n     * @param bizVersion\n     */\n    void activeBiz(String bizName, String bizVersion);\n\n    /**\n     * Get {@link BizState} according to biz name and biz version.\n     *\n     * @param bizName\n     * @param bizVersion\n     * @return\n     */\n    BizState getBizState(String bizName, String bizVersion);\n\n    /**\n     * Get {@link BizState} according to biz identity.\n     *\n     * @param bizIdentity\n     * @return\n     */\n    BizState getBizState(String bizIdentity);\n\n    /**\n     * dynamic to instead a biz\n     * @param addingBiz\n     * @param removing\n     * @return\n     */\n    boolean removeAndAddBiz(Biz addingBiz, Biz removing);\n\n    ConcurrentHashMap<String, ConcurrentHashMap<String, Biz>> getBizRegistration();\n\n    /**\n     * get the lock for the biz with the given name\n     * @param bizName\n     * @return\n     */\n    ReentrantLock getBizLock(String bizName);\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/classloader/ClassLoaderHook.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service.classloader;\n\nimport com.alipay.sofa.ark.spi.service.extension.Extensible;\n\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.Enumeration;\n\n/**\n * implementation of ClassLoaderHook is used to customize the strategy of loading class and resources.\n *\n * @author qilong.zql\n * @since 0.6.0\n * @param <T> {@link com.alipay.sofa.ark.spi.model.Plugin} or {@link com.alipay.sofa.ark.spi.model.Biz}\n */\n@Extensible\npublic interface ClassLoaderHook<T> {\n    /**\n     * invoke this method before load class using biz classLoader or plugin classLoader. If this method\n     * returns null then normal loading process is done.  If this method returns a non-null value then the\n     * rest of the loading process is skipped and the returned value is used.\n     *\n     * @param name class name\n     * @param classLoaderService {@link ClassLoaderService}\n     * @param t plugin or biz instance where this hook is invoked\n     * @return the class found by this hook or null if normal loading process should continue\n     * @throws ClassNotFoundException to terminate the hook and throw an exception\n     */\n    Class<?> preFindClass(String name, ClassLoaderService classLoaderService, T t)\n                                                                                  throws ClassNotFoundException;\n\n    /**\n     * This method will only be called if no class was found from the normal loading process.\n     *\n     * @param name class name\n     * @param classLoaderService {@link ClassLoaderService}\n     * @param t plugin or biz instance where this hook is invoked\n     * @return the class found by this hook or null if normal loading process should continue\n     * @throws ClassNotFoundException to terminate the hook and throw an exception\n     */\n    Class<?> postFindClass(String name, ClassLoaderService classLoaderService, T t)\n                                                                                   throws ClassNotFoundException;\n\n    /**\n     * invoke this method before load resource using biz classLoader or plugin classLoader. If this method\n     * returns null then normal loading process is done.  If this method returns a non-null value then the\n     * rest of the loading process is skipped and the returned value is used.\n     *\n     * @param name resource name\n     * @param classLoaderService {@link ClassLoaderService}\n     * @param t plugin or biz instance where this hook is invoked\n     * @return the resource found by this hook or null if normal loading process should continue\n     */\n    URL preFindResource(String name, ClassLoaderService classLoaderService, T t);\n\n    /**\n     * This method will only be called if no resource was found from the normal loading process.\n     *\n     * @param name resource name\n     * @param classLoaderService {@link ClassLoaderService}\n     * @param t plugin or biz instance where this hook is invoked\n     * @return the resource found by this hook or null if normal loading process should continue\n     */\n    URL postFindResource(String name, ClassLoaderService classLoaderService, T t);\n\n    /**\n     * If this method returns null then normal loading process is done.\n     * If this method returns  a non-null value then the rest of the loading process is skipped and the returned\n     * value is used.\n     * If this method throws an <code>FileNotFoundException</code> then the loading process is terminated\n     * @param name the name of the resource to find\n     * @param classLoaderService {@link ClassLoaderService}\n     * @param t plugin or biz instance where this hook is invoked\n     * @return the resources found by this hook or empty if normal loading process should continue\n     * @throws IOException throw an exception when error occurs.\n     */\n    Enumeration<URL> preFindResources(String name, ClassLoaderService classLoaderService, T t)\n                                                                                              throws IOException;\n\n    /**\n     * This method will only be called if no resources were found from the normal loading process.\n     *\n     * @param name the name of the resource to find\n     * @param classLoaderService {@link ClassLoaderService}\n     * @param t plugin or biz instance where this hook is invoked\n     * @return the resources found by this hook or empty if normal loading process should continue\n     * @throws IOException throw an exception when error occurs.\n     */\n    Enumeration<URL> postFindResources(String name, ClassLoaderService classLoaderService, T t)\n                                                                                               throws IOException;\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/classloader/ClassLoaderService.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service.classloader;\n\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.service.ArkService;\n\nimport java.util.List;\n\n/**\n * ClassLoader Service\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic interface ClassLoaderService extends ArkService {\n\n    /**\n     * prepare plugin exported class and resource index Cache\n     */\n    void prepareExportClassAndResourceCache();\n\n    /**\n     * Whether class is sun reflect related class\n     * @param className class name\n     * @return\n     */\n    boolean isSunReflectClass(String className);\n\n    /**\n     * Whether class is ark spi class\n     * @param className class name\n     * @return\n     */\n    boolean isArkSpiClass(String className);\n\n    /**\n     * Whether class is ark api class\n     * @param className class name\n     * @return\n     */\n    boolean isArkApiClass(String className);\n\n    /**\n     * Whether class is ark log class.\n     * @param className\n     * @return\n     */\n    boolean isArkLogClass(String className);\n\n    /**\n     * Whether class is ark exception class.\n     * @param className\n     * @return\n     */\n    boolean isArkExceptionClass(String className);\n\n    /**\n     * Whether class is in import-class\n     * @param pluginName plugin name\n     * @param className class name\n     * @return\n     */\n    boolean isClassInImport(String pluginName, String className);\n\n    /**\n     * Find export mode for class className\n     * @param className\n     * @return\n     */\n    String getExportMode(String className);\n\n    /**\n     * Find classloader which export class for import class\n     * @param className class name\n     * @return\n     */\n    ClassLoader findExportClassLoader(String className);\n\n    ClassLoader findExportClassLoaderByBiz(Biz biz, String className);\n\n    Plugin findExportPlugin(String className);\n\n    /**\n     * Whether resource is in import-resources\n     * @param pluginName\n     * @param resourceName\n     * @return\n     */\n    boolean isResourceInImport(String pluginName, String resourceName);\n\n    /**\n     * Find classloaders which export resource for import resource in priority orders for import-resources\n     * @param resourceName resource name\n     * @return classloader list\n     */\n    List<ClassLoader> findExportResourceClassLoadersInOrder(String resourceName);\n\n    List<ClassLoader> findExportResourceClassLoadersInOrderByBiz(Biz biz, String resourceName);\n\n    /**\n     * Get JDK Related class classloader\n     * @return\n     */\n    ClassLoader getJDKClassLoader();\n\n    /**\n     * Get Ark Container classloader\n     * @return\n     */\n    ClassLoader getArkClassLoader();\n\n    /**\n     * Get system classloader\n     * @return\n     */\n    ClassLoader getSystemClassLoader();\n\n    /**\n     * Get java agent classloader\n     * @return\n     */\n    ClassLoader getAgentClassLoader();\n\n    /**\n     * Get Ark Biz ClassLoader\n     * @return\n     */\n    ClassLoader getBizClassLoader(String bizIdentity);\n\n    /**\n     * Get Ark Master Biz ClassLoader\n     * @return\n     */\n    ClassLoader getMasterBizClassLoader();\n\n    /**\n     * Get Ark Plugin ClassLoader\n     * @param pluginName\n     * @return\n     */\n    ClassLoader getPluginClassLoader(String pluginName);\n\n    /**\n     * Whether class is denied by biz\n     * @param bizIdentity biz identity\n     * @param className class name\n     * @return\n     */\n    boolean isDeniedImportClass(String bizIdentity, String className);\n\n    /**\n     * Whether resource is denied by biz\n     * @param bizIdentity biz identity\n     * @param resourceName resource name\n     * @return\n     */\n    boolean isDeniedImportResource(String bizIdentity, String resourceName);\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/event/EventAdminService.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service.event;\n\nimport com.alipay.sofa.ark.spi.event.ArkEvent;\n\n/**\n * @author qilong.zql\n * @since 0.4.0\n */\npublic interface EventAdminService {\n\n    /**\n     * Initiate synchronous delivery of an event. This method does not return to\n     * the caller until delivery of the event is completed.\n     *\n     * @param event The event to send to all listeners which subscribe to the\n     *        topic of the event.\n     */\n    void sendEvent(ArkEvent event);\n\n    /**\n     * Register an event handler\n     *\n     * @param eventHandler\n     */\n    void register(EventHandler eventHandler);\n\n    /**\n     * un-register an event handler\n     * @param eventHandler\n     */\n    void unRegister(EventHandler eventHandler);\n\n    /**\n     * un-register event handler whose classLoader matches the specified param.\n     * @param classLoader\n     */\n    void unRegister(ClassLoader classLoader);\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/event/EventHandler.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service.event;\n\nimport com.alipay.sofa.ark.spi.event.ArkEvent;\nimport com.alipay.sofa.ark.spi.service.PriorityOrdered;\n\n/**\n * @author qilong.zql\n * @since 0.4.0\n */\npublic interface EventHandler<E extends ArkEvent> extends PriorityOrdered {\n    /**\n     * Called by the {@link EventAdminService} service to notify the listener of an\n     * event.\n     *\n     * @param event The event that occurred.\n     */\n    void handleEvent(E event);\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/extension/ArkServiceLoader.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service.extension;\n\nimport java.util.List;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class ArkServiceLoader {\n    private static ExtensionLoaderService extensionLoaderService;\n\n    public static <T> T loadExtensionFromArkPlugin(Class<T> interfaceType, String extensionName,\n                                                   String pluginName) {\n        return extensionLoaderService.getExtensionContributorFromArkPlugin(interfaceType,\n            extensionName, pluginName);\n    }\n\n    public static <T> T loadExtensionFromArkBiz(Class<T> interfaceType, String extensionName,\n                                                String bizIdentity) {\n        return extensionLoaderService.getExtensionContributorFromArkBiz(interfaceType,\n            extensionName, bizIdentity);\n    }\n\n    public static <T> List<T> loadExtensionsFromArkBiz(Class<T> interfaceType, String bizIdentity) {\n        return extensionLoaderService\n            .getExtensionContributorsFromArkBiz(interfaceType, bizIdentity);\n    }\n\n    public static ExtensionLoaderService getExtensionLoaderService() {\n        return extensionLoaderService;\n    }\n\n    public static void setExtensionLoaderService(ExtensionLoaderService extensionLoaderService) {\n        ArkServiceLoader.extensionLoaderService = extensionLoaderService;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/extension/Extensible.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service.extension;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * annotation required on interface which is marked as extensible.\n *\n * @author qilong.zql\n * @since 0.6.0\n */\n@Target({ ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface Extensible {\n\n    /**\n     * return specify extensible file name, default value is the\n     * full name of interface.\n     */\n    String file() default \"\";\n\n    /**\n     * return whether this a singleton, with a single, shared instance\n     * returned on all calls, default value is true.\n     */\n    boolean singleton() default true;\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/extension/Extension.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service.extension;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Annotation required on the implementation of extensible interface.\n *\n * @author qilong.zql\n * @since 0.6.0\n */\n@Target({ ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface Extension {\n    /**\n     * extension name\n     */\n    String value() default \"\";\n\n    /**\n     * extension order, Higher values are interpreted as lower priority.\n     * As a consequence, the object with the lowest value has the highest\n     * priority.\n     */\n    int order() default 100;\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/extension/ExtensionClass.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service.extension;\n\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.service.PriorityOrdered;\n\nimport java.util.Objects;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class ExtensionClass<I, T> implements PriorityOrdered {\n    /**\n     * extensible interface type\n     */\n    private Class<I>   interfaceClass;\n\n    /**\n     * extension implementation type\n     */\n    private Class<I>   implementClass;\n\n    /**\n     * annotation on {@link ExtensionClass#interfaceClass}\n     */\n    private Extensible extensible;\n\n    /**\n     * annotation on {@link ExtensionClass#implementClass}\n     */\n    private Extension  extension;\n\n    /**\n     * where extension implementation is defined, ark plugin or ark biz.\n     * now it only support ark plugin.\n     */\n    private T          definedLocation;\n\n    /**\n     * if extensible interface type is singleton, return this as extension implementation.\n     */\n    private I          singleton;\n\n    public Class<I> getInterfaceClass() {\n        return interfaceClass;\n    }\n\n    public void setInterfaceClass(Class<I> interfaceClass) {\n        this.interfaceClass = interfaceClass;\n    }\n\n    public Class<I> getImplementClass() {\n        return implementClass;\n    }\n\n    public void setImplementClass(Class<I> implementClass) {\n        this.implementClass = implementClass;\n    }\n\n    public Extensible getExtensible() {\n        return extensible;\n    }\n\n    public void setExtensible(Extensible extensible) {\n        this.extensible = extensible;\n    }\n\n    public Extension getExtension() {\n        return extension;\n    }\n\n    public void setExtension(Extension extension) {\n        this.extension = extension;\n    }\n\n    public T getDefinedLocation() {\n        return definedLocation;\n    }\n\n    public void setDefinedLocation(T definedLocation) {\n        this.definedLocation = definedLocation;\n    }\n\n    public I getSingleton() {\n        if (singleton == null) {\n            synchronized (this) {\n                singleton = newInstance();\n            }\n        }\n        return singleton;\n    }\n\n    public I getObject() {\n        if (extensible.singleton()) {\n            return getSingleton();\n        } else {\n            return newInstance();\n        }\n    }\n\n    private I newInstance() {\n        try {\n            return implementClass.newInstance();\n        } catch (Throwable throwable) {\n            throw new ArkRuntimeException(String.format(\"Create %s instance error.\",\n                implementClass.getCanonicalName()), throwable);\n        }\n    }\n\n    @Override\n    public int getPriority() {\n        return extension.order();\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        return this == o;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(interfaceClass, implementClass, extensible, extension, definedLocation,\n            singleton);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/extension/ExtensionLoaderService.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service.extension;\n\nimport java.util.List;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic interface ExtensionLoaderService {\n    /**\n     * get specified extension implementation which match interfaceType and extensionName from ark plugin\n     *\n     * @param interfaceType extensible interface type\n     * @param extensionName extension name\n     * @param <T> extension implementation type\n     * @param pluginName pluginName\n     * @return\n     */\n    <T> T getExtensionContributorFromArkPlugin(Class<T> interfaceType, String extensionName,\n                                               String pluginName);\n\n    /**\n     * get specified extension implementation which match interfaceType and extensionName from ark biz\n     *\n     * @param interfaceType extensible interface type\n     * @param extensionName extension name\n     * @param <T> extension implementation type\n     * @param bizIdentity bizIdentity\n     * @return\n     */\n    <T> T getExtensionContributorFromArkBiz(Class<T> interfaceType, String extensionName,\n                                            String bizIdentity);\n\n    /**\n     * get specified extension implementation which match interfaceType and extensionName from ark biz\n     *\n     * @param interfaceType extensible interface type\n     * @param <T> extension implementation type\n     * @param bizIdentity bizIdentity\n     * @return\n     */\n    <T> List<T> getExtensionContributorsFromArkBiz(Class<T> interfaceType, String bizIdentity);\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/injection/InjectionService.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service.injection;\n\nimport com.alipay.sofa.ark.spi.registry.ServiceReference;\n\n/**\n * Process annotation {@link com.alipay.sofa.ark.spi.service.ArkInject}\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic interface InjectionService {\n    /**\n     * auto inject Ark Service\n     * @param reference\n     */\n    void inject(ServiceReference reference);\n\n    /**\n     * inject field annotated by {@literal com.alipay.sofa.ark.spi.service.ArkInject}\n     * @param object\n     */\n    void inject(Object object);\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/plugin/PluginDeployService.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service.plugin;\n\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.service.ArkService;\n\n/**\n * Service to deploy ark plugin\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic interface PluginDeployService extends ArkService {\n\n    /**\n     * Deploy all ark plugins\n     * @throws ArkRuntimeException\n     * @since 0.1.0\n     */\n    void deploy() throws ArkRuntimeException;\n\n    /**\n     * Un-deploy all ark plugins\n     * @throws ArkRuntimeException\n     */\n    void unDeploy() throws ArkRuntimeException;\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/plugin/PluginFactoryService.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service.plugin;\n\nimport com.alipay.sofa.ark.spi.archive.PluginArchive;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.model.PluginConfig;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.Set;\n\n/**\n * Create Plugin according to {@link File} and {@link PluginArchive}\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic interface PluginFactoryService {\n    /**\n     * Create Plugin Model according to {@link PluginArchive}\n     *\n     * @param pluginArchive the {@link PluginArchive} model\n     * @return Biz\n     * @throws IOException throw io exception when {@link PluginArchive} is invalid.\n     */\n    Plugin createPlugin(PluginArchive pluginArchive) throws IOException;\n\n    /**\n     * can custom plugin extensions urls\n     * @param pluginArchive\n     * @param extensions\n     * @param exportPackages\n     * @return\n     * @throws IOException\n     */\n    Plugin createPlugin(PluginArchive pluginArchive, URL[] extensions, Set<String> exportPackages)\n                                                                                                  throws IOException;\n\n    /**\n     * Create Plugin Model according to {@link File}\n     *\n     * @param file the plugin file\n     * @return Plugin\n     * @throws IOException throw io exception when {@link PluginArchive} is invalid.\n     */\n    Plugin createPlugin(File file) throws IOException;\n\n    Plugin createPlugin(File file, URL[] extensions) throws IOException;\n\n    Plugin createPlugin(File file, PluginConfig pluginConfig) throws IOException;\n\n    /**\n     * Mock Plugin Model according to master biz\n     * @return\n     */\n    Plugin createEmbedPlugin(PluginArchive pluginArchive, ClassLoader masterClassLoader)\n                                                                                        throws IOException;\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/plugin/PluginManagerService.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service.plugin;\n\nimport com.alipay.sofa.ark.spi.model.Plugin;\n\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * Service to manage ark plugin\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic interface PluginManagerService {\n\n    /**\n     * Register ark plugin\n     * @param plugin ark plugin info\n     */\n    void registerPlugin(Plugin plugin);\n\n    /**\n     * Get plugin info by plugin name\n     * @param pluginName plugin name\n     * @return\n     */\n    Plugin getPluginByName(String pluginName);\n\n    /**\n     * Get all plugin names\n     * @return\n     */\n    Set<String> getAllPluginNames();\n\n    /**\n     * Get all plugins in priority PriorityOrdered\n     * @return\n     */\n    List<Plugin> getPluginsInOrder();\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/registry/RegistryService.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service.registry;\n\nimport com.alipay.sofa.ark.spi.registry.ServiceFilter;\nimport com.alipay.sofa.ark.spi.registry.ServiceProvider;\nimport com.alipay.sofa.ark.spi.registry.ServiceReference;\n\nimport java.util.List;\n\n/**\n * Registry Service Interface\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic interface RegistryService {\n\n    /**\n     * Publish Service\n     * @param ifClass service interface\n     * @param implObject service implement object\n     * @param serviceProvider service provider\n     * @param <T>\n     * @return\n     */\n    <T> ServiceReference<T> publishService(Class<T> ifClass, T implObject,\n                                           ServiceProvider serviceProvider);\n\n    /**\n     * Publish Service\n     * @param ifClass service interface\n     * @param implObject service implement object\n     * @param uniqueId service implementation unique-id\n     * @param serviceProvider service provider\n     * @param <T>\n     * @return\n     */\n    <T> ServiceReference<T> publishService(Class<T> ifClass, T implObject, String uniqueId,\n                                           ServiceProvider serviceProvider);\n\n    /**\n     * Get Service, when there are multiple services, return the highest priority service\n     * {@link com.alipay.sofa.ark.spi.service.PriorityOrdered}\n     *\n     * @param ifClass service interface\n     * @param <T>\n     * @return service reference\n     */\n    <T> ServiceReference<T> referenceService(Class<T> ifClass);\n\n    /**\n     * Get Service, when there are multiple services, return the highest priority service\n     * {@link com.alipay.sofa.ark.spi.service.PriorityOrdered}\n     *\n     * @param ifClass service interface\n     * @param uniqueId service implementation unique-id\n     * @param <T>\n     * @return service reference\n     */\n    <T> ServiceReference<T> referenceService(Class<T> ifClass, String uniqueId);\n\n    /**\n     * Get Service List, ordered by priority.\n     * {@link com.alipay.sofa.ark.spi.service.PriorityOrdered}\n     *\n     * @param ifClass service interface\n     * @param <T>\n     * @return service reference list\n     */\n    <T> List<ServiceReference<T>> referenceServices(Class<T> ifClass);\n\n    /**\n     * Get Service List, ordered by priority.\n     * {@link com.alipay.sofa.ark.spi.service.PriorityOrdered}\n     *\n     * @param ifClass service interface\n     * @param uniqueId service unique-id\n     * @param <T>\n     * @return service reference list\n     */\n    <T> List<ServiceReference<T>> referenceServices(Class<T> ifClass, String uniqueId);\n\n    /**\n     * Get Service List, ordered by priority.\n     * {@link com.alipay.sofa.ark.spi.service.PriorityOrdered}\n     *\n     * @param serviceFilter service filter\n     * @return service reference\n     */\n    <T> List<ServiceReference<T>> referenceServices(ServiceFilter<T> serviceFilter);\n\n    /**\n     * Drive out service which match the given serviceFilter.\n     *\n     * @param serviceFilter\n     * @return return the count of deleted services\n     */\n    int unPublishServices(ServiceFilter serviceFilter);\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/session/CommandProvider.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service.session;\n\n/**\n * Answer a string (may be as many lines as you like) with help\n * texts that explain the command.\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic interface CommandProvider {\n    /**\n     * Get Command Help Message Tips\n     * @return\n     */\n    String getHelp();\n\n    /**\n     * Handler Specified Command\n     * @param command\n     * @return\n     */\n    String handleCommand(String command);\n\n    /**\n     * Validate whether command is valid\n     * @param command\n     * @return\n     */\n    boolean validate(String command);\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/service/session/TelnetServerService.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service.session;\n\nimport com.alipay.sofa.ark.spi.service.ArkService;\n\n/**\n * Providers a thread that listens on the port for telnet connections\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic interface TelnetServerService extends ArkService {\n\n    void run();\n\n    void shutdown();\n\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/web/AbstractEmbeddedServerService.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.web;\n\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\npublic abstract class AbstractEmbeddedServerService<T> implements EmbeddedServerService<T> {\n    private Map<Integer, T> servers = new ConcurrentHashMap<>();\n\n    @Override\n    public T getEmbedServer(int port) {\n        return servers.get(port);\n    }\n\n    @Override\n    public boolean putEmbedServer(int port, T container) {\n        if (container == null) {\n            return false;\n        }\n        return servers.putIfAbsent(port, container) == null;\n    }\n\n    @Override\n    public Iterator<T> iterator() {\n        return servers.values().iterator();\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/main/java/com/alipay/sofa/ark/spi/web/EmbeddedServerService.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.web;\n\n/**\n * Fetch embed tomcat container in ark\n *\n * @author qilong.zql\n * @since 0.6.0\n */\npublic interface EmbeddedServerService<T> extends Iterable<T> {\n    /**\n     * get embed tomcat with port.\n     * @return\n     */\n    T getEmbedServer(int port);\n\n    /**\n     * put embed tomcat with port.\n     * Once web container instance (e.g. Tomcat, Netty) set to this EmbeddedServerService, it is usually can not be modified!\n     * @param port server port\n     * @param container server container\n     */\n    boolean putEmbedServer(int port, T container);\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/test/java/com/alipay/sofa/ark/spi/argument/LaunchCommandTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.argument;\n\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.lang.reflect.Method;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\nimport static com.alipay.sofa.ark.spi.argument.CommandArgument.*;\nimport static com.alipay.sofa.ark.spi.argument.LaunchCommand.parse;\nimport static java.lang.Integer.valueOf;\nimport static java.lang.String.format;\nimport static java.net.URLDecoder.decode;\nimport static org.junit.Assert.*;\n\n/**\n * @author qilong.zql 18/3/9\n */\npublic class LaunchCommandTest {\n\n    public static List<String> arkCommand = new ArrayList();\n    public static int          count;\n\n    String                     classpath;\n    Method                     method;\n    URL                        fatJarUrl;\n\n    @Before\n    public void init() {\n\n        try {\n            classpath = getClasspath(Objects.requireNonNull(getURLs(this.getClass()\n                .getClassLoader())));\n            method = MainClass.class.getMethod(\"main\", String[].class);\n            fatJarUrl = this.getClass().getClassLoader().getResource(\"test 2.jar\");\n        } catch (Exception ex) {\n            throw new RuntimeException(ex);\n        }\n\n        arkCommand.add(format(\"%s%s=%s\", ARK_CONTAINER_ARGUMENTS_MARK, FAT_JAR_ARGUMENT_KEY,\n            fatJarUrl));\n        arkCommand.add(format(\"%s%s=%s\", ARK_CONTAINER_ARGUMENTS_MARK, CLASSPATH_ARGUMENT_KEY,\n            classpath));\n        arkCommand.add(format(\"%s%s=%s\", ARK_BIZ_ARGUMENTS_MARK, ENTRY_CLASS_NAME_ARGUMENT_KEY,\n            method.getDeclaringClass().getName()));\n        arkCommand.add(format(\"%s%s=%s\", ARK_BIZ_ARGUMENTS_MARK, ENTRY_METHOD_NAME_ARGUMENT_KEY,\n            method.getName()));\n        LaunchCommandTest.count = 0;\n    }\n\n    @Test\n    public void testCommandParser() {\n        try {\n            List<String> args = new ArrayList<>();\n            args.addAll(arkCommand);\n            args.add(\"p1\");\n            args.add(\"p2\");\n            LaunchCommand launchCommand = parse(args.toArray(new String[] {}));\n\n            assertTrue(launchCommand.getEntryClassName().equals(\n                method.getDeclaringClass().getName()));\n            assertTrue(launchCommand.getEntryMethodName().equals(method.getName()));\n            assertTrue(launchCommand.getExecutableArkBizJar().equals(fatJarUrl));\n            assertTrue(launchCommand.getClasspath().length == classpath.split(CLASSPATH_SPLIT).length);\n            for (URL url : launchCommand.getClasspath()) {\n                assertTrue(classpath.contains(url.toExternalForm()));\n            }\n            assertTrue(2 == launchCommand.getLaunchArgs().length);\n        } catch (Exception ex) {\n            assertNull(ex);\n        }\n    }\n\n    @Test\n    public void testEncodedURL() {\n        File file = new File(fatJarUrl.getFile());\n        assertFalse(file.exists());\n        file = new File(decode(fatJarUrl.getFile()));\n        assertTrue(file.exists());\n    }\n\n    public static class MainClass {\n\n        public static void main(String[] args) {\n            if (args.length > 0) {\n                LaunchCommandTest.count += valueOf(args[0]);\n            }\n        }\n\n    }\n\n    private String getClasspath(URL[] urls) {\n\n        StringBuilder sb = new StringBuilder();\n        for (URL url : urls) {\n            sb.append(url.toExternalForm()).append(CLASSPATH_SPLIT);\n        }\n\n        return sb.toString();\n    }\n\n    private URL[] getURLs(ClassLoader classLoader) {\n\n        // https://stackoverflow.com/questions/46519092/how-to-get-all-jars-loaded-by-a-java-application-in-java9\n        if (classLoader instanceof URLClassLoader) {\n            return ((URLClassLoader) classLoader).getURLs();\n        }\n\n        // support jdk9+\n        String classpath = System.getProperty(\"java.class.path\");\n        String[] classpathEntries = classpath.split(System.getProperty(\"path.separator\"));\n        List<URL> classpathURLs = new ArrayList<>();\n\n        for (String classpathEntry : classpathEntries) {\n            URL url = null;\n            try {\n                url = new File(classpathEntry).toURI().toURL();\n            } catch (MalformedURLException e) {\n                e.printStackTrace();\n                throw new ArkRuntimeException(\"Failed to get urls from \" + classLoader, e);\n            }\n            classpathURLs.add(url);\n        }\n\n        return classpathURLs.toArray(new URL[0]);\n    }\n\n    @Test\n    public void testOtherMethods() throws MalformedURLException {\n\n        List<String> args = new ArrayList<>();\n        args.addAll(arkCommand);\n        args.add(\"p1\");\n        args.add(\"p2\");\n        LaunchCommand launchCommand = parse(args.toArray(new String[] {}));\n        launchCommand.setProfiles(new String[] { \"1\" });\n        assertArrayEquals(new String[] { \"1\" }, launchCommand.getProfiles());\n        assertEquals(\"ab\", LaunchCommand.toString(new String[] { \"a\", \"b\" }));\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/test/java/com/alipay/sofa/ark/spi/constant/ConstantsTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.constant;\n\nimport org.junit.Test;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.*;\nimport static java.lang.Class.forName;\nimport static org.junit.Assert.assertEquals;\n\npublic class ConstantsTest {\n\n    @Test\n    public void testAllMethods() throws Exception {\n        forName(\"com.alipay.sofa.ark.spi.constant.Constants\");\n        List<String> channelQuits = new ArrayList<>();\n        channelQuits.add(\"quit\");\n        channelQuits.add(\"q\");\n        channelQuits.add(\"exit\");\n        assertEquals(channelQuits, CHANNEL_QUIT);\n        assertEquals(new String(new byte[] { (byte) 13, (byte) 10 }), TELNET_STRING_END);\n        assertEquals(\"\", DEFAULT_PROFILE);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/test/java/com/alipay/sofa/ark/spi/ext/ExtResponseTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.ext;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.springframework.beans.BeanUtils.copyProperties;\n\npublic class ExtResponseTest {\n\n    @Test\n    public void testAllMethods() {\n        copyProperties(new ExtResponse<>(), new ExtResponse<>());\n        ExtResponse<String> extResponse = new ExtResponse<>();\n        extResponse.setSuccess(true);\n        extResponse.setErrorMsg(\"myMsg\");\n        extResponse.setErrorCode(\"001\");\n        extResponse.setData(\"myData\");\n        String extResponseStr = extResponse.toString();\n        assertEquals(\"ExtResponse{\" + \"success=true, errorMsg='myMsg'\" + \", errorCode='001'\"\n                     + \", data=myData}\", extResponseStr);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/test/java/com/alipay/sofa/ark/spi/model/BizOperationTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.model;\n\nimport com.alipay.sofa.ark.spi.event.AbstractArkEvent;\nimport org.junit.Test;\n\nimport static com.alipay.sofa.ark.spi.model.BizOperation.createBizOperation;\nimport static com.alipay.sofa.ark.spi.model.BizState.*;\nimport static org.junit.Assert.*;\nimport static org.springframework.beans.BeanUtils.copyProperties;\n\npublic class BizOperationTest {\n\n    @Test\n    public void testOperationEqual() {\n\n        BizOperation op1 = createBizOperation();\n        op1.setBizName(\"biz A\").setBizVersion(\"1.0.0\")\n            .setOperationType(BizOperation.OperationType.INSTALL);\n        BizOperation op2 = createBizOperation();\n        op2.setBizName(\"biz A\").setBizVersion(\"1.0.0\")\n            .setOperationType(BizOperation.OperationType.INSTALL);\n\n        assertEquals(op1, op1);\n        assertEquals(op1, op2);\n        op2.setOperationType(BizOperation.OperationType.UNINSTALL);\n        assertNotEquals(op1, op2);\n        op2.setBizVersion(\"2.0.0\");\n        assertNotEquals(op1, op2);\n        op2.setBizName(\"biz B\");\n        assertNotEquals(op1, op2);\n\n        assertFalse(op1.equals(\"\"));\n        assertFalse(op1.equals(null));\n    }\n\n    @Test\n    public void testAbstractArkEvent() {\n        AbstractArkEvent abstractArkEvent = new AbstractArkEvent(\"\") {\n        };\n        copyProperties(abstractArkEvent, abstractArkEvent);\n    }\n\n    @Test\n    public void testBizState() {\n        assertEquals(UNRESOLVED, BizState.of(\"UNRESOLVED\"));\n        assertEquals(RESOLVED, BizState.of(\"RESOLVED\"));\n        assertEquals(ACTIVATED, BizState.of(\"ACTIVATED\"));\n        assertEquals(DEACTIVATED, BizState.of(\"DEACTIVATED\"));\n        assertEquals(BROKEN, BizState.of(\"aaa\"));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/test/java/com/alipay/sofa/ark/spi/replay/ReplayContextTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.replay;\n\nimport org.junit.Test;\n\nimport static com.alipay.sofa.ark.spi.replay.ReplayContext.*;\nimport static org.junit.Assert.assertEquals;\n\npublic class ReplayContextTest {\n\n    @Test\n    public void testAllMethods() {\n\n        assertEquals(null, get());\n\n        set(\"0.0\");\n        assertEquals(\"0.0\", get());\n\n        unset();\n        assertEquals(null, get());\n\n        setPlaceHolder();\n        assertEquals(null, get());\n\n        set(\"2.0\");\n        setPlaceHolder();\n        assertEquals(PLACEHOLDER, get());\n\n        clearPlaceHolder();\n        assertEquals(\"2.0\", get());\n\n        clearPlaceHolder();\n        assertEquals(\"2.0\", get());\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/test/java/com/alipay/sofa/ark/spi/service/extension/ExtensionClassTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.spi.service.extension;\n\nimport org.junit.Test;\n\nimport static org.springframework.beans.BeanUtils.copyProperties;\n\npublic class ExtensionClassTest {\n\n    @Test\n    public void testExtensionClassTest() {\n        ExtensionClass extensionClass = new ExtensionClass();\n        copyProperties(extensionClass, extensionClass);\n        extensionClass.equals(extensionClass);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core/spi/src/test/resources/test 2.jar",
    "content": ""
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Module Overview\n\n**Artifact ID**: `sofa-ark-archive`\n**Package**: `com.alipay.sofa.ark.loader`\n\nThis module implements archive handling for SOFAArk - parsing and loading JAR files, directories, and embedded archives.\n\n## Purpose\n\n- Parse Ark package structure (Fat Jar)\n- Load Biz and Plugin archives from JAR files or directories\n- Handle executable Ark JAR format\n\n## Key Classes\n\n### Archive Implementations\n- `ExecutableArkBizJar` - Main entry point for executable Ark JAR\n- `JarBizArchive` - Business module from JAR file\n- `JarPluginArchive` - Plugin from JAR file\n- `JarContainerArchive` - Container archive from JAR\n- `DirectoryBizArchive` - Business module from exploded directory\n- `ExplodedBizArchive` - Exploded JAR directory archive\n- `DirectoryContainerArchive` - Container from directory\n- `EmbedClassPathArchive` - Embedded classpath archive for testing\n\n### Archive Utilities\n- `archive.JarFileArchive` - Spring Boot style JAR archive\n- `archive.ExplodedArchive` - Exploded directory archive\n- `jar.*` - JAR file handling utilities\n\n## Archive Structure\n\nThe Ark Fat Jar structure follows Spring Boot executable JAR format:\n```\nark-executable.jar\n├── SOFA-ARK/\n│   ├── biz/           # Business module JARs\n│   └── plugin/        # Plugin JARs\n├── SOFA-ARK-CONTAINER/  # Container JAR\n└── BOOT-INF/          # Application classes\n```\n\n## Dependencies\n\n- `sofa-ark-spi` - Archive interfaces\n- `sofa-ark-common` - Utilities\n- `spring-boot-loader` - Spring Boot loader for JAR handling\n\n## Used By\n\n- `sofa-ark-container` - Uses archives to load Biz and Plugins\n- `ark-maven-plugin` - Creates the archive structure"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <artifactId>sofa-ark-core-impl</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n    </parent>\n\n    <artifactId>sofa-ark-archive</artifactId>\n    <name>${project.groupId}:${project.artifactId}</name>\n\n    <dependencies>\n\n        <!--SOFAArk modules-->\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-spi</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-api</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-common</artifactId>\n        </dependency>\n\n        <!--third party libraries-->\n        <dependency>\n            <groupId>commons-io</groupId>\n            <artifactId>commons-io</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-model</artifactId>\n        </dependency>\n\n        <!--test-->\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-inline</artifactId>\n            <version>${mockito.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-loader</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/bootstrap/AbstractLauncher.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.bootstrap;\n\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.loader.jar.JarFile;\nimport com.alipay.sofa.ark.spi.archive.ContainerArchive;\nimport com.alipay.sofa.ark.spi.archive.ExecutableArchive;\nimport com.alipay.sofa.ark.spi.argument.CommandArgument;\nimport com.alipay.sofa.ark.spi.constant.Constants;\n\nimport java.lang.reflect.Method;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic abstract class AbstractLauncher {\n\n    /**\n     * Launch the ark container. This method is the initial entry point when execute an fat jar.\n     * @throws Exception if the ark container fails to launch.\n     */\n    public Object launch(String[] args) throws Exception {\n        if (!isEmbedEnable()) {\n            JarFile.registerUrlProtocolHandler();\n        }\n        ClassLoader classLoader = createContainerClassLoader(getContainerArchive());\n        List<String> attachArgs = new ArrayList<>();\n        attachArgs\n            .add(String.format(\"%s%s=%s\", CommandArgument.ARK_CONTAINER_ARGUMENTS_MARK,\n                CommandArgument.FAT_JAR_ARGUMENT_KEY, getExecutableArchive().getUrl()\n                    .toExternalForm()));\n        attachArgs.addAll(Arrays.asList(args));\n        return launch(attachArgs.toArray(new String[attachArgs.size()]), getMainClass(),\n            classLoader);\n    }\n\n    /**\n     * Launch the ark container. This method is the initial entry point when execute in IDE.\n     * @throws Exception if the ark container fails to launch.\n     */\n    public Object launch(String[] args, String classpath, Method method) throws Exception {\n        if (!isEmbedEnable()) {\n            JarFile.registerUrlProtocolHandler();\n        }\n        ClassLoader classLoader = createContainerClassLoader(getContainerArchive());\n        List<String> attachArgs = new ArrayList<>();\n        attachArgs.add(String.format(\"%s%s=%s\", CommandArgument.ARK_CONTAINER_ARGUMENTS_MARK,\n            CommandArgument.CLASSPATH_ARGUMENT_KEY, classpath));\n        attachArgs.add(String.format(\"%s%s=%s\", CommandArgument.ARK_BIZ_ARGUMENTS_MARK,\n            CommandArgument.ENTRY_CLASS_NAME_ARGUMENT_KEY, method.getDeclaringClass().getName()));\n        attachArgs.add(String.format(\"%s%s=%s\", CommandArgument.ARK_BIZ_ARGUMENTS_MARK,\n            CommandArgument.ENTRY_METHOD_NAME_ARGUMENT_KEY, method.getName()));\n        attachArgs.addAll(Arrays.asList(args));\n        return launch(attachArgs.toArray(new String[attachArgs.size()]), getMainClass(),\n            classLoader);\n    }\n\n    /**\n     * Launch the ark container in {@literal TEST} run mode.\n     *\n     * @param classpath classpath of ark-biz\n     * @param testClass test class\n     * @return Object {@literal com.alipay.sofa.ark.container.ArkContainer}\n     * @throws Exception\n     */\n    public Object launch(String classpath, Class testClass) throws Exception {\n        if (!isEmbedEnable()) {\n            JarFile.registerUrlProtocolHandler();\n        }\n        ClassLoader classLoader = createContainerClassLoader(getContainerArchive());\n        List<String> attachArgs = new ArrayList<>();\n        attachArgs.add(String.format(\"%s%s=%s\", CommandArgument.ARK_CONTAINER_ARGUMENTS_MARK,\n            CommandArgument.CLASSPATH_ARGUMENT_KEY, classpath));\n        attachArgs.add(String.format(\"%s%s=%s\", CommandArgument.ARK_BIZ_ARGUMENTS_MARK,\n            CommandArgument.ENTRY_CLASS_NAME_ARGUMENT_KEY, testClass.getCanonicalName()));\n        return launch(attachArgs.toArray(new String[attachArgs.size()]), getMainClass(),\n            classLoader);\n    }\n\n    protected Object launch(String[] args, String mainClass, ClassLoader classLoader)\n                                                                                     throws Exception {\n        ClassLoader old = Thread.currentThread().getContextClassLoader();\n        try {\n            Thread.currentThread().setContextClassLoader(classLoader);\n            return createMainMethodRunner(mainClass, args).run();\n        } finally {\n            Thread.currentThread().setContextClassLoader(old);\n        }\n    }\n\n    protected MainMethodRunner createMainMethodRunner(String mainClass, String[] args) {\n        return new MainMethodRunner(mainClass, args, null);\n    }\n\n    /**\n     * Returns the executable archive\n     * @return the executable archive\n     * @throws Exception\n     */\n    protected abstract ExecutableArchive getExecutableArchive() throws Exception;\n\n    /**\n     * Returns the container archive archive\n     * @return the container archive archive\n     * @throws Exception\n     */\n    protected ContainerArchive getContainerArchive() throws Exception {\n        return getExecutableArchive().getContainerArchive();\n    }\n\n    /**\n     * Create a classloader for the ark container archive\n     * @param containerArchive the ark container archive\n     * @return the classloader load ark container\n     * @throws Exception\n     */\n    protected ClassLoader createContainerClassLoader(ContainerArchive containerArchive)\n                                                                                       throws Exception {\n        List<URL> classpath = getExecutableArchive().getConfClasspath();\n        classpath.addAll(Arrays.asList(containerArchive.getUrls()));\n        URL[] agentUrls = ClassLoaderUtils.getAgentClassPath();\n        // set parent as app class loader to ensure agent like skywalking works well\n        ClassLoader agentClassLoader = (agentUrls.length > 0 ? new AgentClassLoader(agentUrls,\n            Thread.currentThread().getContextClassLoader()) : null);\n        return createContainerClassLoader(classpath.toArray(new URL[] {}), null, agentClassLoader);\n    }\n\n    /**\n     * Create a classloader for the specified URLs.\n     * @param urls the URLs\n     * @param parent the parent\n     * @return the classloader load ark container\n     */\n    protected ClassLoader createContainerClassLoader(URL[] urls, ClassLoader parent,\n                                                     ClassLoader agentClassLoader) {\n        return isEmbedEnable() ? new ContainerClassLoader(urls, parent, this.getClass()\n            .getClassLoader(), agentClassLoader) : new ContainerClassLoader(urls, parent,\n            agentClassLoader);\n    }\n\n    private boolean isEmbedEnable() {\n        return Boolean.getBoolean(Constants.EMBED_ENABLE);\n    }\n\n    /**\n     * Returns the main class that should be launched.\n     *\n     * @return the name of the main class\n     * @throws Exception if the main class cannot be obtained.\n     */\n    protected abstract String getMainClass() throws Exception;\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/bootstrap/AgentClassLoader.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.bootstrap;\n\nimport java.net.URL;\nimport java.net.URLClassLoader;\n\n/**\n * Used to collect classpath of java agent.\n *\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class AgentClassLoader extends URLClassLoader {\n    static {\n        ClassLoader.registerAsParallelCapable();\n    }\n\n    public AgentClassLoader(URL[] urls, ClassLoader parent) {\n        super(urls, parent);\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/bootstrap/ArkLauncher.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.bootstrap;\n\nimport com.alipay.sofa.ark.spi.archive.ExecutableArchive;\n\n/**\n * {@link AbstractLauncher} for JAR based archives. This launcher assumes that dependency jars are\n * included inside a {@code /SOFA-ARK/container/lib} directory and that application fat jar\n * are included inside a {@code /SOFA-ARK/biz/} directory and that ark plugin fat jar are included\n * inside a {@code /SOFA-ARK/plugin/} directory\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class ArkLauncher extends BaseExecutableArchiveLauncher {\n\n    public final String SOFA_ARK_MAIN = \"com.alipay.sofa.ark.container.ArkContainer\";\n\n    public static void main(String[] args) throws Exception {\n        new ArkLauncher().launch(args);\n    }\n\n    public ArkLauncher() {\n    }\n\n    public ArkLauncher(ExecutableArchive executableArchive) {\n        super(executableArchive);\n    }\n\n    @Override\n    protected String getMainClass() {\n        return SOFA_ARK_MAIN;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/bootstrap/BaseExecutableArchiveLauncher.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.bootstrap;\n\nimport com.alipay.sofa.ark.common.util.FileUtils;\nimport com.alipay.sofa.ark.loader.ExecutableArkBizJar;\nimport com.alipay.sofa.ark.loader.archive.ExplodedArchive;\nimport com.alipay.sofa.ark.loader.archive.JarFileArchive;\nimport com.alipay.sofa.ark.spi.archive.ExecutableArchive;\n\nimport java.io.File;\nimport java.net.URI;\nimport java.security.CodeSource;\nimport java.security.ProtectionDomain;\n\n/**\n * Base class for executable archive {@link AbstractLauncher}s.\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic abstract class BaseExecutableArchiveLauncher extends AbstractLauncher {\n\n    private final ExecutableArchive executableArchive;\n\n    public BaseExecutableArchiveLauncher() {\n        try {\n            this.executableArchive = createArchive();\n        } catch (Exception ex) {\n            throw new IllegalStateException(ex);\n        }\n    }\n\n    protected BaseExecutableArchiveLauncher(ExecutableArchive executableArchive) {\n        this.executableArchive = executableArchive;\n    }\n\n    @Override\n    protected ExecutableArchive getExecutableArchive() {\n        return this.executableArchive;\n    }\n\n    /**\n     * Returns the executable file archive\n     * @return executable file archive\n     * @throws Exception\n     */\n    protected ExecutableArchive createArchive() throws Exception {\n        ProtectionDomain protectionDomain = getClass().getProtectionDomain();\n        CodeSource codeSource = protectionDomain.getCodeSource();\n        URI location = (codeSource == null ? null : codeSource.getLocation().toURI());\n        String path = (location == null ? null : location.getSchemeSpecificPart());\n        if (path == null) {\n            throw new IllegalStateException(\"Unable to determine code source archive\");\n        }\n        File root = FileUtils.file(path);\n        if (!root.exists()) {\n            throw new IllegalStateException(\"Unable to determine code source archive from \" + root);\n        }\n        return root.isDirectory() ? new ExecutableArkBizJar(new ExplodedArchive(root))\n            : new ExecutableArkBizJar(new JarFileArchive(root), root.toURI().toURL());\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/bootstrap/ClasspathLauncher.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.bootstrap;\n\nimport com.alipay.sofa.ark.common.util.*;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.loader.*;\nimport com.alipay.sofa.ark.loader.archive.JarFileArchive;\nimport com.alipay.sofa.ark.spi.archive.*;\nimport com.alipay.sofa.ark.spi.constant.Constants;\n\nimport java.io.*;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.util.*;\nimport java.util.jar.JarFile;\nimport java.util.jar.Manifest;\nimport java.util.zip.ZipEntry;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.ARK_CONF_BASE_DIR;\nimport static com.alipay.sofa.ark.spi.constant.Constants.SUREFIRE_BOOT_CLASSPATH;\nimport static com.alipay.sofa.ark.spi.constant.Constants.SUREFIRE_BOOT_CLASSPATH_SPLIT;\n\n/**\n * @author ruoshan\n * @since 0.1.0\n */\npublic class ClasspathLauncher extends ArkLauncher {\n\n    public ClasspathLauncher(ClassPathArchive classPathArchive) {\n        super(classPathArchive);\n    }\n\n    public static class ClassPathArchive implements ExecutableArchive {\n\n        public static final String     FILE_IN_JAR = \"!/\";\n\n        private final String           className;\n\n        private final String           methodName;\n\n        private final URL[]            urls;\n\n        protected final URLClassLoader urlClassLoader;\n\n        private File                   arkConfBaseDir;\n\n        public ClassPathArchive(String className, String methodName, URL[] urls) throws IOException {\n            AssertUtils.isFalse(StringUtils.isEmpty(className),\n                \"Entry class name must be specified.\");\n            this.className = className;\n            this.methodName = methodName;\n            this.urls = urls;\n            List<URL> classpath = getConfClasspath();\n            classpath.addAll(Arrays.asList(this.urls));\n            urlClassLoader = new URLClassLoader(classpath.toArray(new URL[] {}), null);\n        }\n\n        public List<URL> filterUrls(String resource) throws Exception {\n            List<URL> urlList = new ArrayList<>();\n\n            Enumeration<URL> enumeration = urlClassLoader.findResources(resource);\n            while (enumeration.hasMoreElements()) {\n                URL resourceUrl = enumeration.nextElement();\n                String resourceFile = resourceUrl.getFile();\n                String jarFile = resourceFile.substring(0,\n                    resourceFile.length() - resource.length() - FILE_IN_JAR.length());\n                urlList.add(new URL(jarFile));\n            }\n\n            return urlList;\n        }\n\n        @Override\n        public ContainerArchive getContainerArchive() throws Exception {\n\n            ContainerArchive archive = getJarContainerArchive();\n\n            if (archive == null) {\n                archive = createDirectoryContainerArchive();\n            }\n\n            if (archive == null) {\n                throw new ArkRuntimeException(\"No Ark Container Jar File Found.\");\n            }\n\n            return archive;\n        }\n\n        protected ContainerArchive getJarContainerArchive() throws Exception {\n            List<URL> urlList = filterUrls(Constants.ARK_CONTAINER_MARK_ENTRY);\n\n            if (urlList.isEmpty()) {\n                return null;\n            }\n\n            if (urlList.size() > 1) {\n                throw new ArkRuntimeException(\"Duplicate Container Jar File Found.\");\n            }\n\n            return new JarContainerArchive(new JarFileArchive(FileUtils.file(urlList.get(0)\n                .getFile())));\n        }\n\n        @Override\n        public List<BizArchive> getBizArchives() throws Exception {\n            List<URL> urlList = filterUrls(Constants.ARK_BIZ_MARK_ENTRY);\n\n            List<BizArchive> bizArchives = new LinkedList<>();\n            if (className != null && methodName != null) {\n                bizArchives.add(createDirectoryBizModuleArchive());\n            }\n\n            for (URL url : urlList) {\n                bizArchives\n                    .add(new JarBizArchive(new JarFileArchive(FileUtils.file(url.getFile()))));\n            }\n\n            return bizArchives;\n        }\n\n        @Override\n        public List<PluginArchive> getPluginArchives() throws Exception {\n            List<URL> urlList = filterUrls(Constants.ARK_PLUGIN_MARK_ENTRY);\n\n            List<PluginArchive> pluginArchives = new ArrayList<>();\n            for (URL url : urlList) {\n                pluginArchives.add(new JarPluginArchive(new JarFileArchive(FileUtils.file(url\n                    .getFile()))));\n            }\n\n            return pluginArchives;\n        }\n\n        @Override\n        public List<URL> getConfClasspath() throws IOException {\n            List<URL> urls = new ArrayList<>();\n            if (arkConfBaseDir == null) {\n                arkConfBaseDir = deduceArkConfBaseDir();\n            }\n            scanConfClasspath(arkConfBaseDir, urls);\n            return urls;\n        }\n\n        private void scanConfClasspath(File arkConfBaseDir, List<URL> classpath) throws IOException {\n            if (arkConfBaseDir == null || arkConfBaseDir.isFile()\n                || arkConfBaseDir.listFiles() == null) {\n                return;\n            }\n            classpath.add(arkConfBaseDir.toURI().toURL());\n            for (File subFile : arkConfBaseDir.listFiles()) {\n                scanConfClasspath(subFile, classpath);\n            }\n        }\n\n        private File deduceArkConfBaseDir() {\n            File arkConfDir = null;\n            try {\n                URLClassLoader tempClassLoader = new URLClassLoader(urls);\n                Class entryClass = tempClassLoader.loadClass(className);\n                String classLocation = ClassUtils.getCodeBase(entryClass);\n                if (classLocation.startsWith(\"file:\")) {\n                    classLocation = classLocation.substring(\"file:\".length());\n                }\n                File file = classLocation == null ? null : FileUtils.file(classLocation);\n                while (file != null) {\n                    arkConfDir = FileUtils\n                        .file(file.getPath() + File.separator + ARK_CONF_BASE_DIR);\n                    if (arkConfDir.exists() && arkConfDir.isDirectory()) {\n                        break;\n                    }\n                    file = file.getParentFile();\n                }\n            } catch (Throwable throwable) {\n                throw new ArkRuntimeException(throwable);\n            }\n            // return 'conf/' directory or null\n            return arkConfDir == null ? null : arkConfDir.getParentFile();\n        }\n\n        @Override\n        public URL getUrl() {\n            throw new RuntimeException(\"unreachable invocation.\");\n        }\n\n        @Override\n        public Manifest getManifest() {\n            throw new RuntimeException(\"unreachable invocation.\");\n        }\n\n        @Override\n        public List<Archive> getNestedArchives(EntryFilter filter) {\n            throw new RuntimeException(\"unreachable invocation.\");\n        }\n\n        @Override\n        public Archive getNestedArchive(Entry entry) {\n            throw new RuntimeException(\"unreachable invocation.\");\n        }\n\n        @Override\n        public InputStream getInputStream(ZipEntry zipEntry) {\n            throw new RuntimeException(\"unreachable invocation.\");\n        }\n\n        @Override\n        public Iterator<Entry> iterator() {\n            throw new RuntimeException(\"unreachable invocation.\");\n        }\n\n        protected BizArchive createDirectoryBizModuleArchive() {\n            return new DirectoryBizArchive(className, methodName, filterBizUrls(urls));\n        }\n\n        protected ContainerArchive createDirectoryContainerArchive() {\n            URL[] candidates;\n            if (fromSurefire(urls)) {\n                candidates = parseClassPathFromSurefireBoot(getSurefireBooterJar(urls));\n            } else {\n                candidates = urls;\n            }\n            URL[] filterUrls = filterURLs(candidates);\n            return filterUrls == null ? null : new DirectoryContainerArchive(filterUrls);\n        }\n\n        protected boolean fromSurefire(URL[] urls) {\n            if (urls.length <= 4) {\n                for (URL url : urls) {\n                    if (url.getFile().contains(Constants.SUREFIRE_BOOT_JAR)) {\n                        return true;\n                    }\n                }\n            }\n            return false;\n        }\n\n        private URL getSurefireBooterJar(URL[] urls) {\n            for (URL url : urls) {\n                if (url.getFile().contains(Constants.SUREFIRE_BOOT_JAR)) {\n                    return url;\n                }\n            }\n            return null;\n        }\n\n        /**\n         * this method is used to choose jar file which is contained in sofa-ark-all.jar\n         *\n         * @return\n         */\n        protected URL[] filterURLs(URL[] urls) {\n            Set<String> arkContainerJarMarkers = DirectoryContainerArchive\n                .getArkContainerJarMarkers();\n\n            Set<URL> containerClassPath = new HashSet<>();\n            for (String marker : arkContainerJarMarkers) {\n                for (URL url : urls) {\n                    if (url.getPath().contains(marker)) {\n                        containerClassPath.add(url);\n                    }\n                }\n            }\n\n            return arkContainerJarMarkers.size() != containerClassPath.size() ? null\n                : containerClassPath.toArray(new URL[] {});\n        }\n\n        /**\n         * this method is used to eliminate agent classpath and biz classpath\n         *\n         * @param urls\n         * @return\n         */\n        protected URL[] filterBizUrls(URL[] urls) {\n            URL[] agentClassPath = ClassLoaderUtils.getAgentClassPath();\n            List<URL> urlList;\n            try {\n                urlList = filterUrls(Constants.ARK_BIZ_MARK_ENTRY);\n            } catch (Throwable throwable) {\n                // ignore\n                urlList = Collections.emptyList();\n            }\n            List<URL> bizURls = new ArrayList<>();\n            boolean isAgent;\n            for (URL url : urls) {\n                isAgent = false;\n                for (URL agentUrl : agentClassPath) {\n                    if (url.equals(agentUrl)) {\n                        isAgent = true;\n                        break;\n                    }\n                }\n                if (!isAgent && !urlList.contains(url)) {\n                    bizURls.add(url);\n                }\n            }\n\n            return bizURls.toArray(new URL[] {});\n        }\n\n        /**\n         * when execute mvn test, the classpath would be recorded in a MANIFEST.MF file ,\n         * including a surefire boot jar.\n         *\n         * @param surefireBootJar\n         * @return\n         */\n        protected URL[] parseClassPathFromSurefireBoot(URL surefireBootJar) {\n            AssertUtils.assertNotNull(surefireBootJar, \"SurefireBooter jar should not be null.\");\n            try (JarFile jarFile = new JarFile(surefireBootJar.getFile())) {\n                String[] classPath = jarFile.getManifest().getMainAttributes()\n                    .getValue(SUREFIRE_BOOT_CLASSPATH).split(SUREFIRE_BOOT_CLASSPATH_SPLIT);\n                List<URL> urls = new ArrayList<>();\n                for (String path : classPath) {\n                    urls.add(new URL(path));\n                }\n                return urls.toArray(new URL[] {});\n            } catch (IOException ex) {\n                throw new ArkRuntimeException(\"Parse classpath failed from surefire boot jar.\", ex);\n            }\n        }\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/bootstrap/ContainerClassLoader.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.bootstrap;\n\nimport com.alipay.sofa.ark.loader.jar.Handler;\n\nimport java.io.IOException;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.util.Enumeration;\n\n/**\n * ClassLoader to load Ark Container\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class ContainerClassLoader extends URLClassLoader {\n    private static final String ARK_SPI_PACKAGES       = \"com.alipay.sofa.ark.spi\";\n    private static final String ARK_API_PACKAGES       = \"com.alipay.sofa.ark.api\";\n    private static final String ARK_EXCEPTION_PACKAGES = \"com.alipay.sofa.ark.exception\";\n    private static final String ARK_LOG_PACKAGES       = \"com.alipay.sofa.ark.common.log\";\n\n    private ClassLoader         exportClassLoader;\n\n    private ClassLoader         agentClassLoader;\n\n    public ContainerClassLoader(URL[] urls, ClassLoader parent, ClassLoader agentClassLoader) {\n        super(urls, parent);\n        this.agentClassLoader = agentClassLoader;\n    }\n\n    public ContainerClassLoader(URL[] urls, ClassLoader parent, ClassLoader exportClassLoader,\n                                ClassLoader agentClassLoader) {\n        super(urls, parent);\n        this.exportClassLoader = exportClassLoader;\n        this.agentClassLoader = agentClassLoader;\n    }\n\n    @Override\n    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {\n        Handler.setUseFastConnectionExceptions(true);\n        try {\n            Class<?> clazz = resolveArkExportClass(name);\n            if (clazz != null) {\n                return clazz;\n            }\n            return resolveClass(name, resolve);\n        } finally {\n            Handler.setUseFastConnectionExceptions(false);\n        }\n    }\n\n    public Class<?> resolveClass(String name, boolean resolve) throws ClassNotFoundException {\n        try {\n            return super.loadClass(name, resolve);\n        } catch (ClassNotFoundException e) {\n            if (agentClassLoader != null) {\n                return agentClassLoader.loadClass(name);\n            }\n            throw e;\n        }\n    }\n\n    /**\n     * Load ark spi class\n     *\n     * @param name\n     * @return\n     */\n    protected Class<?> resolveArkExportClass(String name) {\n        if (exportClassLoader != null && isArkExportClass(name)) {\n            try {\n                return exportClassLoader.loadClass(name);\n            } catch (ClassNotFoundException e) {\n                // ignore\n            }\n        }\n        return null;\n    }\n\n    public boolean isArkExportClass(String className) {\n        return className.startsWith(ARK_SPI_PACKAGES) || className.startsWith(ARK_API_PACKAGES)\n               || className.startsWith(ARK_EXCEPTION_PACKAGES)\n               || className.endsWith(ARK_LOG_PACKAGES);\n    }\n\n    @Override\n    public URL getResource(String name) {\n        Handler.setUseFastConnectionExceptions(true);\n        try {\n            return super.getResource(name);\n        } finally {\n            Handler.setUseFastConnectionExceptions(false);\n        }\n    }\n\n    @Override\n    public Enumeration<URL> getResources(String name) throws IOException {\n        Handler.setUseFastConnectionExceptions(true);\n        try {\n            return new UseFastConnectionExceptionsEnumeration(super.getResources(name));\n        } finally {\n            Handler.setUseFastConnectionExceptions(false);\n        }\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/bootstrap/MainMethodRunner.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.bootstrap;\n\nimport java.lang.reflect.Method;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Utility class that is used  to call a main method. The class\n * containing the main method is loaded using the thread context class loader.\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class MainMethodRunner {\n\n    private final String              mainClassName;\n\n    private final String[]            args;\n\n    private final Map<String, String> envs;\n\n    /**\n     * Create a new {@link MainMethodRunner} instance.\n     * @param mainClass the main class\n     * @param args incoming arguments\n     */\n    public MainMethodRunner(String mainClass, String[] args, Map<String, String> envs) {\n        this.mainClassName = mainClass;\n        this.args = (args == null ? new String[] {} : args.clone());\n        this.envs = envs == null ? new HashMap<>() : envs;\n    }\n\n    public Object run() throws Exception {\n        Class<?> mainClass = Thread.currentThread().getContextClassLoader()\n            .loadClass(this.mainClassName);\n        Method mainMethod = mainClass.getDeclaredMethod(\"main\", String[].class);\n\n        // TODO: add envs into the tenant of jdk\n        return mainMethod.invoke(null, new Object[] { this.args });\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/bootstrap/UseFastConnectionExceptionsEnumeration.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.bootstrap;\n\nimport com.alipay.sofa.ark.loader.jar.Handler;\n\nimport java.net.URL;\nimport java.util.Enumeration;\n\n/**\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class UseFastConnectionExceptionsEnumeration implements Enumeration<URL> {\n\n    private final Enumeration<URL> delegate;\n\n    public UseFastConnectionExceptionsEnumeration(Enumeration<URL> delegate) {\n        this.delegate = delegate;\n    }\n\n    @Override\n    public boolean hasMoreElements() {\n        Handler.setUseFastConnectionExceptions(true);\n        try {\n            return this.delegate.hasMoreElements();\n        } finally {\n            Handler.setUseFastConnectionExceptions(false);\n        }\n\n    }\n\n    @Override\n    public URL nextElement() {\n        Handler.setUseFastConnectionExceptions(true);\n        try {\n            return this.delegate.nextElement();\n        } finally {\n            Handler.setUseFastConnectionExceptions(false);\n        }\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/DirectoryBizArchive.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader;\n\nimport com.alipay.sofa.ark.spi.archive.Archive;\nimport com.alipay.sofa.ark.spi.archive.BizArchive;\nimport com.alipay.sofa.ark.spi.constant.Constants;\n\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.jar.Manifest;\nimport java.util.zip.ZipEntry;\nimport static com.alipay.sofa.ark.spi.constant.Constants.*;\n\n/**\n * Ark Biz Module Directory\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class DirectoryBizArchive implements BizArchive {\n\n    public static final String MOCK_IDE_ARK_BIZ_NAME         = \"Startup In IDE\";\n    public static final String MOCK_IDE_ARK_BIZ_VERSION      = \"Mock version\";\n    public static final String MOCK_IDE_ARK_BIZ_MAIN_CLASS   = \"Mock Main Class\";\n    public static final String MOCK_IDE_WEB_CONTEXT_PATH     = ROOT_WEB_CONTEXT_PATH;\n    public static final int    MOCK_IDE_BIZ_STARTUP_PRIORITY = 0;\n\n    private final String       className;\n\n    private final String       methodName;\n\n    private final URL[]        urls;\n\n    private final Manifest     manifest                      = new Manifest();\n\n    public DirectoryBizArchive(String className, String methodName, URL[] urls) {\n        this.className = (className == null ? MOCK_IDE_ARK_BIZ_MAIN_CLASS : className);\n        this.methodName = methodName;\n        manifest.getMainAttributes().putValue(MAIN_CLASS_ATTRIBUTE, this.className);\n        manifest.getMainAttributes().putValue(START_CLASS_ATTRIBUTE, this.className);\n        manifest.getMainAttributes().putValue(PRIORITY_ATTRIBUTE,\n            String.valueOf(MOCK_IDE_BIZ_STARTUP_PRIORITY));\n        manifest.getMainAttributes().putValue(ARK_BIZ_NAME, MOCK_IDE_ARK_BIZ_NAME);\n        manifest.getMainAttributes().putValue(ARK_BIZ_VERSION, MOCK_IDE_ARK_BIZ_VERSION);\n        manifest.getMainAttributes().putValue(WEB_CONTEXT_PATH, MOCK_IDE_WEB_CONTEXT_PATH);\n        this.urls = urls;\n    }\n\n    public boolean isTestMode() {\n        return MOCK_IDE_ARK_BIZ_MAIN_CLASS.equals(className);\n    }\n\n    public String getClassName() {\n        return className;\n    }\n\n    public String getMethodName() {\n        return methodName;\n    }\n\n    @Override\n    public URL[] getUrls() {\n        return this.urls;\n    }\n\n    @Override\n    public boolean isEntryExist(EntryFilter filter) {\n        return filter.matches(new Entry() {\n            @Override\n            public boolean isDirectory() {\n                return false;\n            }\n\n            @Override\n            public String getName() {\n                return Constants.ARK_BIZ_MARK_ENTRY;\n            }\n        });\n    }\n\n    @Override\n    public URL getUrl() {\n        throw new RuntimeException(\"unreachable invocation.\");\n    }\n\n    @Override\n    public Manifest getManifest() {\n        return manifest;\n    }\n\n    @Override\n    public List<Archive> getNestedArchives(EntryFilter filter) {\n        throw new RuntimeException(\"unreachable invocation.\");\n    }\n\n    @Override\n    public Archive getNestedArchive(Entry entry) {\n        if (Constants.ARK_BIZ_MARK_ENTRY.equals(entry.getName())) {\n            return new NoopBizArchive();\n        }\n        throw new RuntimeException(\"unreachable invocation.\");\n    }\n\n    @Override\n    public InputStream getInputStream(ZipEntry zipEntry) {\n        throw new RuntimeException(\"unreachable invocation.\");\n    }\n\n    @Override\n    public Iterator<Entry> iterator() {\n        throw new RuntimeException(\"unreachable invocation.\");\n    }\n\n    class NoopBizArchive extends JarBizArchive {\n        public NoopBizArchive() {\n            super(null);\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/DirectoryContainerArchive.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader;\n\nimport com.alipay.sofa.ark.spi.archive.Archive;\nimport com.alipay.sofa.ark.spi.archive.ContainerArchive;\n\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.jar.Manifest;\nimport java.util.zip.ZipEntry;\n\n/**\n * Ark Container Directory\n *\n * @author qilong.zql\n * @since 0.3.0\n */\npublic class DirectoryContainerArchive implements ContainerArchive {\n\n    private final URL[]           urls;\n\n    private final static String[] AKR_CONTAINER_JAR = { \"aopalliance-1.0\", \"commons-io-2.7\",\n            \"guava-33.0.0-jre\", \"guice-6.0.0\", \"failureaccess-1.0.1\", \"javax.inject-1\",\n            \"logback-core-1.2.9\", \"logback-classic-1.2.9\", \"slf4j-api-1.7.32\",\n            \"log-sofa-boot-starter\", \"log-sofa-boot\", \"sofa-common-tools\",\n            \"netty-all-4.1.109.Final\", \"netty-transport-4.1.109.Final\",\n            \"netty-common-4.1.109.Final\", \"netty-handler-4.1.109.Final\",\n            \"netty-codec-4.1.109.Final\", \"netty-buffer-4.1.109.Final\",\n            \"sofa-ark-parent/core-impl/container/target/classes\",\n            \"sofa-ark-parent/core-impl/archive/target/classes\",\n            \"sofa-ark-parent/core/spi/target/classes\",\n            \"sofa-ark-parent/core/common/target/classes\",\n            \"sofa-ark-parent/core/exception/target/classes\",\n            \"sofa-ark-parent/core/api/target/classes\" };\n\n    public DirectoryContainerArchive(URL[] urls) {\n        this.urls = urls;\n    }\n\n    public static Set<String> getArkContainerJarMarkers() {\n        return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(AKR_CONTAINER_JAR)));\n    }\n\n    @Override\n    public URL[] getUrls() {\n        return urls;\n    }\n\n    @Override\n    public URL getUrl() {\n        throw new RuntimeException(\"unreachable invocation.\");\n    }\n\n    @Override\n    public Manifest getManifest() {\n        throw new RuntimeException(\"unreachable invocation.\");\n    }\n\n    @Override\n    public List<Archive> getNestedArchives(EntryFilter filter) {\n        throw new RuntimeException(\"unreachable invocation.\");\n    }\n\n    @Override\n    public Archive getNestedArchive(Entry entry) {\n        throw new RuntimeException(\"unreachable invocation.\");\n    }\n\n    @Override\n    public InputStream getInputStream(ZipEntry zipEntry) {\n        throw new RuntimeException(\"unreachable invocation.\");\n    }\n\n    @Override\n    public Iterator<Entry> iterator() {\n        throw new RuntimeException(\"unreachable invocation.\");\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/EmbedClassPathArchive.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader;\n\nimport com.alipay.sofa.ark.bootstrap.ClasspathLauncher;\nimport com.alipay.sofa.ark.common.util.FileUtils;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.loader.archive.JarFileArchive;\nimport com.alipay.sofa.ark.spi.archive.Archive;\nimport com.alipay.sofa.ark.spi.archive.BizArchive;\nimport com.alipay.sofa.ark.spi.archive.ContainerArchive;\nimport com.alipay.sofa.ark.spi.archive.PluginArchive;\nimport com.alipay.sofa.ark.spi.constant.Constants;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Enumeration;\nimport java.util.LinkedList;\nimport java.util.List;\n\n/**\n * A embed classpath archive base on an application fat jar\n *\n * @author bingjie.lbj\n */\npublic class EmbedClassPathArchive extends ClasspathLauncher.ClassPathArchive {\n\n    public EmbedClassPathArchive(String className, String method, URL[] urls) throws IOException {\n        super(className, method, urls);\n    }\n\n    @Override\n    public ContainerArchive getContainerArchive() throws Exception {\n        List<URL> urlList = filterUrls(Constants.ARK_CONTAINER_MARK_ENTRY);\n\n        if (urlList.isEmpty()) {\n            return createDirectoryContainerArchive();\n        }\n\n        if (urlList.size() > 1) {\n            throw new ArkRuntimeException(\"Duplicate Container Jar File Found.\");\n        }\n\n        return new JarContainerArchive(getUrlJarFileArchive(urlList.get(0)));\n    }\n\n    @Override\n    public List<BizArchive> getBizArchives() throws Exception {\n        //将classpath中的biz包载入\n        List<URL> urlList = filterBizUrl(Constants.ARK_BIZ_MARK_ENTRY);\n        List<BizArchive> bizArchives = new LinkedList<>();\n        for (URL url : urlList) {\n            //判断是classpath下的还是fatjar内的biz包\n            if (url.getPath().contains(\".jar!\")) {\n                Archive archiveFromJarEntry = getArchiveFromJarEntry(url);\n                if (archiveFromJarEntry != null) {\n                    bizArchives.add(new JarBizArchive(archiveFromJarEntry));\n                }\n\n            } else {\n                bizArchives\n                    .add(new JarBizArchive(new JarFileArchive(FileUtils.file(url.getFile()))));\n            }\n        }\n        return bizArchives;\n    }\n\n    /**\n     * 从biz包内解析出archive\n     * @param jarUrl biz包的url路径\n     * @return 依赖的arkbiz包\n     * @throws IOException\n     */\n    private Archive getArchiveFromJarEntry(URL jarUrl) throws IOException {\n        String jarPath = jarUrl.getPath().substring(0, jarUrl.getPath().indexOf(\"!\"));\n        String bizPath = jarUrl.getPath().substring(jarUrl.getPath().indexOf(\"!\") + 2);\n        List<Archive> nestedArchives = new JarFileArchive(FileUtils.file(jarPath))\n                .getNestedArchives(entry -> entry.getName().equals(bizPath));\n        if (nestedArchives.isEmpty()) {\n            return null;\n        }\n        return nestedArchives.get(0);\n    }\n\n    /**\n     * 过滤出biz包\n     */\n    public List<URL> filterBizUrl(String resource) throws Exception {\n        List<URL> urlList = new ArrayList<>();\n        Enumeration<URL> enumeration = super.urlClassLoader.findResources(resource);\n        while (enumeration.hasMoreElements()) {\n            URL resourceUrl = enumeration.nextElement();\n            String resourceFile = resourceUrl.getFile();\n            String jarFile = resourceFile.substring(0, resourceFile.length() - resource.length()\n                                                       - FILE_IN_JAR.length());\n            urlList.add(new URL(jarFile));\n        }\n        return urlList;\n    }\n\n    @Override\n    public List<PluginArchive> getPluginArchives() throws Exception {\n        List<URL> urlList = filterUrls(Constants.ARK_PLUGIN_MARK_ENTRY);\n\n        List<PluginArchive> pluginArchives = new ArrayList<>();\n        for (URL url : urlList) {\n            pluginArchives.add(new JarPluginArchive(getUrlJarFileArchive(url)));\n        }\n        return pluginArchives;\n    }\n\n    protected JarFileArchive getUrlJarFileArchive(URL url) throws IOException {\n        String file = url.getFile();\n        if (file.contains(FILE_IN_JAR)) {\n            int pos = file.indexOf(FILE_IN_JAR);\n            File fatJarFile = FileUtils.file(file.substring(0, pos));\n            String nestedJar = file.substring(file.lastIndexOf(\"/\") + 1);\n            JarFileArchive fatJarFileArchive = new JarFileArchive(fatJarFile);\n            List<Archive> matched = fatJarFileArchive.getNestedArchives(entry -> entry.getName().contains(nestedJar));\n            return (JarFileArchive) matched.get(0);\n        } else {\n            return new JarFileArchive(FileUtils.file(file));\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/ExecutableArkBizJar.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader;\n\nimport com.alipay.sofa.ark.spi.archive.*;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.jar.Manifest;\nimport java.util.zip.ZipEntry;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.CONF_BASE_DIR;\n\n/**\n * Executable Ark Biz Fat Jar\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class ExecutableArkBizJar implements ExecutableArchive {\n\n    public final String  SOFA_ARK_CONTAINER = \"SOFA-ARK/container/\";\n\n    public final String  SOFA_ARK_MODULE    = \"SOFA-ARK/biz/\";\n\n    public final String  SOFA_ARK_PLUGIN    = \"SOFA-ARK/plugin/\";\n\n    public final Archive archive;\n\n    public final URL     url;\n\n    public ExecutableArkBizJar(Archive archive) {\n        this(archive, null);\n    }\n\n    public ExecutableArkBizJar(Archive archive, URL url) {\n        this.archive = archive;\n        this.url = url;\n    }\n\n    @Override\n    public URL getUrl() throws MalformedURLException {\n        return this.url == null ? this.archive.getUrl() : this.url;\n    }\n\n    @Override\n    public Manifest getManifest() throws IOException {\n        return archive.getManifest();\n    }\n\n    @Override\n    public List<Archive> getNestedArchives(EntryFilter filter) throws IOException {\n        List<Archive> nestedArchives = new ArrayList<>();\n        for (Entry entry : this) {\n            if (filter.matches(entry)) {\n                nestedArchives.add(getNestedArchive(entry));\n            }\n        }\n        return Collections.unmodifiableList(nestedArchives);\n    }\n\n    @Override\n    public InputStream getInputStream(ZipEntry zipEntry) throws IOException {\n        return this.archive.getInputStream(zipEntry);\n    }\n\n    @Override\n    public Archive getNestedArchive(Entry entry) throws IOException {\n        return this.archive.getNestedArchive(entry);\n    }\n\n    @Override\n    public Iterator<Entry> iterator() {\n        return this.archive.iterator();\n    }\n\n    @Override\n    public ContainerArchive getContainerArchive() throws Exception {\n\n        List<Archive> archives = getNestedArchives(new EntryFilter() {\n            @Override\n            public boolean matches(Entry entry) {\n                return !entry.getName().equals(SOFA_ARK_CONTAINER)\n                       && entry.getName().startsWith(SOFA_ARK_CONTAINER);\n            }\n        });\n\n        if (archives.isEmpty()) {\n            throw new RuntimeException(\"No ark container archive found!\");\n        }\n\n        return new JarContainerArchive(archives.get(0));\n    }\n\n    /**\n     * Returns the ark-biz module archives that will run upon ark container\n     * @return biz-app archives\n     * @throws Exception\n     */\n    @Override\n    public List<BizArchive> getBizArchives() throws Exception {\n\n        List<Archive> archives = getNestedArchives(new EntryFilter() {\n            @Override\n            public boolean matches(Entry entry) {\n                return !entry.getName().equals(SOFA_ARK_MODULE)\n                       && entry.getName().startsWith(SOFA_ARK_MODULE);\n            }\n        });\n\n        List<BizArchive> bizArchives = new ArrayList<>();\n        for (Archive archive : archives) {\n            bizArchives.add(new JarBizArchive(archive));\n        }\n        return bizArchives;\n\n    }\n\n    /**\n     * Returns the ark plugin archives that will be applied to class isolation strategy of ark container\n     * @return ark plugin archives\n     * @throws Exception\n     */\n    @Override\n    public List<PluginArchive> getPluginArchives() throws Exception {\n\n        List<Archive> archives = this.archive.getNestedArchives(new EntryFilter() {\n            @Override\n            public boolean matches(Entry entry) {\n                return !entry.getName().equals(SOFA_ARK_PLUGIN)\n                       && entry.getName().startsWith(SOFA_ARK_PLUGIN);\n            }\n        });\n\n        List<PluginArchive> pluginArchives = new ArrayList<>();\n        for (Archive archive : archives) {\n            pluginArchives.add(new JarPluginArchive(archive));\n        }\n        return pluginArchives;\n\n    }\n\n    @Override\n    public List<URL> getConfClasspath() throws Exception {\n        List<Archive> archives = getNestedArchives(new EntryFilter() {\n            @Override\n            public boolean matches(Entry entry) {\n                return entry.getName().startsWith(CONF_BASE_DIR) && entry.isDirectory();\n            }\n        });\n        List<URL> urls = new ArrayList<>();\n        for (Archive archive : archives) {\n            urls.add(archive.getUrl());\n        }\n        return urls;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/ExplodedBizArchive.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader;\n\nimport com.alipay.sofa.ark.spi.archive.Archive;\nimport com.alipay.sofa.ark.spi.archive.BizArchive;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.jar.Manifest;\nimport java.util.zip.ZipEntry;\n\n/**\n * Ark  Biz Module exploded directory archive\n *\n * @author bingjie.lbj\n */\npublic class ExplodedBizArchive implements BizArchive {\n    private static final String SOFA_ARK_BIZ_LIB = \"lib/\";\n    private static final String MANIFEST_NAME    = \"META-INF/MANIFEST.MF\";\n    private File                file;\n    private URL[]               urls;\n    private Manifest            manifest;\n\n    public ExplodedBizArchive(File root) throws IOException {\n        this.file = root;\n        this.urls = scanUrl();\n        try (FileInputStream fileInputStream = new FileInputStream(new File(root, MANIFEST_NAME));) {\n            this.manifest = new Manifest(fileInputStream);\n        }\n    }\n\n    private URL[] scanUrl() throws MalformedURLException {\n        List<URL> urls = new ArrayList<>();\n        urls.add(this.file.toURI().toURL());\n        File libs = new File(file, SOFA_ARK_BIZ_LIB);\n        urls.add(libs.toURI().toURL());\n        File[] files = libs.listFiles();\n        if (files != null) {\n            for (File lib : files) {\n                urls.add(lib.toURI().toURL());\n            }\n        }\n\n        return urls.toArray(new URL[] {});\n    }\n\n    @Override\n    public URL[] getUrls() throws IOException {\n        return urls;\n    }\n\n    @Override\n    public boolean isEntryExist(EntryFilter filter) {\n        throw new UnsupportedOperationException(\"unreachable invocation.\");\n    }\n\n    @Override\n    public URL getUrl() throws MalformedURLException {\n        return file.toURI().toURL();\n    }\n\n    @Override\n    public Manifest getManifest() throws IOException {\n        return this.manifest;\n    }\n\n    @Override\n    public List<Archive> getNestedArchives(EntryFilter filter) throws IOException {\n        throw new UnsupportedOperationException(\"unreachable invocation.\");\n    }\n\n    @Override\n    public Archive getNestedArchive(Entry entry) throws IOException {\n        throw new UnsupportedOperationException(\"unreachable invocation.\");\n    }\n\n    @Override\n    public InputStream getInputStream(ZipEntry zipEntry) throws IOException {\n        throw new UnsupportedOperationException(\"unreachable invocation.\");\n    }\n\n    @Override\n    public Iterator<Entry> iterator() {\n        throw new UnsupportedOperationException(\"unreachable invocation.\");\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/JarBizArchive.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader;\n\nimport com.alipay.sofa.ark.spi.archive.AbstractArchive;\nimport com.alipay.sofa.ark.spi.archive.Archive;\nimport com.alipay.sofa.ark.spi.archive.BizArchive;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.jar.Manifest;\nimport java.util.zip.ZipEntry;\n\n/**\n * Ark Biz Module Fat Jar\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class JarBizArchive extends AbstractArchive implements BizArchive {\n\n    public final Archive archive;\n\n    private final String SOFA_ARK_BIZ_LIB        = \"lib/\";\n\n    private final String SOFA_ARK_BIZ_LIB_EXPORT = \"lib/export\";\n\n    public JarBizArchive(Archive archive) {\n        this.archive = archive;\n    }\n\n    @Override\n    public URL getUrl() throws MalformedURLException {\n        return this.archive.getUrl();\n    }\n\n    @Override\n    public Manifest getManifest() throws IOException {\n        return this.archive.getManifest();\n    }\n\n    @Override\n    public List<Archive> getNestedArchives(EntryFilter filter) throws IOException {\n        List<Archive> nestedArchives = new ArrayList<>();\n        for (Entry entry : this) {\n            if (filter.matches(entry)) {\n                nestedArchives.add(getNestedArchive(entry));\n            }\n        }\n        return Collections.unmodifiableList(nestedArchives);\n    }\n\n    @Override\n    public InputStream getInputStream(ZipEntry zipEntry) throws IOException {\n        return this.archive.getInputStream(zipEntry);\n    }\n\n    @Override\n    public Archive getNestedArchive(Entry entry) throws IOException {\n        return archive.getNestedArchive(entry);\n    }\n\n    @Override\n    public Iterator<Entry> iterator() {\n        return this.archive.iterator();\n    }\n\n    @Override\n    public URL[] getUrls() throws IOException {\n        return getUrls(new EntryFilter() {\n            @Override\n            public boolean matches(Entry entry) {\n                return entry.getName().startsWith(SOFA_ARK_BIZ_LIB);\n            }\n        });\n    }\n\n    public URL[] getExportUrls() throws IOException {\n        return getUrls(new EntryFilter() {\n            @Override\n            public boolean matches(Entry entry) {\n                return entry.getName().startsWith(SOFA_ARK_BIZ_LIB_EXPORT)\n                       && !entry.getName().equals(SOFA_ARK_BIZ_LIB_EXPORT);\n            }\n        });\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/JarContainerArchive.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader;\n\nimport com.alipay.sofa.ark.spi.archive.AbstractArchive;\nimport com.alipay.sofa.ark.spi.archive.Archive;\nimport com.alipay.sofa.ark.spi.archive.ContainerArchive;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.Iterator;\nimport java.util.jar.Manifest;\nimport java.util.zip.ZipEntry;\n\n/**\n * Ark Container Fat Jar\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class JarContainerArchive extends AbstractArchive implements ContainerArchive {\n\n    private final Archive archive;\n\n    private final String  SOFA_ARK_CONTAINER_LIB = \"lib/\";\n\n    public JarContainerArchive(Archive archive) {\n        this.archive = archive;\n    }\n\n    @Override\n    public URL[] getUrls() throws IOException {\n        return getUrls(new EntryFilter() {\n            @Override\n            public boolean matches(Entry entry) {\n                return entry.getName().startsWith(SOFA_ARK_CONTAINER_LIB);\n            }\n        });\n    }\n\n    @Override\n    public URL getUrl() throws MalformedURLException {\n        return this.archive.getUrl();\n    }\n\n    @Override\n    public Manifest getManifest() throws IOException {\n        return this.archive.getManifest();\n    }\n\n    @Override\n    public Archive getNestedArchive(Entry entry) throws IOException {\n        return this.archive.getNestedArchive(entry);\n    }\n\n    @Override\n    public InputStream getInputStream(ZipEntry zipEntry) throws IOException {\n        return this.archive.getInputStream(zipEntry);\n    }\n\n    @Override\n    public Iterator<Entry> iterator() {\n        return this.archive.iterator();\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/JarPluginArchive.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader;\n\nimport com.alipay.sofa.ark.spi.archive.AbstractArchive;\nimport com.alipay.sofa.ark.spi.archive.Archive;\nimport com.alipay.sofa.ark.spi.archive.PluginArchive;\n\nimport java.io.*;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.*;\nimport java.util.jar.Manifest;\nimport java.util.zip.ZipEntry;\n\n/**\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class JarPluginArchive extends AbstractArchive implements PluginArchive {\n\n    public final Archive        archive;\n    private URL[]               extensionUrls;\n\n    private final static String SOFA_ARK_PLUGIN_LIB = \"lib/\";\n\n    public JarPluginArchive(Archive archive) {\n        this.archive = archive;\n    }\n\n    public URL[] getExtensionUrls() {\n        return extensionUrls;\n    }\n\n    @Override\n    public URL getUrl() throws MalformedURLException {\n        return this.archive.getUrl();\n    }\n\n    @Override\n    public Manifest getManifest() throws IOException {\n        return this.archive.getManifest();\n    }\n\n    @Override\n    public List<Archive> getNestedArchives(EntryFilter filter) throws IOException {\n        List<Archive> nestedArchives = new ArrayList<>();\n        for (Entry entry : this) {\n            if (filter.matches(entry)) {\n                nestedArchives.add(getNestedArchive(entry));\n            }\n        }\n        return Collections.unmodifiableList(nestedArchives);\n    }\n\n    @Override\n    public InputStream getInputStream(ZipEntry zipEntry) throws IOException {\n        return this.archive.getInputStream(zipEntry);\n    }\n\n    @Override\n    public Archive getNestedArchive(Entry entry) throws IOException {\n        return this.archive.getNestedArchive(entry);\n    }\n\n    @Override\n    public Iterator<Entry> iterator() {\n        return this.archive.iterator();\n    }\n\n    /**\n     * fetch classpath to startup sofa-ark plugin\n     *\n     * @return\n     */\n    @Override\n    public URL[] getUrls() throws IOException {\n        return getUrls(new EntryFilter() {\n            @Override\n            public boolean matches(Entry entry) {\n                return entry.getName().startsWith(SOFA_ARK_PLUGIN_LIB)\n                       && !SOFA_ARK_PLUGIN_LIB.equals(entry.getName());\n            }\n        });\n    }\n\n    @Override\n    public void setExtensionUrls(URL[] extensionUrls) {\n        this.extensionUrls = extensionUrls;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/archive/ExplodedArchive.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.archive;\n\nimport com.alipay.sofa.ark.spi.archive.Archive;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.Deque;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.NoSuchElementException;\nimport java.util.Set;\nimport java.util.jar.Manifest;\nimport java.util.zip.ZipEntry;\n\n/**\n * {@link Archive} implementation backed by an exploded archive directory.\n *\n * @author Phillip Webb\n * @author Andy Wilkinson\n */\npublic class ExplodedArchive implements Archive {\n\n    private static final Set<String> SKIPPED_NAMES = new HashSet<>(Arrays.asList(\".\", \"..\"));\n\n    private final File               root;\n\n    private final boolean            recursive;\n\n    private File                     manifestFile;\n\n    private Manifest                 manifest;\n\n    /**\n     * Create a new {@link ExplodedArchive} instance.\n     * @param root the root folder\n     */\n    public ExplodedArchive(File root) {\n        this(root, true);\n    }\n\n    /**\n     * Create a new {@link ExplodedArchive} instance.\n     * @param root the root folder\n     * @param recursive if recursive searching should be used to locate the manifest.\n     * Defaults to {@code true}, folders with a large tree might want to set this to\n     * {@code\n     * false}.\n     */\n    public ExplodedArchive(File root, boolean recursive) {\n        if (!root.exists() || !root.isDirectory()) {\n            throw new IllegalArgumentException(\"Invalid source folder \" + root);\n        }\n        this.root = root;\n        this.recursive = recursive;\n        this.manifestFile = getManifestFile(root);\n    }\n\n    private File getManifestFile(File root) {\n        File metaInf = new File(root, \"META-INF\");\n        return new File(metaInf, \"MANIFEST.MF\");\n    }\n\n    @Override\n    public URL getUrl() throws MalformedURLException {\n        return this.root.toURI().toURL();\n    }\n\n    @Override\n    public Manifest getManifest() throws IOException {\n        if (this.manifest == null && this.manifestFile.exists()) {\n            try (FileInputStream inputStream = new FileInputStream(this.manifestFile)) {\n                this.manifest = new Manifest(inputStream);\n            }\n        }\n        return this.manifest;\n    }\n\n    @Override\n    public List<Archive> getNestedArchives(EntryFilter filter) throws IOException {\n        List<Archive> nestedArchives = new ArrayList<>();\n        for (Entry entry : this) {\n            if (filter.matches(entry)) {\n                nestedArchives.add(getNestedArchive(entry));\n            }\n        }\n        return Collections.unmodifiableList(nestedArchives);\n    }\n\n    @Override\n    public InputStream getInputStream(ZipEntry zipEntry) throws IOException {\n        throw new RuntimeException(\"unreachable invocation.\");\n    }\n\n    @Override\n    public Iterator<Entry> iterator() {\n        return new FileEntryIterator(this.root, this.recursive);\n    }\n\n    public Archive getNestedArchive(Entry entry) throws IOException {\n        File file = ((FileEntry) entry).getFile();\n        return (file.isDirectory() ? new ExplodedArchive(file) : new JarFileArchive(file));\n    }\n\n    @Override\n    public String toString() {\n        try {\n            return getUrl().toString();\n        } catch (Exception ex) {\n            return \"exploded archive\";\n        }\n    }\n\n    /**\n     * File based {@link Entry} {@link Iterator}.\n     */\n    private static class FileEntryIterator implements Iterator<Entry> {\n\n        private final Comparator<File>      entryComparator = new EntryComparator();\n\n        private final File                  root;\n\n        private final boolean               recursive;\n\n        private final Deque<Iterator<File>> stack           = new LinkedList<>();\n\n        private File                        current;\n\n        FileEntryIterator(File root, boolean recursive) {\n            this.root = root;\n            this.recursive = recursive;\n            this.stack.add(listFiles(root));\n            this.current = poll();\n        }\n\n        @Override\n        public boolean hasNext() {\n            return this.current != null;\n        }\n\n        @Override\n        public Entry next() {\n            if (this.current == null) {\n                throw new NoSuchElementException();\n            }\n            File file = this.current;\n            if (file.isDirectory() && (this.recursive || file.getParentFile().equals(this.root))) {\n                this.stack.addFirst(listFiles(file));\n            }\n            this.current = poll();\n            String name = file.toURI().getPath().substring(this.root.toURI().getPath().length());\n            return new FileEntry(name, file);\n        }\n\n        private Iterator<File> listFiles(File file) {\n            File[] files = file.listFiles();\n            if (files == null) {\n                return Collections.<File> emptyList().iterator();\n            }\n            Arrays.sort(files, this.entryComparator);\n            return Arrays.asList(files).iterator();\n        }\n\n        private File poll() {\n            while (!this.stack.isEmpty()) {\n                while (this.stack.peek().hasNext()) {\n                    File file = this.stack.peek().next();\n                    if (!SKIPPED_NAMES.contains(file.getName())) {\n                        return file;\n                    }\n                }\n                this.stack.poll();\n            }\n            return null;\n        }\n\n        @Override\n        public void remove() {\n            throw new UnsupportedOperationException(\"remove\");\n        }\n\n        /**\n         * {@link Comparator} that orders {@link File} entries by their absolute paths.\n         */\n        private static class EntryComparator implements Comparator<File> {\n\n            @Override\n            public int compare(File o1, File o2) {\n                return o1.getAbsolutePath().compareTo(o2.getAbsolutePath());\n            }\n\n        }\n\n    }\n\n    /**\n     * {@link Entry} backed by a File.\n     */\n    private static class FileEntry implements Entry {\n\n        private final String name;\n\n        private final File   file;\n\n        FileEntry(String name, File file) {\n            this.name = name;\n            this.file = file;\n        }\n\n        public File getFile() {\n            return this.file;\n        }\n\n        @Override\n        public boolean isDirectory() {\n            return this.file.isDirectory();\n        }\n\n        @Override\n        public String getName() {\n            return this.name;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/archive/JarFileArchive.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.archive;\n\nimport java.io.*;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.*;\nimport java.util.jar.JarEntry;\nimport java.util.jar.Manifest;\nimport java.util.zip.ZipEntry;\n\nimport com.alipay.sofa.ark.common.util.FileUtils;\nimport com.alipay.sofa.ark.loader.jar.JarFile;\nimport com.alipay.sofa.ark.loader.data.RandomAccessData.ResourceAccess;\nimport com.alipay.sofa.ark.spi.archive.Archive;\n\n/**\n * {@link Archive} implementation backed by a {@link JarFile}.\n *\n * @author Phillip Webb\n * @author Andy Wilkinson\n */\npublic class JarFileArchive implements Archive {\n\n    private static final String UNPACK_MARKER = \"UNPACK:\";\n\n    private static final int    BUFFER_SIZE   = 32 * 1024;\n\n    private final JarFile       jarFile;\n\n    private URL                 url;\n\n    private File                tempUnpackFolder;\n\n    public JarFileArchive(File file) throws IOException {\n        this(file, null);\n    }\n\n    public JarFileArchive(File file, URL url) throws IOException {\n        this(new JarFile(file));\n        this.url = url;\n    }\n\n    public JarFileArchive(JarFile jarFile) {\n        this.jarFile = jarFile;\n    }\n\n    @Override\n    public URL getUrl() throws MalformedURLException {\n        if (this.url != null) {\n            return this.url;\n        }\n        return this.jarFile.getUrl();\n    }\n\n    @Override\n    public Manifest getManifest() throws IOException {\n        return this.jarFile.getManifest();\n    }\n\n    @Override\n    public List<Archive> getNestedArchives(EntryFilter filter) throws IOException {\n        List<Archive> nestedArchives = new ArrayList<>();\n        for (Entry entry : this) {\n            if (filter.matches(entry)) {\n                nestedArchives.add(getNestedArchive(entry));\n            }\n        }\n        return Collections.unmodifiableList(nestedArchives);\n    }\n\n    @Override\n    public InputStream getInputStream(ZipEntry zipEntry) throws IOException {\n        return this.jarFile.getInputStream(zipEntry);\n    }\n\n    @Override\n    public Iterator<Entry> iterator() {\n        return new EntryIterator(this.jarFile.entries());\n    }\n\n    public Properties getPomProperties() throws IOException {\n        return this.jarFile.getPomProperties();\n    }\n\n    public Archive getNestedArchive(Entry entry) throws IOException {\n        JarEntry jarEntry = ((JarFileEntry) entry).getJarEntry();\n        if (jarEntry.getComment() != null && jarEntry.getComment().startsWith(UNPACK_MARKER)) {\n            return getUnpackedNestedArchive(jarEntry);\n        }\n        try {\n            JarFile jarFile = this.jarFile.getNestedJarFile(jarEntry);\n            return new JarFileArchive(jarFile);\n        } catch (Exception ex) {\n            throw new IllegalStateException(\"Failed to get nested archive for entry \"\n                                            + entry.getName(), ex);\n        }\n    }\n\n    private Archive getUnpackedNestedArchive(JarEntry jarEntry) throws IOException {\n        String name = jarEntry.getName();\n        if (name.lastIndexOf(\"/\") != -1) {\n            name = name.substring(name.lastIndexOf(\"/\") + 1);\n        }\n        File file = new File(getTempUnpackFolder(), name);\n        if (!file.exists() || file.length() != jarEntry.getSize()) {\n            unpack(jarEntry, file);\n        }\n        return new JarFileArchive(file, file.toURI().toURL());\n    }\n\n    private File getTempUnpackFolder() {\n        if (this.tempUnpackFolder == null) {\n            File tempFolder = FileUtils.file(System.getProperty(\"java.io.tmpdir\"));\n            this.tempUnpackFolder = createUnpackFolder(tempFolder);\n        }\n        return this.tempUnpackFolder;\n    }\n\n    private File createUnpackFolder(File parent) {\n        int attempts = 0;\n        while (attempts++ < 1000) {\n            String fileName = FileUtils.file(this.jarFile.getName()).getName();\n            File unpackFolder = new File(parent, fileName + \"-spring-boot-libs-\"\n                                                 + UUID.randomUUID());\n            if (unpackFolder.mkdirs()) {\n                return unpackFolder;\n            }\n        }\n        throw new IllegalStateException(\"Failed to create unpack folder in directory '\" + parent\n                                        + \"'\");\n    }\n\n    private void unpack(JarEntry entry, File file) throws IOException {\n        InputStream inputStream = this.jarFile.getInputStream(entry, ResourceAccess.ONCE);\n        try {\n            OutputStream outputStream = new FileOutputStream(file);\n            try {\n                byte[] buffer = new byte[BUFFER_SIZE];\n                int bytesRead;\n                while ((bytesRead = inputStream.read(buffer)) != -1) {\n                    outputStream.write(buffer, 0, bytesRead);\n                }\n                outputStream.flush();\n            } finally {\n                outputStream.close();\n            }\n        } finally {\n            inputStream.close();\n        }\n    }\n\n    @Override\n    public String toString() {\n        try {\n            return getUrl().toString();\n        } catch (Exception ex) {\n            return \"jar archive\";\n        }\n    }\n\n    /**\n     * {@link Archive.Entry} iterator implementation backed by {@link JarEntry}.\n     */\n    private static class EntryIterator implements Iterator<Entry> {\n\n        private final Enumeration<JarEntry> enumeration;\n\n        EntryIterator(Enumeration<JarEntry> enumeration) {\n            this.enumeration = enumeration;\n        }\n\n        @Override\n        public boolean hasNext() {\n            return this.enumeration.hasMoreElements();\n        }\n\n        @Override\n        public Entry next() {\n            return new JarFileEntry(this.enumeration.nextElement());\n        }\n\n        @Override\n        public void remove() {\n            throw new UnsupportedOperationException(\"remove\");\n        }\n\n    }\n\n    /**\n     * {@link Archive.Entry} implementation backed by a {@link JarEntry}.\n     */\n    public static class JarFileEntry implements Entry {\n\n        private final JarEntry jarEntry;\n\n        public JarFileEntry(JarEntry jarEntry) {\n            this.jarEntry = jarEntry;\n        }\n\n        public JarEntry getJarEntry() {\n            return this.jarEntry;\n        }\n\n        @Override\n        public boolean isDirectory() {\n            return this.jarEntry.isDirectory();\n        }\n\n        @Override\n        public String getName() {\n            return this.jarEntry.getName();\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/data/RandomAccessData.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.data;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\n/**\n * Interface that provides read-only random access to some underlying data.\n * Implementations must allow concurrent reads in a thread-safe manner.\n *\n * @author Phillip Webb\n */\npublic interface RandomAccessData {\n\n    /**\n     * Returns an {@link InputStream} that can be used to read the underlying data. The\n     * caller is responsible close the underlying stream.\n     * @param access hint indicating how the underlying data should be accessed\n     * @return a new input stream that can be used to read the underlying data.\n     * @throws IOException if the stream cannot be opened\n     */\n    InputStream getInputStream(ResourceAccess access) throws IOException;\n\n    /**\n     * Returns a new {@link RandomAccessData} for a specific subsection of this data.\n     * @param offset the offset of the subsection\n     * @param length the length of the subsection\n     * @return the subsection data\n     */\n    RandomAccessData getSubsection(long offset, long length);\n\n    /**\n     * Returns the size of the data.\n     * @return the size\n     */\n    long getSize();\n\n    /**\n     * Lock modes for accessing the underlying resource.\n     */\n    enum ResourceAccess {\n\n        /**\n         * Obtain access to the underlying resource once and keep it until the stream is\n         * closed.\n         */\n        ONCE,\n\n        /**\n         * Obtain access to the underlying resource on each read, releasing it when done.\n         */\n        PER_READ\n\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/data/RandomAccessDataFile.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.data;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.RandomAccessFile;\nimport java.util.Queue;\nimport java.util.concurrent.ConcurrentLinkedQueue;\nimport java.util.concurrent.Semaphore;\n\n/**\n * {@link RandomAccessData} implementation backed by a {@link RandomAccessFile}.\n *\n * @author Phillip Webb\n */\npublic class RandomAccessDataFile implements RandomAccessData {\n\n    private static final int DEFAULT_CONCURRENT_READS = 4;\n\n    private final File       file;\n\n    private final FilePool   filePool;\n\n    private final long       offset;\n\n    private final long       length;\n\n    /**\n     * Create a new {@link RandomAccessDataFile} backed by the specified file.\n     * @param file the underlying file\n     * @throws IllegalArgumentException if the file is null or does not exist\n     * @see #RandomAccessDataFile(File, int)\n     */\n    public RandomAccessDataFile(File file) {\n        this(file, DEFAULT_CONCURRENT_READS);\n    }\n\n    /**\n     * Create a new {@link RandomAccessDataFile} backed by the specified file.\n     * @param file the underlying file\n     * @param concurrentReads the maximum number of concurrent reads allowed on the\n     * underlying file before blocking\n     * @throws IllegalArgumentException if the file is null or does not exist\n     * @see #RandomAccessDataFile(File)\n     */\n    public RandomAccessDataFile(File file, int concurrentReads) {\n        if (file == null) {\n            throw new IllegalArgumentException(\"File must not be null\");\n        }\n        if (!file.exists()) {\n            throw new IllegalArgumentException(String.format(\"File must exist: %s\", file.getPath()));\n        }\n        this.file = file;\n        this.filePool = new FilePool(file, concurrentReads);\n        this.offset = 0L;\n        this.length = file.length();\n    }\n\n    /**\n     * Private constructor used to create a {@link #getSubsection(long, long) subsection}.\n     * @param file the underlying file\n     * @param pool the underlying pool\n     * @param offset the offset of the section\n     * @param length the length of the section\n     */\n    private RandomAccessDataFile(File file, FilePool pool, long offset, long length) {\n        this.file = file;\n        this.filePool = pool;\n        this.offset = offset;\n        this.length = length;\n    }\n\n    /**\n     * Returns the underlying File.\n     * @return the underlying file\n     */\n    public File getFile() {\n        return this.file;\n    }\n\n    @Override\n    public InputStream getInputStream(ResourceAccess access) throws IOException {\n        return new DataInputStream(access);\n    }\n\n    @Override\n    public RandomAccessData getSubsection(long offset, long length) {\n        if (offset < 0 || length < 0 || offset + length > this.length) {\n            throw new IndexOutOfBoundsException();\n        }\n        return new RandomAccessDataFile(this.file, this.filePool, this.offset + offset, length);\n    }\n\n    @Override\n    public long getSize() {\n        return this.length;\n    }\n\n    public void close() throws IOException {\n        this.filePool.close();\n    }\n\n    /**\n     * {@link RandomAccessDataFile}.\n     */\n    private class DataInputStream extends InputStream {\n\n        private RandomAccessFile file;\n\n        private int              position;\n\n        DataInputStream(ResourceAccess access) throws IOException {\n            if (access == ResourceAccess.ONCE) {\n                this.file = new RandomAccessFile(RandomAccessDataFile.this.file, \"r\");\n                this.file.seek(RandomAccessDataFile.this.offset);\n            }\n        }\n\n        @Override\n        public int read() throws IOException {\n            return doRead(null, 0, 1);\n        }\n\n        @Override\n        public int read(byte[] b) throws IOException {\n            return read(b, 0, b == null ? 0 : b.length);\n        }\n\n        @Override\n        public int read(byte[] b, int off, int len) throws IOException {\n            if (b == null) {\n                throw new NullPointerException(\"Bytes must not be null\");\n            }\n            return doRead(b, off, len);\n        }\n\n        /**\n         * Perform the actual read.\n         * @param b the bytes to read or {@code null} when reading a single byte\n         * @param off the offset of the byte array\n         * @param len the length of data to read\n         * @return the number of bytes read into {@code b} or the actual read byte if\n         * {@code b} is {@code null}. Returns -1 when the end of the stream is reached\n         * @throws IOException in case of I/O errors\n         */\n        public int doRead(byte[] b, int off, int len) throws IOException {\n            if (len == 0) {\n                return 0;\n            }\n            int cappedLen = cap(len);\n            if (cappedLen <= 0) {\n                return -1;\n            }\n            RandomAccessFile file = this.file;\n            try {\n                if (file == null) {\n                    file = RandomAccessDataFile.this.filePool.acquire();\n                    file.seek(RandomAccessDataFile.this.offset + this.position);\n                }\n                if (b == null) {\n                    int rtn = file.read();\n                    moveOn(rtn == -1 ? 0 : 1);\n                    return rtn;\n                } else {\n                    return (int) moveOn(file.read(b, off, cappedLen));\n                }\n            } finally {\n                if (this.file == null && file != null) {\n                    RandomAccessDataFile.this.filePool.release(file);\n                }\n            }\n        }\n\n        @Override\n        public long skip(long n) {\n            return (n <= 0 ? 0 : moveOn(cap(n)));\n        }\n\n        @Override\n        public void close() throws IOException {\n            if (this.file != null) {\n                this.file.close();\n            }\n        }\n\n        /**\n         * Cap the specified value such that it cannot exceed the number of bytes\n         * remaining.\n         * @param n the value to cap\n         * @return the capped value\n         */\n        private int cap(long n) {\n            return (int) Math.min(RandomAccessDataFile.this.length - this.position, n);\n        }\n\n        /**\n         * Move the stream position forwards the specified amount.\n         * @param amount the amount to move\n         * @return the amount moved\n         */\n        private long moveOn(int amount) {\n            this.position += amount;\n            return amount;\n        }\n\n    }\n\n    /**\n     * Manage a pool that can be used to perform concurrent reads on the underlying\n     * {@link RandomAccessFile}.\n     */\n    static class FilePool {\n\n        private final File                    file;\n\n        private final int                     size;\n\n        private final Semaphore               available;\n\n        private final Queue<RandomAccessFile> files;\n\n        FilePool(File file, int size) {\n            this.file = file;\n            this.size = size;\n            this.available = new Semaphore(size);\n            this.files = new ConcurrentLinkedQueue<>();\n        }\n\n        public RandomAccessFile acquire() throws IOException {\n            this.available.acquireUninterruptibly();\n            RandomAccessFile file = this.files.poll();\n            if (file != null) {\n                return file;\n            }\n            return new RandomAccessFile(this.file, \"r\");\n        }\n\n        public void release(RandomAccessFile file) {\n            this.files.add(file);\n            this.available.release();\n        }\n\n        public void close() throws IOException {\n            this.available.acquireUninterruptibly(this.size);\n            try {\n                RandomAccessFile pooledFile = this.files.poll();\n                while (pooledFile != null) {\n                    pooledFile.close();\n                    pooledFile = this.files.poll();\n                }\n            } finally {\n                this.available.release(this.size);\n            }\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/jar/AsciiBytes.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\nimport java.nio.charset.Charset;\n\n/**\n * Simple wrapper around a byte array that represents an ASCII. Used for performance\n * reasons to save constructing Strings for ZIP data.\n *\n * @author Phillip Webb\n * @author Andy Wilkinson\n */\nfinal public class AsciiBytes {\n\n    private static final Charset UTF_8 = Charset.forName(\"UTF-8\");\n\n    private final byte[]         bytes;\n\n    private final int            offset;\n\n    private final int            length;\n\n    private String               string;\n\n    private int                  hash;\n\n    /**\n     * Create a new {@link AsciiBytes} from the specified String.\n     * @param string the source string\n     */\n    public AsciiBytes(String string) {\n        this(string.getBytes(UTF_8));\n        this.string = string;\n    }\n\n    /**\n     * Create a new {@link AsciiBytes} from the specified bytes. NOTE: underlying bytes\n     * are not expected to change.\n     * @param bytes the source bytes\n     */\n    public AsciiBytes(byte[] bytes) {\n        this(bytes, 0, bytes.length);\n    }\n\n    /**\n     * Create a new {@link AsciiBytes} from the specified bytes. NOTE: underlying bytes\n     * are not expected to change.\n     * @param bytes the source bytes\n     * @param offset the offset\n     * @param length the length\n     */\n    public AsciiBytes(byte[] bytes, int offset, int length) {\n        if (offset < 0 || length < 0 || (offset + length) > bytes.length) {\n            throw new IndexOutOfBoundsException();\n        }\n        this.bytes = bytes;\n        this.offset = offset;\n        this.length = length;\n    }\n\n    public int length() {\n        return this.length;\n    }\n\n    public boolean startsWith(AsciiBytes prefix) {\n        if (this == prefix) {\n            return true;\n        }\n        if (prefix.length > this.length) {\n            return false;\n        }\n        for (int i = 0; i < prefix.length; i++) {\n            if (this.bytes[i + this.offset] != prefix.bytes[i + prefix.offset]) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    public boolean endsWith(AsciiBytes postfix) {\n        if (this == postfix) {\n            return true;\n        }\n        if (postfix.length > this.length) {\n            return false;\n        }\n        for (int i = 0; i < postfix.length; i++) {\n            if (this.bytes[this.offset + (this.length - 1) - i] != postfix.bytes[postfix.offset\n                                                                                 + (postfix.length - 1)\n                                                                                 - i]) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    public AsciiBytes substring(int beginIndex) {\n        return substring(beginIndex, this.length);\n    }\n\n    public AsciiBytes substring(int beginIndex, int endIndex) {\n        int length = endIndex - beginIndex;\n        if (this.offset + length > this.bytes.length) {\n            throw new IndexOutOfBoundsException();\n        }\n        return new AsciiBytes(this.bytes, this.offset + beginIndex, length);\n    }\n\n    public AsciiBytes append(String string) {\n        if (string == null || string.isEmpty()) {\n            return this;\n        }\n        return append(string.getBytes(UTF_8));\n    }\n\n    public AsciiBytes append(AsciiBytes asciiBytes) {\n        if (asciiBytes == null || asciiBytes.length() == 0) {\n            return this;\n        }\n        return append(asciiBytes.bytes);\n    }\n\n    public AsciiBytes append(byte[] bytes) {\n        if (bytes == null || bytes.length == 0) {\n            return this;\n        }\n        byte[] combined = new byte[this.length + bytes.length];\n        System.arraycopy(this.bytes, this.offset, combined, 0, this.length);\n        System.arraycopy(bytes, 0, combined, this.length, bytes.length);\n        return new AsciiBytes(combined);\n    }\n\n    @Override\n    public String toString() {\n        if (this.string == null) {\n            this.string = new String(this.bytes, this.offset, this.length, UTF_8);\n        }\n        return this.string;\n    }\n\n    @Override\n    public int hashCode() {\n        int hash = this.hash;\n        if (hash == 0 && this.bytes.length > 0) {\n            for (int i = this.offset; i < this.offset + this.length; i++) {\n                int b = this.bytes[i];\n                if (b < 0) {\n                    b = b & 0x7F;\n                    int limit;\n                    int excess = 0x80;\n                    if (b < 96) {\n                        limit = 1;\n                        excess += 0x40 << 6;\n                    } else if (b < 112) {\n                        limit = 2;\n                        excess += (0x60 << 12) + (0x80 << 6);\n                    } else {\n                        limit = 3;\n                        excess += (0x70 << 18) + (0x80 << 12) + (0x80 << 6);\n                    }\n                    for (int j = 0; j < limit; j++) {\n                        b = (b << 6) + (this.bytes[++i] & 0xFF);\n                    }\n                    b -= excess;\n                }\n                if (b <= 0xFFFF) {\n                    hash = 31 * hash + b;\n                } else {\n                    hash = 31 * hash + ((b >> 0xA) + 0xD7C0);\n                    hash = 31 * hash + ((b & 0x3FF) + 0xDC00);\n                }\n            }\n            this.hash = hash;\n        }\n        return hash;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (obj == null) {\n            return false;\n        }\n        if (this == obj) {\n            return true;\n        }\n        if (obj.getClass().equals(AsciiBytes.class)) {\n            AsciiBytes other = (AsciiBytes) obj;\n            if (this.length == other.length) {\n                for (int i = 0; i < this.length; i++) {\n                    if (this.bytes[this.offset + i] != other.bytes[other.offset + i]) {\n                        return false;\n                    }\n                }\n                return true;\n            }\n        }\n        return false;\n    }\n\n    static String toString(byte[] bytes) {\n        return new String(bytes, UTF_8);\n    }\n\n    public static int hashCode(String string) {\n        // We're compatible with String's hashCode().\n        return string.hashCode();\n    }\n\n    public static int hashCode(int hash, String string) {\n        for (int i = 0; i < string.length(); i++) {\n            hash = 31 * hash + string.charAt(i);\n        }\n        return hash;\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/jar/Bytes.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\nimport com.alipay.sofa.ark.loader.data.RandomAccessData;\nimport com.alipay.sofa.ark.loader.data.RandomAccessData.ResourceAccess;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\n/**\n * Utilities for dealing with bytes from ZIP files.\n *\n * @author Phillip Webb\n */\nfinal public class Bytes {\n\n    private static final byte[] EMPTY_BYTES = new byte[] {};\n\n    private Bytes() {\n    }\n\n    public static byte[] get(RandomAccessData data) throws IOException {\n        InputStream inputStream = data.getInputStream(ResourceAccess.ONCE);\n        try {\n            return get(inputStream, data.getSize());\n        } finally {\n            inputStream.close();\n        }\n    }\n\n    public static byte[] get(InputStream inputStream, long length) throws IOException {\n        if (length == 0) {\n            return EMPTY_BYTES;\n        }\n        byte[] bytes = new byte[(int) length];\n        if (!fill(inputStream, bytes)) {\n            throw new IOException(\"Unable to read bytes\");\n        }\n        return bytes;\n    }\n\n    public static boolean fill(InputStream inputStream, byte[] bytes) throws IOException {\n        return fill(inputStream, bytes, 0, bytes.length);\n    }\n\n    private static boolean fill(InputStream inputStream, byte[] bytes, int offset, int length)\n                                                                                              throws IOException {\n        while (length > 0) {\n            int read = inputStream.read(bytes, offset, length);\n            if (read == -1) {\n                return false;\n            }\n            offset += read;\n            length = -read;\n        }\n        return true;\n    }\n\n    public static long littleEndianValue(byte[] bytes, int offset, int length) {\n        long value = 0;\n        for (int i = length - 1; i >= 0; i--) {\n            value = ((value << 8) | (bytes[offset + i] & 0xFF));\n        }\n        return value;\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/jar/CentralDirectoryEndRecord.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\nimport com.alipay.sofa.ark.loader.data.RandomAccessData;\n\nimport java.io.IOException;\n\n/**\n * A ZIP File \"End of central directory record\" (EOCD).\n *\n * @author Phillip Webb\n * @author Andy Wilkinson\n * @see <a href=\"http://en.wikipedia.org/wiki/Zip_%28file_format%29\">Zip File Format</a>\n */\nfinal public class CentralDirectoryEndRecord {\n\n    private static final int MINIMUM_SIZE           = 22;\n\n    private static final int MAXIMUM_COMMENT_LENGTH = 0xFFFF;\n\n    private static final int MAXIMUM_SIZE           = MINIMUM_SIZE + MAXIMUM_COMMENT_LENGTH;\n\n    private static final int SIGNATURE              = 0x06054b50;\n\n    private static final int COMMENT_LENGTH_OFFSET  = 20;\n\n    private static final int READ_BLOCK_SIZE        = 256;\n\n    private byte[]           block;\n\n    private int              offset;\n\n    private int              size;\n\n    /**\n     * Create a new {@link CentralDirectoryEndRecord} instance from the specified\n     * {@link RandomAccessData}, searching backwards from the end until a valid block is\n     * located.\n     * @param data the source data\n     * @throws IOException in case of I/O errors\n     */\n    public CentralDirectoryEndRecord(RandomAccessData data) throws IOException {\n        this.block = createBlockFromEndOfData(data, READ_BLOCK_SIZE);\n        this.size = MINIMUM_SIZE;\n        this.offset = this.block.length - this.size;\n        while (!isValid()) {\n            this.size++;\n            if (this.size > this.block.length) {\n                if (this.size >= MAXIMUM_SIZE || this.size > data.getSize()) {\n                    throw new IOException(\"Unable to find ZIP central directory \"\n                                          + \"records after reading \" + this.size + \" bytes\");\n                }\n                this.block = createBlockFromEndOfData(data, this.size + READ_BLOCK_SIZE);\n            }\n            this.offset = this.block.length - this.size;\n        }\n    }\n\n    private byte[] createBlockFromEndOfData(RandomAccessData data, int size) throws IOException {\n        int length = (int) Math.min(data.getSize(), size);\n        return Bytes.get(data.getSubsection(data.getSize() - length, length));\n    }\n\n    public boolean isValid() {\n        if (this.block.length < MINIMUM_SIZE\n            || Bytes.littleEndianValue(this.block, this.offset, 4) != SIGNATURE) {\n            return false;\n        }\n        // Total size must be the structure size + comment\n        long commentLength = Bytes.littleEndianValue(this.block, this.offset\n                                                                 + COMMENT_LENGTH_OFFSET, 2);\n        return this.size == MINIMUM_SIZE + commentLength;\n    }\n\n    /**\n     * Returns the location in the data that the archive actually starts. For most files\n     * the archive data will start at 0, however, it is possible to have prefixed bytes\n     * (often used for startup scripts) at the beginning of the data.\n     * @param data the source data\n     * @return the offset within the data where the archive begins\n     */\n    public long getStartOfArchive(RandomAccessData data) {\n        long length = Bytes.littleEndianValue(this.block, this.offset + 12, 4);\n        long specifiedOffset = Bytes.littleEndianValue(this.block, this.offset + 16, 4);\n        long actualOffset = data.getSize() - this.size - length;\n        return actualOffset - specifiedOffset;\n    }\n\n    /**\n     * Return the bytes of the \"Central directory\" based on the offset indicated in this\n     * record.\n     * @param data the source data\n     * @return the central directory data\n     */\n    public RandomAccessData getCentralDirectory(RandomAccessData data) {\n        long offset = Bytes.littleEndianValue(this.block, this.offset + 16, 4);\n        long length = Bytes.littleEndianValue(this.block, this.offset + 12, 4);\n        return data.getSubsection(offset, length);\n    }\n\n    /**\n     * Return the number of ZIP entries in the file.\n     * @return the number of records in the zip\n     */\n    public int getNumberOfRecords() {\n        long numberOfRecords = Bytes.littleEndianValue(this.block, this.offset + 10, 2);\n        if (numberOfRecords == 0xFFFF) {\n            throw new IllegalStateException(\"Zip64 archives are not supported\");\n        }\n        return (int) numberOfRecords;\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/jar/CentralDirectoryFileHeader.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\nimport com.alipay.sofa.ark.loader.data.RandomAccessData;\n\nimport java.io.IOException;\nimport java.util.Calendar;\nimport java.util.GregorianCalendar;\n\n/**\n * A ZIP File \"Central directory file header record\" (CDFH).\n *\n * @author Phillip Webb\n * @author Andy Wilkinson\n * @see <a href=\"http://en.wikipedia.org/wiki/Zip_%28file_format%29\">Zip File Format</a>\n */\n\nfinal public class CentralDirectoryFileHeader implements FileHeader {\n\n    private static final AsciiBytes SLASH      = new AsciiBytes(\"/\");\n\n    private static final byte[]     NO_EXTRA   = {};\n\n    private static final AsciiBytes NO_COMMENT = new AsciiBytes(\"\");\n\n    private byte[]                  header;\n\n    private int                     headerOffset;\n\n    private AsciiBytes              name;\n\n    private byte[]                  extra;\n\n    private AsciiBytes              comment;\n\n    private long                    localHeaderOffset;\n\n    public CentralDirectoryFileHeader() {\n    }\n\n    public CentralDirectoryFileHeader(byte[] header, int headerOffset, AsciiBytes name,\n                                      byte[] extra, AsciiBytes comment, long localHeaderOffset) {\n        super();\n        this.header = header;\n        this.headerOffset = headerOffset;\n        this.name = name;\n        this.extra = extra;\n        this.comment = comment;\n        this.localHeaderOffset = localHeaderOffset;\n    }\n\n    public void load(byte[] data, int dataOffset, RandomAccessData variableData,\n                     int variableOffset, JarEntryFilter filter) throws IOException {\n        // Load fixed part\n        this.header = data;\n        this.headerOffset = dataOffset;\n        long nameLength = Bytes.littleEndianValue(data, dataOffset + 28, 2);\n        long extraLength = Bytes.littleEndianValue(data, dataOffset + 30, 2);\n        long commentLength = Bytes.littleEndianValue(data, dataOffset + 32, 2);\n        this.localHeaderOffset = Bytes.littleEndianValue(data, dataOffset + 42, 4);\n        // Load variable part\n        dataOffset += 46;\n        if (variableData != null) {\n            data = Bytes.get(variableData.getSubsection(variableOffset + 46, nameLength\n                                                                             + extraLength\n                                                                             + commentLength));\n            dataOffset = 0;\n        }\n        this.name = new AsciiBytes(data, dataOffset, (int) nameLength);\n        if (filter != null) {\n            this.name = filter.apply(this.name);\n        }\n        this.extra = NO_EXTRA;\n        this.comment = NO_COMMENT;\n        if (extraLength > 0) {\n            this.extra = new byte[(int) extraLength];\n            System.arraycopy(data, (int) (dataOffset + nameLength), this.extra, 0,\n                this.extra.length);\n        }\n        if (commentLength > 0) {\n            this.comment = new AsciiBytes(data, (int) (dataOffset + nameLength + extraLength),\n                (int) commentLength);\n        }\n    }\n\n    public AsciiBytes getName() {\n        return this.name;\n    }\n\n    @Override\n    public boolean hasName(String name, String suffix) {\n        return this.name.equals(new AsciiBytes(suffix == null ? name : name + suffix));\n    }\n\n    public boolean isDirectory() {\n        return this.name.endsWith(SLASH);\n    }\n\n    @Override\n    public int getMethod() {\n        return (int) Bytes.littleEndianValue(this.header, this.headerOffset + 10, 2);\n    }\n\n    public long getTime() {\n        long date = Bytes.littleEndianValue(this.header, this.headerOffset + 14, 2);\n        long time = Bytes.littleEndianValue(this.header, this.headerOffset + 12, 2);\n        return decodeMsDosFormatDateTime(date, time).getTimeInMillis();\n    }\n\n    /**\n     * Decode MS-DOS Date Time details. See\n     * <a href=\"http://mindprod.com/jgloss/zip.html\">mindprod.com/jgloss/zip.html</a> for\n     * more details of the format.\n     * @param date the date part\n     * @param time the time part\n     * @return a {@link Calendar} containing the decoded date.\n     */\n    private Calendar decodeMsDosFormatDateTime(long date, long time) {\n        int year = (int) ((date >> 9) & 0x7F) + 1980;\n        int month = (int) ((date >> 5) & 0xF) - 1;\n        int day = (int) (date & 0x1F);\n        int hours = (int) ((time >> 11) & 0x1F);\n        int minutes = (int) ((time >> 5) & 0x3F);\n        int seconds = (int) ((time << 1) & 0x3E);\n        return new GregorianCalendar(year, month, day, hours, minutes, seconds);\n    }\n\n    public long getCrc() {\n        return Bytes.littleEndianValue(this.header, this.headerOffset + 16, 4);\n    }\n\n    @Override\n    public long getCompressedSize() {\n        return Bytes.littleEndianValue(this.header, this.headerOffset + 20, 4);\n    }\n\n    @Override\n    public long getSize() {\n        return Bytes.littleEndianValue(this.header, this.headerOffset + 24, 4);\n    }\n\n    public byte[] getExtra() {\n        return this.extra;\n    }\n\n    public AsciiBytes getComment() {\n        return this.comment;\n    }\n\n    @Override\n    public long getLocalHeaderOffset() {\n        return this.localHeaderOffset;\n    }\n\n    @Override\n    public CentralDirectoryFileHeader clone() {\n        byte[] header = new byte[46];\n        System.arraycopy(this.header, this.headerOffset, header, 0, header.length);\n        return new CentralDirectoryFileHeader(header, 0, this.name, header, this.comment,\n            this.localHeaderOffset);\n    }\n\n    public static CentralDirectoryFileHeader fromRandomAccessData(RandomAccessData data,\n                                                                  int offset, JarEntryFilter filter)\n                                                                                                    throws IOException {\n        CentralDirectoryFileHeader fileHeader = new CentralDirectoryFileHeader();\n        byte[] bytes = Bytes.get(data.getSubsection(offset, 46));\n        fileHeader.load(bytes, 0, data, offset, filter);\n        return fileHeader;\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/jar/CentralDirectoryParser.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\nimport com.alipay.sofa.ark.loader.data.RandomAccessData;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Parses the central directory from a JAR file.\n *\n * @author Phillip Webb\n * @see CentralDirectoryVisitor\n */\nfinal public class CentralDirectoryParser {\n\n    private final static int                    CENTRAL_DIRECTORY_HEADER_BASE_SIZE = 46;\n\n    private final List<CentralDirectoryVisitor> visitors                           = new ArrayList<>();\n\n    public <T extends CentralDirectoryVisitor> T addVisitor(T visitor) {\n        this.visitors.add(visitor);\n        return visitor;\n    }\n\n    /**\n     * Parse the source data, triggering {@link CentralDirectoryVisitor visitors}.\n     * @param data the source data\n     * @param skipPrefixBytes if prefix bytes should be skipped\n     * @return The actual archive data without any prefix bytes\n     * @throws IOException on error\n     */\n    public RandomAccessData parse(RandomAccessData data, boolean skipPrefixBytes)\n                                                                                 throws IOException {\n        CentralDirectoryEndRecord endRecord = new CentralDirectoryEndRecord(data);\n        if (skipPrefixBytes) {\n            data = getArchiveData(endRecord, data);\n        }\n        RandomAccessData centralDirectoryData = endRecord.getCentralDirectory(data);\n        visitStart(endRecord, centralDirectoryData);\n        parseEntries(endRecord, centralDirectoryData);\n        visitEnd();\n        return data;\n    }\n\n    private void parseEntries(CentralDirectoryEndRecord endRecord,\n                              RandomAccessData centralDirectoryData) throws IOException {\n        byte[] bytes = Bytes.get(centralDirectoryData);\n        CentralDirectoryFileHeader fileHeader = new CentralDirectoryFileHeader();\n        int dataOffset = 0;\n        for (int i = 0; i < endRecord.getNumberOfRecords(); i++) {\n            fileHeader.load(bytes, dataOffset, null, 0, null);\n            visitFileHeader(dataOffset, fileHeader);\n            dataOffset += CENTRAL_DIRECTORY_HEADER_BASE_SIZE + fileHeader.getName().length()\n                          + fileHeader.getComment().length() + fileHeader.getExtra().length;\n        }\n    }\n\n    private RandomAccessData getArchiveData(CentralDirectoryEndRecord endRecord,\n                                            RandomAccessData data) {\n        long offset = endRecord.getStartOfArchive(data);\n        if (offset == 0) {\n            return data;\n        }\n        return data.getSubsection(offset, data.getSize() - offset);\n    }\n\n    private void visitStart(CentralDirectoryEndRecord endRecord,\n                            RandomAccessData centralDirectoryData) {\n        for (CentralDirectoryVisitor visitor : this.visitors) {\n            visitor.visitStart(endRecord, centralDirectoryData);\n        }\n    }\n\n    private void visitFileHeader(int dataOffset, CentralDirectoryFileHeader fileHeader) {\n        for (CentralDirectoryVisitor visitor : this.visitors) {\n            visitor.visitFileHeader(fileHeader, dataOffset);\n        }\n    }\n\n    private void visitEnd() {\n        for (CentralDirectoryVisitor visitor : this.visitors) {\n            visitor.visitEnd();\n        }\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/jar/CentralDirectoryVisitor.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\nimport com.alipay.sofa.ark.loader.data.RandomAccessData;\n\n/**\n * Callback visitor triggered by {@link CentralDirectoryParser}.\n *\n * @author Phillip Webb\n */\npublic interface CentralDirectoryVisitor {\n\n    void visitStart(CentralDirectoryEndRecord endRecord, RandomAccessData centralDirectoryData);\n\n    void visitFileHeader(CentralDirectoryFileHeader fileHeader, int dataOffset);\n\n    void visitEnd();\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/jar/FileHeader.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\nimport java.util.zip.ZipEntry;\n\n/**\n * A file header record that has been loaded from a Jar file.\n *\n * @author Phillip Webb\n * @see JarEntry\n * @see CentralDirectoryFileHeader\n */\ninterface FileHeader {\n\n    /**\n     * Returns {@code true} if the header has the given name.\n     * @param name the name to test\n     * @param suffix an additional suffix (or {@code null})\n     * @return {@code true} if the header has the given name\n     */\n    boolean hasName(String name, String suffix);\n\n    /**\n     * Return the offset of the load file header within the archive data.\n     * @return the local header offset\n     */\n    long getLocalHeaderOffset();\n\n    /**\n     * Return the compressed size of the entry.\n     * @return the compressed size.\n     */\n    long getCompressedSize();\n\n    /**\n     * Return the uncompressed size of the entry.\n     * @return the uncompressed size.\n     */\n    long getSize();\n\n    /**\n     * Return the method used to compress the data.\n     * @return the zip compression method\n     * @see ZipEntry#STORED\n     * @see ZipEntry#DEFLATED\n     */\n    int getMethod();\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/jar/Handler.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\nimport com.alipay.sofa.ark.common.util.FileUtils;\nimport com.alipay.sofa.ark.common.util.StringUtils;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.ref.SoftReference;\nimport java.lang.reflect.Method;\nimport java.net.*;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * {@link URLStreamHandler} for Spring Boot loader {@link JarFile}s.\n *\n * @author Phillip Webb\n * @author Andy Wilkinson\n * @see JarFile#registerUrlProtocolHandler()\n */\npublic class Handler extends URLStreamHandler {\n\n    // NOTE: in order to be found as a URL protocol handler, this class must be public,\n    // must be named Handler and must be in a package ending '.jar'\n\n    private static final String                      JAR_PROTOCOL      = \"jar:\";\n\n    private static final String                      FILE_PROTOCOL     = \"file:\";\n\n    private static final String                      SEPARATOR         = \"!/\";\n\n    private static final String[]                    FALLBACK_HANDLERS = { \"sun.net.www.protocol.jar.Handler\" };\n\n    private static final Method                      OPEN_CONNECTION_METHOD;\n\n    static {\n        Method method = null;\n        try {\n            method = URLStreamHandler.class.getDeclaredMethod(\"openConnection\", URL.class);\n        } catch (Exception ex) {\n            // Swallow and ignore\n        }\n        OPEN_CONNECTION_METHOD = method;\n    }\n\n    private static SoftReference<Map<File, JarFile>> rootFileCache;\n\n    static {\n        rootFileCache = new SoftReference<>(null);\n    }\n\n    private final JarFile                            jarFile;\n\n    private URLStreamHandler                         fallbackHandler;\n\n    public Handler() {\n        this(null);\n    }\n\n    public Handler(JarFile jarFile) {\n        this.jarFile = jarFile;\n    }\n\n    @Override\n    protected URLConnection openConnection(URL url) throws IOException {\n        if (this.jarFile != null) {\n            return JarURLConnection.get(url, this.jarFile);\n        }\n        try {\n            return JarURLConnection.get(url, getRootJarFileFromUrl(url));\n        } catch (Exception ex) {\n            return openFallbackConnection(url, ex);\n        }\n    }\n\n    private URLConnection openFallbackConnection(URL url, Exception reason) throws IOException {\n        try {\n            return openConnection(getFallbackHandler(), url);\n        } catch (Exception ex) {\n            if (reason instanceof IOException) {\n                log(false, ex);\n                throw (IOException) reason;\n            }\n            log(true, ex);\n            if (reason instanceof RuntimeException) {\n                throw (RuntimeException) reason;\n            }\n            throw new IllegalStateException(reason);\n        }\n    }\n\n    private void log(boolean warning, Exception cause) {\n        try {\n            Logger.getLogger(getClass().getName()).log((warning ? Level.WARNING : Level.FINEST),\n                \"Unable to open fallback handler\", cause);\n        } catch (Exception ex) {\n            if (warning) {\n                System.err.println(\"WARNING: \" + \"Unable to open fallback handler\"); //NOPMD\n            }\n        }\n    }\n\n    private URLStreamHandler getFallbackHandler() {\n        if (this.fallbackHandler != null) {\n            return this.fallbackHandler;\n        }\n        for (String handlerClassName : FALLBACK_HANDLERS) {\n            try {\n                Class<?> handlerClass = Class.forName(handlerClassName);\n                this.fallbackHandler = (URLStreamHandler) handlerClass.newInstance();\n                return this.fallbackHandler;\n            } catch (Exception ex) {\n                // Ignore\n            }\n        }\n        throw new IllegalStateException(\"Unable to find fallback handler\");\n    }\n\n    private URLConnection openConnection(URLStreamHandler handler, URL url) throws Exception {\n        if (OPEN_CONNECTION_METHOD == null) {\n            throw new IllegalStateException(\"Unable to invoke fallback open connection method\");\n        }\n        OPEN_CONNECTION_METHOD.setAccessible(true);\n        return (URLConnection) OPEN_CONNECTION_METHOD.invoke(handler, url);\n    }\n\n    @Override\n    protected void parseURL(URL context, String spec, int start, int limit) {\n        if (StringUtils.startWithToLowerCase(spec, JAR_PROTOCOL)) {\n            setFile(context, getFileFromSpec(spec.substring(start, limit)));\n        } else {\n            setFile(context, getFileFromContext(context, spec.substring(start, limit)));\n        }\n    }\n\n    private String getFileFromSpec(String spec) {\n        int separatorIndex = spec.lastIndexOf(\"!/\");\n        if (separatorIndex == -1) {\n            throw new IllegalArgumentException(\"No !/ in spec '\" + spec + \"'\");\n        }\n        try {\n            new URL(spec.substring(0, separatorIndex));\n            return spec;\n        } catch (MalformedURLException ex) {\n            throw new IllegalArgumentException(\"Invalid spec URL '\" + spec + \"'\", ex);\n        }\n    }\n\n    private String getFileFromContext(URL context, String spec) {\n        String file = context.getFile();\n        StringBuilder sb = new StringBuilder(file.length() + spec.length());\n        if (spec.startsWith(\"/\")) {\n            return sb.append(trimToJarRoot(file)).append(SEPARATOR).append(spec.substring(1))\n                .toString();\n        }\n        if (file.endsWith(\"/\")) {\n            return sb.append(file).append(spec).toString();\n        }\n        int lastSlashIndex = file.lastIndexOf('/');\n        if (lastSlashIndex == -1) {\n            throw new IllegalArgumentException(\"No / found in context URL's file '\" + file + \"'\");\n        }\n        return sb.append(file.substring(0, lastSlashIndex + 1)).append(spec).toString();\n    }\n\n    private String trimToJarRoot(String file) {\n        int lastSeparatorIndex = file.lastIndexOf(SEPARATOR);\n        if (lastSeparatorIndex == -1) {\n            throw new IllegalArgumentException(\"No !/ found in context URL's file '\" + file + \"'\");\n        }\n        return file.substring(0, lastSeparatorIndex);\n    }\n\n    private void setFile(URL context, String file) {\n        setURL(context, JAR_PROTOCOL, null, -1, null, null, normalize(file), null, null);\n    }\n\n    private String normalize(String file) {\n        int afterLastSeparatorIndex = file.lastIndexOf(SEPARATOR) + SEPARATOR.length();\n        String afterSeparator = file.substring(afterLastSeparatorIndex);\n        afterSeparator = replaceParentDir(afterSeparator);\n        afterSeparator = replaceCurrentDir(afterSeparator);\n        return new StringBuilder(afterLastSeparatorIndex + afterSeparator.length())\n            .append(file.substring(0, afterLastSeparatorIndex)).append(afterSeparator).toString();\n    }\n\n    String replaceParentDir(String file) {\n        int parentDirIndex;\n        while ((parentDirIndex = file.indexOf(\"/../\")) >= 0) {\n            int precedingSlashIndex = file.lastIndexOf('/', parentDirIndex - 1);\n            if (precedingSlashIndex >= 0) {\n                file = file.substring(0, precedingSlashIndex) + file.substring(parentDirIndex + 3);\n            } else {\n                file = file.substring(parentDirIndex + 4);\n            }\n        }\n        return file;\n    }\n\n    private String replaceCurrentDir(String file) {\n        return file.replace(\"/./\", \"/\");\n    }\n\n    @Override\n    protected int hashCode(URL u) {\n        return hashCode(u.getProtocol(), u.getFile());\n    }\n\n    private int hashCode(String protocol, String file) {\n        int result = (protocol == null ? 0 : protocol.hashCode());\n        int separatorIndex = file.indexOf(SEPARATOR);\n        if (separatorIndex == -1) {\n            return result + file.hashCode();\n        }\n        String source = file.substring(0, separatorIndex);\n        String entry = canonicalize(file.substring(separatorIndex + 2));\n        try {\n            result += new URL(source).hashCode();\n        } catch (MalformedURLException ex) {\n            result += source.hashCode();\n        }\n        result += entry.hashCode();\n        return result;\n    }\n\n    @Override\n    protected boolean sameFile(URL u1, URL u2) {\n        if (!\"jar\".equals(u1.getProtocol()) || !\"jar\".equals(u2.getProtocol())) {\n            return false;\n        }\n        int separator1 = u1.getFile().indexOf(SEPARATOR);\n        int separator2 = u2.getFile().indexOf(SEPARATOR);\n        if (separator1 == -1 || separator2 == -1) {\n            return super.sameFile(u1, u2);\n        }\n        String nested1 = u1.getFile().substring(separator1 + SEPARATOR.length());\n        String nested2 = u2.getFile().substring(separator2 + SEPARATOR.length());\n        if (!nested1.equals(nested2)) {\n            String canonical1 = canonicalize(nested1);\n            String canonical2 = canonicalize(nested2);\n            if (!canonical1.equals(canonical2)) {\n                return false;\n            }\n        }\n        String root1 = u1.getFile().substring(0, separator1);\n        String root2 = u2.getFile().substring(0, separator2);\n        try {\n            return super.sameFile(new URL(root1), new URL(root2));\n        } catch (MalformedURLException ex) {\n            // Continue\n        }\n        return super.sameFile(u1, u2);\n    }\n\n    private String canonicalize(String path) {\n        return path.replace(SEPARATOR, \"/\");\n    }\n\n    public JarFile getRootJarFileFromUrl(URL url) throws IOException {\n        String spec = url.getFile();\n        int separatorIndex = spec.indexOf(SEPARATOR);\n        if (separatorIndex == -1) {\n            throw new MalformedURLException(\"Jar URL does not contain !/ separator\");\n        }\n        String name = spec.substring(0, separatorIndex);\n        return getRootJarFile(name);\n    }\n\n    private JarFile getRootJarFile(String name) throws IOException {\n        try {\n            if (!name.startsWith(FILE_PROTOCOL)) {\n                throw new IllegalStateException(\"Not a file URL\");\n            }\n            String path = name.substring(FILE_PROTOCOL.length());\n            File file = FileUtils.file(path);\n            Map<File, JarFile> cache = rootFileCache.get();\n            JarFile result = (cache == null ? null : cache.get(file));\n            if (result == null) {\n                result = new JarFile(file);\n                addToRootFileCache(file, result);\n            }\n            return result;\n        } catch (Exception ex) {\n            throw new IOException(\"Unable to open root Jar file '\" + name + \"'\", ex);\n        }\n    }\n\n    /**\n     * Add the given {@link JarFile} to the root file cache.\n     * @param sourceFile the source file to add\n     * @param jarFile the jar file.\n     */\n    static void addToRootFileCache(File sourceFile, JarFile jarFile) {\n        Map<File, JarFile> cache = rootFileCache.get();\n        if (cache == null) {\n            cache = new ConcurrentHashMap<>(16);\n            rootFileCache = new SoftReference<>(cache);\n        }\n        cache.put(sourceFile, jarFile);\n    }\n\n    /**\n     * Set if a generic static exception can be thrown when a URL cannot be connected.\n     * This optimization is used during class loading to save creating lots of exceptions\n     * which are then swallowed.\n     * @param useFastConnectionExceptions if fast connection exceptions can be used.\n     */\n    public static void setUseFastConnectionExceptions(boolean useFastConnectionExceptions) {\n        JarURLConnection.setUseFastExceptions(useFastConnectionExceptions);\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/jar/JarEntry.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\nimport java.io.IOException;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.security.CodeSigner;\nimport java.security.cert.Certificate;\nimport java.util.jar.Attributes;\nimport java.util.jar.Manifest;\n\n/**\n * Extended variant of {@link java.util.jar.JarEntry} returned by {@link JarFile}s.\n *\n * @author Phillip Webb\n */\npublic class JarEntry extends java.util.jar.JarEntry implements FileHeader {\n\n    private Certificate[] certificates;\n\n    private CodeSigner[]  codeSigners;\n\n    private final JarFile jarFile;\n\n    private long          localHeaderOffset;\n\n    JarEntry(JarFile jarFile, CentralDirectoryFileHeader header) {\n        super(header.getName().toString());\n        this.jarFile = jarFile;\n        this.localHeaderOffset = header.getLocalHeaderOffset();\n        setCompressedSize(header.getCompressedSize());\n        setMethod(header.getMethod());\n        setCrc(header.getCrc());\n        setSize(header.getSize());\n        setExtra(header.getExtra());\n        setComment(header.getComment().toString());\n        setSize(header.getSize());\n        setTime(header.getTime());\n    }\n\n    @Override\n    public boolean hasName(String name, String suffix) {\n        return getName().length() == name.length() + suffix.length() && getName().startsWith(name)\n               && getName().endsWith(suffix);\n    }\n\n    /**\n     * Return a {@link URL} for this {@link JarEntry}.\n     * @return the URL for the entry\n     * @throws MalformedURLException if the URL is not valid\n     */\n    URL getUrl() throws MalformedURLException {\n        return new URL(this.jarFile.getUrl(), getName());\n    }\n\n    @Override\n    public Attributes getAttributes() throws IOException {\n        Manifest manifest = this.jarFile.getManifest();\n        return (manifest == null ? null : manifest.getAttributes(getName()));\n    }\n\n    @Override\n    public Certificate[] getCertificates() {\n        if (this.jarFile.isSigned() && this.certificates == null) {\n            this.jarFile.setupEntryCertificates(this);\n        }\n        return this.certificates;\n    }\n\n    @Override\n    public CodeSigner[] getCodeSigners() {\n        if (this.jarFile.isSigned() && this.codeSigners == null) {\n            this.jarFile.setupEntryCertificates(this);\n        }\n        return this.codeSigners;\n    }\n\n    void setCertificates(java.util.jar.JarEntry entry) {\n        this.certificates = entry.getCertificates();\n        this.codeSigners = entry.getCodeSigners();\n    }\n\n    @Override\n    public long getLocalHeaderOffset() {\n        return this.localHeaderOffset;\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/jar/JarEntryFilter.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\n/**\n * Interface that can be used to filter and optionally rename jar entries.\n *\n * @author Phillip Webb\n */\npublic interface JarEntryFilter {\n\n    /**\n     * Apply the jar entry filter.\n     * @param name the current entry name. This may be different that the original entry\n     * name if a previous filter has been applied\n     * @return the new name of the entry or {@code null} if the entry should not be\n     * included.\n     */\n    AsciiBytes apply(AsciiBytes name);\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/jar/JarFile.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\nimport com.alipay.sofa.ark.loader.data.RandomAccessData;\nimport com.alipay.sofa.ark.loader.data.RandomAccessData.ResourceAccess;\nimport com.alipay.sofa.ark.loader.data.RandomAccessDataFile;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.lang.ref.SoftReference;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URLStreamHandler;\nimport java.net.URLStreamHandlerFactory;\nimport java.util.Enumeration;\nimport java.util.Iterator;\nimport java.util.Properties;\nimport java.util.jar.JarInputStream;\nimport java.util.jar.Manifest;\nimport java.util.zip.ZipEntry;\n\n/**\n * Extended variant of {@link java.util.jar.JarFile} that behaves in the same way but\n * offers the following additional functionality.\n * <ul>\n * <li>A nested {@link JarFile} can be {@link #getNestedJarFile(ZipEntry) obtained} based\n * on any directory entry.</li>\n * <li>A nested {@link JarFile} can be {@link #getNestedJarFile(ZipEntry) obtained} for\n * embedded JAR files (as long as their entry is not compressed).</li>\n * </ul>\n *\n * @author Phillip Webb\n */\npublic class JarFile extends java.util.jar.JarFile {\n\n    private static final String        MANIFEST_NAME            = \"META-INF/MANIFEST.MF\";\n\n    private static final String        POM_PROPERTIES           = \"pom.properties\";\n\n    private static final String        PROTOCOL_HANDLER         = \"java.protocol.handler.pkgs\";\n\n    private static final String        HANDLERS_PACKAGE         = \"com.alipay.sofa.ark.loader\";\n\n    private static final AsciiBytes    META_INF                 = new AsciiBytes(\"META-INF/\");\n\n    private static final AsciiBytes    SIGNATURE_FILE_EXTENSION = new AsciiBytes(\".SF\");\n\n    private final RandomAccessDataFile rootFile;\n\n    private final String               pathFromRoot;\n\n    private final RandomAccessData     data;\n\n    private final JarFileType          type;\n\n    private URL                        url;\n\n    private JarFileEntries             entries;\n\n    private SoftReference<Manifest>    manifest;\n\n    private boolean                    signed;\n\n    /**\n     * Create a new {@link JarFile} backed by the specified file.\n     * @param file the root jar file\n     * @throws IOException if the file cannot be read\n     */\n    public JarFile(File file) throws IOException {\n        this(new RandomAccessDataFile(file));\n    }\n\n    /**\n     * Create a new {@link JarFile} backed by the specified file.\n     * @param file the root jar file\n     * @throws IOException if the file cannot be read\n     */\n    JarFile(RandomAccessDataFile file) throws IOException {\n        this(file, \"\", file, JarFileType.DIRECT);\n    }\n\n    /**\n     * Private constructor used to create a new {@link JarFile} either directly or from a\n     * nested entry.\n     * @param rootFile the root jar file\n     * @param pathFromRoot the name of this file\n     * @param data the underlying data\n     * @param type the type of the jar file\n     * @throws IOException if the file cannot be read\n     */\n    private JarFile(RandomAccessDataFile rootFile, String pathFromRoot, RandomAccessData data,\n                    JarFileType type) throws IOException {\n        this(rootFile, pathFromRoot, data, null, type);\n    }\n\n    private JarFile(RandomAccessDataFile rootFile, String pathFromRoot, RandomAccessData data,\n                    JarEntryFilter filter, JarFileType type) throws IOException {\n        super(rootFile.getFile());\n        this.rootFile = rootFile;\n        this.pathFromRoot = pathFromRoot;\n        CentralDirectoryParser parser = new CentralDirectoryParser();\n        this.entries = parser.addVisitor(new JarFileEntries(this, filter));\n        parser.addVisitor(centralDirectoryVisitor());\n        this.data = parser.parse(data, filter == null);\n        this.type = type;\n    }\n\n    private CentralDirectoryVisitor centralDirectoryVisitor() {\n        return new CentralDirectoryVisitor() {\n\n            @Override\n            public void visitStart(CentralDirectoryEndRecord endRecord,\n                                   RandomAccessData centralDirectoryData) {\n            }\n\n            @Override\n            public void visitFileHeader(CentralDirectoryFileHeader fileHeader, int dataOffset) {\n                AsciiBytes name = fileHeader.getName();\n                if (name.startsWith(META_INF) && name.endsWith(SIGNATURE_FILE_EXTENSION)) {\n                    JarFile.this.signed = true;\n                }\n            }\n\n            @Override\n            public void visitEnd() {\n            }\n\n        };\n    }\n\n    protected final RandomAccessDataFile getRootJarFile() {\n        return this.rootFile;\n    }\n\n    RandomAccessData getData() {\n        return this.data;\n    }\n\n    @Override\n    public Manifest getManifest() throws IOException {\n        Manifest manifest = (this.manifest == null ? null : this.manifest.get());\n        if (manifest == null) {\n            if (this.type == JarFileType.NESTED_DIRECTORY) {\n                try (JarFile jarFile = new JarFile(this.getRootJarFile())) {\n                    manifest = jarFile.getManifest();\n                }\n            } else {\n                try (InputStream inputStream = getInputStream(MANIFEST_NAME, ResourceAccess.ONCE)) {\n                    if (inputStream == null) {\n                        return null;\n                    }\n                    manifest = new Manifest(inputStream);\n                }\n            }\n            this.manifest = new SoftReference<>(manifest);\n        }\n        return manifest;\n    }\n\n    public Properties getPomProperties() throws IOException {\n        Enumeration<java.util.jar.JarEntry> entries = this.entries();\n\n        Properties p = new Properties();\n        while (entries.hasMoreElements()) {\n            java.util.jar.JarEntry jarEntry = entries.nextElement();\n            if (jarEntry.getName().endsWith(POM_PROPERTIES)) {\n                try (InputStream is = this.getInputStream(jarEntry)) {\n                    p.load(is);\n                    return p;\n                }\n            }\n        }\n        return p;\n    }\n\n    @Override\n    public Enumeration<java.util.jar.JarEntry> entries() {\n        final Iterator<JarEntry> iterator = this.entries.iterator();\n        return new Enumeration<java.util.jar.JarEntry>() {\n\n            @Override\n            public boolean hasMoreElements() {\n                return iterator.hasNext();\n            }\n\n            @Override\n            public java.util.jar.JarEntry nextElement() {\n                return iterator.next();\n            }\n\n        };\n    }\n\n    @Override\n    public JarEntry getJarEntry(String name) {\n        return (JarEntry) getEntry(name);\n    }\n\n    public boolean containsEntry(String name) {\n        return this.entries.containsEntry(name);\n    }\n\n    @Override\n    public ZipEntry getEntry(String name) {\n        return this.entries.getEntry(name);\n    }\n\n    @Override\n    public synchronized InputStream getInputStream(ZipEntry ze) throws IOException {\n        return getInputStream(ze, ResourceAccess.PER_READ);\n    }\n\n    public InputStream getInputStream(ZipEntry ze, ResourceAccess access) throws IOException {\n        if (ze instanceof JarEntry) {\n            return this.entries.getInputStream((JarEntry) ze, access);\n        }\n        return getInputStream(ze == null ? null : ze.getName(), access);\n    }\n\n    InputStream getInputStream(String name, ResourceAccess access) throws IOException {\n        return this.entries.getInputStream(name, access);\n    }\n\n    /**\n     * Return a nested {@link JarFile} loaded from the specified entry.\n     * @param entry the zip entry\n     * @return a {@link JarFile} for the entry\n     * @throws IOException if the nested jar file cannot be read\n     */\n    public synchronized JarFile getNestedJarFile(final ZipEntry entry) throws IOException {\n        return getNestedJarFile((JarEntry) entry);\n    }\n\n    /**\n     * Return a nested {@link JarFile} loaded from the specified entry.\n     * @param entry the zip entry\n     * @return a {@link JarFile} for the entry\n     * @throws IOException if the nested jar file cannot be read\n     */\n    public synchronized JarFile getNestedJarFile(JarEntry entry) throws IOException {\n        try {\n            return createJarFileFromEntry(entry);\n        } catch (Exception ex) {\n            throw new IOException(\"Unable to open nested jar file '\" + entry.getName() + \"'\", ex);\n        }\n    }\n\n    private JarFile createJarFileFromEntry(JarEntry entry) throws IOException {\n        if (entry.isDirectory()) {\n            return createJarFileFromDirectoryEntry(entry);\n        }\n        return createJarFileFromFileEntry(entry);\n    }\n\n    private JarFile createJarFileFromDirectoryEntry(JarEntry entry) throws IOException {\n        final AsciiBytes sourceName = new AsciiBytes(entry.getName());\n        JarEntryFilter filter = new JarEntryFilter() {\n\n            @Override\n            public AsciiBytes apply(AsciiBytes name) {\n                if (name.startsWith(sourceName) && !name.equals(sourceName)) {\n                    return name.substring(sourceName.length());\n                }\n                return null;\n            }\n\n        };\n        return new JarFile(this.rootFile, this.pathFromRoot + \"!/\"\n                                          + entry.getName().substring(0, sourceName.length() - 1),\n            this.data, filter, JarFileType.NESTED_DIRECTORY);\n    }\n\n    private JarFile createJarFileFromFileEntry(JarEntry entry) throws IOException {\n        if (entry.getMethod() != ZipEntry.STORED) {\n            throw new IllegalStateException(\n                \"Unable to open nested entry '\" + entry.getName()\n                        + \"'. It has been compressed and nested \"\n                        + \"jar files must be stored without compression. Please check the \"\n                        + \"mechanism used to create your executable jar file\");\n        }\n        RandomAccessData entryData = this.entries.getEntryData(entry.getName());\n        return new JarFile(this.rootFile, this.pathFromRoot + \"!/\" + entry.getName(), entryData,\n            JarFileType.NESTED_JAR);\n    }\n\n    @Override\n    public int size() {\n        return (int) this.data.getSize();\n    }\n\n    @Override\n    public void close() throws IOException {\n        super.close();\n        this.rootFile.close();\n    }\n\n    /**\n     * Return a URL that can be used to access this JAR file. NOTE: the specified URL\n     * cannot be serialized and or cloned.\n     * @return the URL\n     * @throws MalformedURLException if the URL is malformed\n     */\n    public URL getUrl() throws MalformedURLException {\n        if (this.url == null) {\n            Handler handler = new Handler(this);\n            String file = this.rootFile.getFile().toURI() + this.pathFromRoot + \"!/\";\n            file = file.replace(\"file:////\", \"file://\"); // Fix UNC paths\n            this.url = new URL(\"jar\", \"\", -1, file, handler);\n\n        }\n        return this.url;\n    }\n\n    @Override\n    public String toString() {\n        return getName();\n    }\n\n    @Override\n    public String getName() {\n        String str = this.rootFile.getFile().toString();\n        StringBuilder sb = new StringBuilder(str.length() + this.pathFromRoot.length());\n        return sb.append(str).append(this.pathFromRoot).toString();\n    }\n\n    boolean isSigned() {\n        return this.signed;\n    }\n\n    void setupEntryCertificates(JarEntry entry) {\n        // Fallback to JarInputStream to obtain certificates, not fast but hopefully not\n        // happening that often.\n        try {\n            JarInputStream inputStream = new JarInputStream(getData().getInputStream(\n                ResourceAccess.ONCE));\n            try {\n                java.util.jar.JarEntry certEntry = inputStream.getNextJarEntry();\n                while (certEntry != null) {\n                    inputStream.closeEntry();\n                    if (entry.getName().equals(certEntry.getName())) {\n                        setCertificates(entry, certEntry);\n                    }\n                    setCertificates(getJarEntry(certEntry.getName()), certEntry);\n                    certEntry = inputStream.getNextJarEntry();\n                }\n            } finally {\n                inputStream.close();\n            }\n        } catch (IOException ex) {\n            throw new IllegalStateException(ex);\n        }\n    }\n\n    private void setCertificates(JarEntry entry, java.util.jar.JarEntry certEntry) {\n        if (entry != null) {\n            entry.setCertificates(certEntry);\n        }\n    }\n\n    public void clearCache() {\n        this.entries.clearCache();\n    }\n\n    protected String getPathFromRoot() {\n        return this.pathFromRoot;\n    }\n\n    JarFileType getType() {\n        return this.type;\n    }\n\n    /**\n     * Register a {@literal 'java.protocol.handler.pkgs'} property so that a\n     * {@link URLStreamHandler} will be located to deal with jar URLs.\n     */\n    public static void registerUrlProtocolHandler() {\n        String handlers = System.getProperty(PROTOCOL_HANDLER, \"\");\n        System.setProperty(PROTOCOL_HANDLER, (\"\".equals(handlers) ? HANDLERS_PACKAGE\n            : handlers + \"|\" + HANDLERS_PACKAGE));\n        resetCachedUrlHandlers();\n    }\n\n    /**\n     * Reset any cached handlers just in case a jar protocol has already been used. We\n     * reset the handler by trying to set a null {@link URLStreamHandlerFactory} which\n     * should have no effect other than clearing the handlers cache.\n     */\n    private static void resetCachedUrlHandlers() {\n        try {\n            URL.setURLStreamHandlerFactory(null);\n        } catch (Error ex) {\n            // Ignore\n        }\n    }\n\n    /**\n     * The type of a {@link JarFile}.\n     */\n    enum JarFileType {\n        DIRECT, NESTED_DIRECTORY, NESTED_JAR\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/jar/JarFileEntries.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\nimport com.alipay.sofa.ark.loader.data.RandomAccessData;\nimport com.alipay.sofa.ark.loader.data.RandomAccessData.ResourceAccess;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.*;\nimport java.util.zip.ZipEntry;\n\n/**\n * Provides access to entries from a {@link JarFile}. In order to reduce memory\n * consumption entry details are stored using int arrays. The {@code hashCodes} array\n * stores the hash code of the entry name, the {@code centralDirectoryOffsets} provides\n * the offset to the central directory record and {@code positions} provides the original\n * order position of the entry. The arrays are stored in hashCode order so that a binary\n * search can be used to find a name.\n * <p>\n * A typical Spring Boot application will have somewhere in the region of 10,500 entries\n * which should consume about 122K.\n *\n * @author Phillip Webb\n */\npublic class JarFileEntries implements CentralDirectoryVisitor, Iterable<JarEntry> {\n\n    private static final long              LOCAL_FILE_HEADER_SIZE = 30;\n\n    private static final String            SLASH                  = \"/\";\n\n    private static final String            NO_SUFFIX              = \"\";\n\n    protected static final int             ENTRY_CACHE_SIZE       = 25;\n\n    private final JarFile                  jarFile;\n\n    private final JarEntryFilter           filter;\n\n    private RandomAccessData               centralDirectoryData;\n\n    private int                            size;\n\n    private int[]                          hashCodes;\n\n    private int[]                          centralDirectoryOffsets;\n\n    private int[]                          positions;\n\n    private final Map<Integer, FileHeader> entriesCache           = Collections\n                                                                      .synchronizedMap(new LinkedHashMap<Integer, FileHeader>(\n                                                                          16, 0.75f, true) {\n\n                                                                          @Override\n                                                                          protected boolean removeEldestEntry(Map.Entry<Integer, FileHeader> eldest) {\n                                                                              if (JarFileEntries.this.jarFile\n                                                                                  .isSigned()) {\n                                                                                  return false;\n                                                                              }\n                                                                              return size() >= ENTRY_CACHE_SIZE;\n                                                                          }\n\n                                                                      });\n\n    public JarFileEntries(JarFile jarFile, JarEntryFilter filter) {\n        this.jarFile = jarFile;\n        this.filter = filter;\n    }\n\n    @Override\n    public void visitStart(CentralDirectoryEndRecord endRecord,\n                           RandomAccessData centralDirectoryData) {\n        int maxSize = endRecord.getNumberOfRecords();\n        this.centralDirectoryData = centralDirectoryData;\n        this.hashCodes = new int[maxSize];\n        this.centralDirectoryOffsets = new int[maxSize];\n        this.positions = new int[maxSize];\n    }\n\n    @Override\n    public void visitFileHeader(CentralDirectoryFileHeader fileHeader, int dataOffset) {\n        AsciiBytes name = applyFilter(fileHeader.getName());\n        if (name != null) {\n            add(name, fileHeader, dataOffset);\n        }\n    }\n\n    private void add(AsciiBytes name, CentralDirectoryFileHeader fileHeader, int dataOffset) { //NOPMD\n        this.hashCodes[this.size] = name.hashCode();\n        this.centralDirectoryOffsets[this.size] = dataOffset;\n        this.positions[this.size] = this.size;\n        this.size++;\n    }\n\n    @Override\n    public void visitEnd() {\n        sort(0, this.size - 1);\n        int[] positions = this.positions;\n        this.positions = new int[positions.length];\n        for (int i = 0; i < this.size; i++) {\n            this.positions[positions[i]] = i;\n        }\n    }\n\n    private void sort(int left, int right) {\n        // Quick sort algorithm, uses hashCodes as the source but sorts all arrays\n        if (left < right) {\n            int pivot = this.hashCodes[left + (right - left) / 2];\n            int i = left;\n            int j = right;\n            while (i <= j) {\n                while (this.hashCodes[i] < pivot) {\n                    i++;\n                }\n                while (this.hashCodes[j] > pivot) {\n                    j--;\n                }\n                if (i <= j) {\n                    swap(i, j);\n                    i++;\n                    j--;\n                }\n            }\n            if (left < j) {\n                sort(left, j);\n            }\n            if (right > i) {\n                sort(i, right);\n            }\n        }\n    }\n\n    private void swap(int i, int j) {\n        swap(this.hashCodes, i, j);\n        swap(this.centralDirectoryOffsets, i, j);\n        swap(this.positions, i, j);\n    }\n\n    private void swap(int[] array, int i, int j) {\n        int temp = array[i];\n        array[i] = array[j];\n        array[j] = temp;\n    }\n\n    @Override\n    public Iterator<JarEntry> iterator() {\n        return new EntryIterator();\n    }\n\n    public boolean containsEntry(String name) {\n        return getEntry(name, FileHeader.class, true) != null;\n    }\n\n    public JarEntry getEntry(String name) {\n        return getEntry(name, JarEntry.class, true);\n    }\n\n    public InputStream getInputStream(String name, ResourceAccess access) throws IOException {\n        FileHeader entry = getEntry(name, FileHeader.class, false);\n        return getInputStream(entry, access);\n    }\n\n    public InputStream getInputStream(FileHeader entry, ResourceAccess access) throws IOException {\n        if (entry == null) {\n            return null;\n        }\n        InputStream inputStream = getEntryData(entry).getInputStream(access);\n        if (entry.getMethod() == ZipEntry.DEFLATED) {\n            inputStream = new ZipInflaterInputStream(inputStream, (int) entry.getSize());\n        }\n        return inputStream;\n    }\n\n    public RandomAccessData getEntryData(String name) throws IOException {\n        FileHeader entry = getEntry(name, FileHeader.class, false);\n        if (entry == null) {\n            return null;\n        }\n        return getEntryData(entry);\n    }\n\n    private RandomAccessData getEntryData(FileHeader entry) throws IOException {\n        // aspectjrt-1.7.4.jar has a different ext bytes length in the\n        // local directory to the central directory. We need to re-read\n        // here to skip them\n        RandomAccessData data = this.jarFile.getData();\n        byte[] localHeader = Bytes.get(data.getSubsection(entry.getLocalHeaderOffset(),\n            LOCAL_FILE_HEADER_SIZE));\n        long nameLength = Bytes.littleEndianValue(localHeader, 26, 2);\n        long extraLength = Bytes.littleEndianValue(localHeader, 28, 2);\n        return data.getSubsection(entry.getLocalHeaderOffset() + LOCAL_FILE_HEADER_SIZE\n                                  + nameLength + extraLength, entry.getCompressedSize());\n    }\n\n    private <T extends FileHeader> T getEntry(String name, Class<T> type, boolean cacheEntry) {\n        int hashCode = AsciiBytes.hashCode(name);\n        T entry = getEntry(hashCode, name, NO_SUFFIX, type, cacheEntry);\n        if (entry == null) {\n            hashCode = AsciiBytes.hashCode(hashCode, SLASH);\n            entry = getEntry(hashCode, name, SLASH, type, cacheEntry);\n        }\n        return entry;\n    }\n\n    private <T extends FileHeader> T getEntry(int hashCode, String name, String suffix,\n                                              Class<T> type, boolean cacheEntry) {\n        int index = getFirstIndex(hashCode);\n        while (index >= 0 && index < this.size && this.hashCodes[index] == hashCode) {\n            T entry = getEntry(index, type, cacheEntry);\n            if (entry.hasName(name, suffix)) {\n                return entry;\n            }\n            index++;\n        }\n        return null;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private <T extends FileHeader> T getEntry(int index, Class<T> type, boolean cacheEntry) {\n        try {\n            FileHeader cached = this.entriesCache.get(index);\n            FileHeader entry = (cached != null ? cached : CentralDirectoryFileHeader\n                .fromRandomAccessData(this.centralDirectoryData,\n                    this.centralDirectoryOffsets[index], this.filter));\n            if (CentralDirectoryFileHeader.class.equals(entry.getClass())\n                && type.equals(JarEntry.class)) {\n                entry = new JarEntry(this.jarFile, (CentralDirectoryFileHeader) entry);\n            }\n            if (cacheEntry && cached != entry) {\n                this.entriesCache.put(index, entry);\n            }\n            return (T) entry;\n        } catch (IOException ex) {\n            throw new IllegalStateException(ex);\n        }\n    }\n\n    private int getFirstIndex(int hashCode) {\n        int index = Arrays.binarySearch(this.hashCodes, 0, this.size, hashCode);\n        if (index < 0) {\n            return -1;\n        }\n        while (index > 0 && this.hashCodes[index - 1] == hashCode) {\n            index--;\n        }\n        return index;\n    }\n\n    public void clearCache() {\n        this.entriesCache.clear();\n    }\n\n    private AsciiBytes applyFilter(AsciiBytes name) {\n        return (this.filter == null ? name : this.filter.apply(name));\n    }\n\n    /**\n     * Iterator for contained entries.\n     */\n    private class EntryIterator implements Iterator<JarEntry> {\n\n        private int index = 0;\n\n        @Override\n        public boolean hasNext() {\n            return this.index < JarFileEntries.this.size;\n        }\n\n        @Override\n        public JarEntry next() {\n            if (!hasNext()) {\n                throw new NoSuchElementException();\n            }\n            int entryIndex = JarFileEntries.this.positions[this.index];\n            this.index++;\n            return getEntry(entryIndex, JarEntry.class, false);\n        }\n\n        @Override\n        public void remove() {\n            throw new UnsupportedOperationException(\"remove\");\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/jar/JarURLConnection.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\nimport com.alipay.sofa.ark.loader.data.RandomAccessData.ResourceAccess;\n\nimport java.io.*;\nimport java.net.*;\nimport java.security.Permission;\n\n/**\n * {@link java.net.JarURLConnection} used to support {@link JarFile#getUrl()}.\n *\n * @author Phillip Webb\n * @author Andy Wilkinson\n * @author Rostyslav Dudka\n */\nfinal public class JarURLConnection extends java.net.JarURLConnection {\n\n    private static ThreadLocal<Boolean>        useFastExceptions              = new ThreadLocal<>();\n\n    private static final FileNotFoundException FILE_NOT_FOUND_EXCEPTION       = new FileNotFoundException(\n                                                                                  \"Jar file or entry not found\");\n\n    private static final IllegalStateException NOT_FOUND_CONNECTION_EXCEPTION = new IllegalStateException(\n                                                                                  FILE_NOT_FOUND_EXCEPTION);\n\n    private static final String                SEPARATOR                      = \"!/\";\n\n    private static final URL                   EMPTY_JAR_URL;\n\n    static {\n        try {\n            EMPTY_JAR_URL = new URL(\"jar:\", null, 0, \"file:!/\", new URLStreamHandler() {\n                @Override\n                protected URLConnection openConnection(URL u) {\n                    // Stub URLStreamHandler to prevent the wrong JAR Handler from being\n                    // Instantiated and cached.\n                    return null;\n                }\n            });\n        } catch (MalformedURLException ex) {\n            throw new IllegalStateException(ex);\n        }\n    }\n\n    private static final JarEntryName          EMPTY_JAR_ENTRY_NAME           = new JarEntryName(\"\");\n\n    private static final String                READ_ACTION                    = \"read\";\n\n    private static final JarURLConnection      NOT_FOUND_CONNECTION           = JarURLConnection\n                                                                                  .notFound();\n\n    private final JarFile                      jarFile;\n\n    private Permission                         permission;\n\n    private URL                                jarFileUrl;\n\n    private final JarEntryName                 jarEntryName;\n\n    private JarEntry                           jarEntry;\n\n    private JarURLConnection(URL url, JarFile jarFile, JarEntryName jarEntryName)\n                                                                                 throws IOException {\n        // What we pass to super is ultimately ignored\n        super(EMPTY_JAR_URL);\n        this.url = url;\n        this.jarFile = jarFile;\n        this.jarEntryName = jarEntryName;\n    }\n\n    @Override\n    public void connect() throws IOException {\n        if (this.jarFile == null) {\n            throw FILE_NOT_FOUND_EXCEPTION;\n        }\n        if (!this.jarEntryName.isEmpty() && this.jarEntry == null) {\n            this.jarEntry = this.jarFile.getJarEntry(getEntryName());\n            if (this.jarEntry == null) {\n                throwFileNotFound(this.jarEntryName, this.jarFile);\n            }\n        }\n        this.connected = true;\n    }\n\n    @Override\n    public JarFile getJarFile() throws IOException {\n        connect();\n        return this.jarFile;\n    }\n\n    @Override\n    public URL getJarFileURL() {\n        if (this.jarFile == null) {\n            throw NOT_FOUND_CONNECTION_EXCEPTION;\n        }\n        if (this.jarFileUrl == null) {\n            this.jarFileUrl = buildJarFileUrl();\n        }\n        return this.jarFileUrl;\n    }\n\n    private URL buildJarFileUrl() {\n        try {\n            String spec = this.jarFile.getUrl().getFile();\n            if (spec.endsWith(SEPARATOR)) {\n                spec = spec.substring(0, spec.length() - SEPARATOR.length());\n            }\n            if (!spec.contains(SEPARATOR)) {\n                return new URL(spec);\n            }\n            return new URL(\"jar:\" + spec);\n        } catch (MalformedURLException ex) {\n            throw new IllegalStateException(ex);\n        }\n    }\n\n    @Override\n    public JarEntry getJarEntry() throws IOException {\n        if (this.jarEntryName == null || this.jarEntryName.isEmpty()) {\n            return null;\n        }\n        connect();\n        return this.jarEntry;\n    }\n\n    @Override\n    public String getEntryName() {\n        if (this.jarFile == null) {\n            throw NOT_FOUND_CONNECTION_EXCEPTION;\n        }\n        return this.jarEntryName.toString();\n    }\n\n    @Override\n    public InputStream getInputStream() throws IOException {\n        if (this.jarFile == null) {\n            throw FILE_NOT_FOUND_EXCEPTION;\n        }\n        if (this.jarEntryName.isEmpty() && this.jarFile.getType() == JarFile.JarFileType.DIRECT) {\n            throw new IOException(\"no entry name specified\");\n        }\n        connect();\n        InputStream inputStream = (this.jarEntryName.isEmpty() ? this.jarFile.getData()\n            .getInputStream(ResourceAccess.ONCE) : this.jarFile.getInputStream(this.jarEntry));\n        if (inputStream == null) {\n            throwFileNotFound(this.jarEntryName, this.jarFile);\n        }\n        return inputStream;\n    }\n\n    private void throwFileNotFound(Object entry, JarFile jarFile) throws FileNotFoundException {\n        if (Boolean.TRUE.equals(useFastExceptions.get())) {\n            throw FILE_NOT_FOUND_EXCEPTION;\n        }\n        throw new FileNotFoundException(\"JAR entry \" + entry + \" not found in \" + jarFile.getName());\n    }\n\n    @Override\n    public int getContentLength() {\n        long length = getContentLengthLong();\n        if (length > Integer.MAX_VALUE) {\n            return -1;\n        }\n        return (int) length;\n    }\n\n    @Override\n    public long getContentLengthLong() {\n        if (this.jarFile == null) {\n            return -1;\n        }\n        try {\n            if (this.jarEntryName.isEmpty()) {\n                return this.jarFile.size();\n            }\n            JarEntry entry = getJarEntry();\n            return (entry == null ? -1 : (int) entry.getSize());\n        } catch (IOException ex) {\n            return -1;\n        }\n    }\n\n    @Override\n    public Object getContent() throws IOException {\n        connect();\n        return (this.jarEntryName.isEmpty() ? this.jarFile : super.getContent());\n    }\n\n    @Override\n    public String getContentType() {\n        return (this.jarEntryName == null ? null : this.jarEntryName.getContentType());\n    }\n\n    @Override\n    public Permission getPermission() throws IOException {\n        if (this.jarFile == null) {\n            throw FILE_NOT_FOUND_EXCEPTION;\n        }\n        if (this.permission == null) {\n            this.permission = new FilePermission(this.jarFile.getRootJarFile().getFile().getPath(),\n                READ_ACTION);\n        }\n        return this.permission;\n    }\n\n    @Override\n    public long getLastModified() {\n        if (this.jarFile == null || this.jarEntryName.isEmpty()) {\n            return 0;\n        }\n        try {\n            JarEntry entry = getJarEntry();\n            return (entry == null ? 0 : entry.getTime());\n        } catch (IOException ex) {\n            return 0;\n        }\n    }\n\n    static void setUseFastExceptions(boolean useFastExceptions) {\n        JarURLConnection.useFastExceptions.set(useFastExceptions);\n    }\n\n    static JarURLConnection get(URL url, JarFile jarFile) throws IOException {\n        String spec = extractFullSpec(url, jarFile.getPathFromRoot());\n        int separator;\n        int index = 0;\n        while ((separator = spec.indexOf(SEPARATOR, index)) > 0) {\n            String entryName = spec.substring(index, separator);\n            JarEntry jarEntry = jarFile.getJarEntry(entryName);\n            if (jarEntry == null) {\n                return JarURLConnection.notFound(jarFile, JarEntryName.get(entryName));\n            }\n            jarFile = jarFile.getNestedJarFile(jarEntry);\n            index = separator + SEPARATOR.length();\n        }\n        JarEntryName jarEntryName = JarEntryName.get(spec, index);\n        if (Boolean.TRUE.equals(useFastExceptions.get())) {\n            if (!jarEntryName.isEmpty() && !jarFile.containsEntry(jarEntryName.toString())) {\n                return NOT_FOUND_CONNECTION;\n            }\n        }\n        return new JarURLConnection(url, jarFile, jarEntryName);\n    }\n\n    private static String extractFullSpec(URL url, String pathFromRoot) {\n        String file = url.getFile();\n        int separatorIndex = file.indexOf(SEPARATOR);\n        if (separatorIndex < 0) {\n            return \"\";\n        }\n        int specIndex = separatorIndex + SEPARATOR.length() + pathFromRoot.length();\n        return file.substring(specIndex);\n    }\n\n    private static JarURLConnection notFound() {\n        try {\n            return notFound(null, null);\n        } catch (IOException ex) {\n            throw new IllegalStateException(ex);\n        }\n    }\n\n    private static JarURLConnection notFound(JarFile jarFile, JarEntryName jarEntryName)\n                                                                                        throws IOException {\n        if (Boolean.TRUE.equals(useFastExceptions.get())) {\n            return NOT_FOUND_CONNECTION;\n        }\n        return new JarURLConnection(null, jarFile, jarEntryName);\n    }\n\n    /**\n     * A JarEntryName parsed from a URL String.\n     */\n    static class JarEntryName {\n\n        private final String name;\n\n        private String       contentType;\n\n        JarEntryName(String spec) {\n            this.name = decode(spec);\n        }\n\n        private String decode(String source) {\n            if (source.indexOf('%') < 0) {\n                return source;\n            }\n            ByteArrayOutputStream bos = new ByteArrayOutputStream(source.length());\n            write(source, bos);\n            // AsciiBytes is what is used to store the JarEntries so make it symmetric\n            return AsciiBytes.toString(bos.toByteArray());\n        }\n\n        private void write(String source, ByteArrayOutputStream outputStream) {\n            int length = source.length();\n            for (int i = 0; i < length; i++) {\n                int c = source.charAt(i);\n                if (c > 127) {\n                    try {\n                        String encoded = URLEncoder.encode(String.valueOf((char) c), \"UTF-8\");\n                        write(encoded, outputStream);\n                    } catch (UnsupportedEncodingException ex) {\n                        throw new IllegalStateException(ex);\n                    }\n                } else {\n                    if (c == '%') {\n                        if ((i + 2) >= length) {\n                            throw new IllegalArgumentException(\"Invalid encoded sequence \\\"\"\n                                                               + source.substring(i) + \"\\\"\");\n                        }\n                        c = decodeEscapeSequence(source, i);\n                        i += 2;\n                    }\n                    outputStream.write(c);\n                }\n            }\n        }\n\n        private char decodeEscapeSequence(String source, int i) {\n            int hi = Character.digit(source.charAt(i + 1), 16);\n            int lo = Character.digit(source.charAt(i + 2), 16);\n            if (hi == -1 || lo == -1) {\n                throw new IllegalArgumentException(\"Invalid encoded sequence \\\"\"\n                                                   + source.substring(i) + \"\\\"\");\n            }\n            return ((char) ((hi << 4) + lo));\n        }\n\n        @Override\n        public String toString() {\n            return this.name;\n        }\n\n        public boolean isEmpty() {\n            return this.name.isEmpty();\n        }\n\n        public String getContentType() {\n            if (this.contentType == null) {\n                this.contentType = deduceContentType();\n            }\n            return this.contentType;\n        }\n\n        private String deduceContentType() {\n            // Guess the content type, don't bother with streams as mark is not supported\n            String type = (isEmpty() ? \"x-java/jar\" : null);\n            type = (type != null ? type : guessContentTypeFromName(toString()));\n            type = (type != null ? type : \"content/unknown\");\n            return type;\n        }\n\n        public static JarEntryName get(String spec) {\n            return get(spec, 0);\n        }\n\n        public static JarEntryName get(String spec, int beginIndex) {\n            if (spec.length() <= beginIndex) {\n                return EMPTY_JAR_ENTRY_NAME;\n            }\n            return new JarEntryName(spec.substring(beginIndex));\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/jar/JarUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\nimport com.alipay.sofa.ark.common.util.FileUtils;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.loader.archive.JarFileArchive;\nimport com.alipay.sofa.ark.loader.util.ModifyPathUtils;\nimport org.apache.maven.model.Model;\nimport org.apache.maven.model.io.xpp3.MavenXpp3Reader;\n\nimport java.io.*;\nimport java.nio.file.Files;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.jar.JarEntry;\n\npublic class JarUtils {\n    private static final String                        CLASSPATH_ROOT_IDENTITY          = \"/target/classes\";\n\n    private static final String                        TEST_CLASSPATH_ROOT_IDENTITY     = \"/target/test-classes\";\n    private static final String                        TARGET_ROOT_IDENTITY             = \"/target/\";\n\n    private static final String                        JAR_POM_PROPERTIES_RELATIVE_PATH = \"maven-archiver/pom.properties\";\n    private static final String                        JAR_ARTIFACT_ID                  = \"artifactId\";\n\n    private static final String                        JAR_POM_PROPERTIES               = \"pom.properties\";\n\n    private static final String                        POM_FILE                         = \"/pom.xml\";\n\n    private static final String                        VERSION_REGEX                    = \"^([0-9]+\\\\.)+.+\";\n\n    private static final MavenXpp3Reader               READER                           = new MavenXpp3Reader();\n\n    public static final String                         JAR_SEPARATOR                    = \"!/\";\n\n    public static final String                         JAR_SUFFIX                       = \".jar\";\n\n    public static final String                         JAR_UNPACK                       = \".jar-unpack\";\n\n    private static final Map<String, Optional<String>> artifactIdCacheMap               = new ConcurrentHashMap<>();\n\n    static File searchPomProperties(File dirOrFile) {\n        if (dirOrFile == null || !dirOrFile.exists()) {\n            return null;\n        }\n        if (dirOrFile.isFile() && JAR_POM_PROPERTIES.equals(dirOrFile.getName())) {\n            return dirOrFile;\n        }\n        if (dirOrFile.isDirectory()) {\n            File[] files = dirOrFile.listFiles();\n\n            if (files != null) {\n                for (File file : files) {\n                    File result = searchPomProperties(file);\n                    if (result != null) {\n                        return result;\n                    }\n                }\n            }\n        }\n        return null;\n    }\n\n    static String getArtifactIdFromLocalClassPath(String fileClassPath) {\n\n        String libraryFile = fileClassPath.replace(\"file:\", \"\");\n        // 1. search pom.properties\n        int classesRootIndex = libraryFile.endsWith(CLASSPATH_ROOT_IDENTITY) ? libraryFile\n            .indexOf(CLASSPATH_ROOT_IDENTITY) : libraryFile.indexOf(CLASSPATH_ROOT_IDENTITY + \"/\");\n        int testClassesRootIndex = libraryFile.endsWith(TEST_CLASSPATH_ROOT_IDENTITY) ? libraryFile\n            .indexOf(TEST_CLASSPATH_ROOT_IDENTITY) : libraryFile\n            .indexOf(TEST_CLASSPATH_ROOT_IDENTITY + \"/\");\n        String pomPropertiesPath;\n        String pomXmlPath = null;\n        if (classesRootIndex != -1) {\n            pomPropertiesPath = libraryFile.substring(0,\n                classesRootIndex + TARGET_ROOT_IDENTITY.length())\n                                + JAR_POM_PROPERTIES_RELATIVE_PATH;\n            pomXmlPath = libraryFile.substring(0, classesRootIndex) + POM_FILE;\n        } else if (testClassesRootIndex != -1) {\n            pomPropertiesPath = libraryFile.substring(0, testClassesRootIndex\n                                                         + TARGET_ROOT_IDENTITY.length())\n                                + JAR_POM_PROPERTIES_RELATIVE_PATH;\n            pomXmlPath = libraryFile.substring(0, testClassesRootIndex) + POM_FILE;\n        } else {\n            // is not from test classpath, for example install uncompressed modules, just return null\n            // search for pom.properties\n            File pomPropertiesFile = searchPomProperties(FileUtils.file(libraryFile));\n            if (pomPropertiesFile != null && pomPropertiesFile.exists()) {\n                pomPropertiesPath = pomPropertiesFile.getAbsolutePath();\n            } else {\n                // not found pom.properties\n                pomPropertiesPath = null;\n            }\n        }\n\n        String artifactId = null;\n        if (!StringUtils.isEmpty(pomPropertiesPath)) {\n            try (InputStream inputStream = Files.newInputStream(FileUtils.file(pomPropertiesPath)\n                .toPath())) {\n                Properties properties = new Properties();\n                properties.load(inputStream);\n                artifactId = properties.getProperty(JAR_ARTIFACT_ID);\n            } catch (IOException e) {\n                // ignore\n            }\n        }\n\n        if (StringUtils.isEmpty(artifactId) && !StringUtils.isEmpty(pomXmlPath)) {\n            try (FileReader fileReader = new FileReader(pomXmlPath)) {\n                Model model = READER.read(fileReader);\n                return model.getArtifactId();\n            } catch (Exception e) {\n                // ignore\n            }\n        }\n        return artifactId;\n    }\n\n    public static String parseArtifactId(String jarLocation) {\n        // 1. /xxx/xxx/xx.jar!/\n        // 2. /xxx/xxx/xx.jar!/xxxx.class\n        // 3. /xxx/xxx/xx.jar\n        // 4. /xxx/xxx/xxx-bootstrap-1.0.0-ark-biz.jar!/BOOT-INF/lib/spring-boot-2.4.13.jar!/\n        // 5. /xxx/xxx-bootstrap-1.0.0-ark-biz.jar!/BOOT-INF/lib/sofa-ark-springboot-starter-2.1.1.jar!/META-INF/spring.factories\n        // 6. /xxx/xxx/target/classes/xxxx.jar\n        // 7. /xxx/xxx/target/test-classes/yyy/yyy/\n        // 8. /xxx/xxx/xxx-starter-1.0.0-SNAPSHOT.jar!/BOOT-INF/lib/xxx2-starter-1.1.4-SNAPSHOT-ark-biz.jar!/lib/xxx3-230605-sofa.jar!/\n        // 9. if is ark plugin, then return null to set declared default\n        //10. use unpack model, file   /xxx/xxx/xxx-0.0.1-ark-biz.jar-unpack/\n        \n        // Check if it's an unpacked directory\n        if (jarLocation.contains(JAR_UNPACK)) {\n            // Try to extract artifactId from the unpacked directory\n            String artifactId = parseArtifactIdFromUnpackedDir(jarLocation);\n            if (artifactId != null) {\n                return artifactId;\n            }\n            // If failed, fallback to extracting from directory name\n            return doGetArtifactIdFromFileName(jarLocation);\n        }\n        \n        // For non-unpacked paths, clean the jar location prefix and suffix\n        if (jarLocation.contains(JAR_SUFFIX)) {\n            jarLocation = jarLocation.substring(0, jarLocation.lastIndexOf(JAR_SUFFIX) + JAR_SUFFIX.length());\n        }\n        if (jarLocation.startsWith(\"file:\")) {\n            jarLocation = jarLocation.substring(\"file:\".length());\n        }\n\n        // modify the path to suit WindowsOS\n        jarLocation = ModifyPathUtils.modifyPath(jarLocation);\n        String finalJarLocation = jarLocation;\n        artifactIdCacheMap.computeIfAbsent(jarLocation, a -> {\n            try {\n                String artifactId;\n                String[] as = a.split(JAR_SEPARATOR, -1);\n                if (as.length == 1) {\n                    // no '!/'\n                    if (a.endsWith(\".jar\")) {\n                        artifactId = parseArtifactIdFromJar(a);\n                        if (StringUtils.isEmpty(artifactId)) {\n                            artifactId = doGetArtifactIdFromFileName(a);\n                        }\n                    } else {\n                        artifactId = getArtifactIdFromLocalClassPath(a);\n                    }\n                } else {\n                    // contains one '!/' or more\n                    artifactId = parseArtifactIdFromJar(a);\n                    if (StringUtils.isEmpty(artifactId)) {\n                        artifactId = doGetArtifactIdFromFileName(a);\n                    }\n                }\n                return Optional.ofNullable(artifactId);\n            } catch (IOException e) {\n                throw new RuntimeException(String.format(\"Failed to parse artifact id from jar %s.\",\n                        finalJarLocation), e);\n            }\n\n        });\n        return artifactIdCacheMap.get(jarLocation).orElse(null);\n    }\n\n    private static String doGetArtifactIdFromFileName(String jarLocation) {\n        String[] jarInfos = jarLocation.split(\"/\");\n        if (jarInfos.length == 0) {\n            return null;\n        }\n        String artifactVersion = jarInfos[jarInfos.length - 1];\n        String[] artifactVersionInfos = artifactVersion.split(\"-\");\n        List<String> artifactInfos = new ArrayList<>();\n        boolean getVersion = false;\n        for (String info : artifactVersionInfos) {\n            if (!StringUtils.isEmpty(info) && info.matches(VERSION_REGEX)) {\n                getVersion = true;\n                break;\n            }\n            artifactInfos.add(info);\n        }\n        if (getVersion) {\n            return String.join(\"-\", artifactInfos);\n        }\n        // if can't find any version from jar name, then we just return null to paas the declared check\n        return null;\n    }\n\n    private static String parseArtifactIdFromJar(String jarLocation) throws IOException {\n        try (com.alipay.sofa.ark.loader.jar.JarFile jarFile = getNestedRootJarFromJarLocation(jarLocation)) {\n            JarFileArchive jarFileArchive = new JarFileArchive(jarFile);\n            return jarFileArchive.getPomProperties().getProperty(JAR_ARTIFACT_ID);\n        }\n    }\n\n    public static com.alipay.sofa.ark.loader.jar.JarFile getNestedRootJarFromJarLocation(String jarLocation)\n                                                                                                            throws IOException {\n        //  /xxx/xxx/xxx-starter-1.0.0-SNAPSHOT.jar!/BOOT-INF/lib/xxx2-starter-1.1.4-SNAPSHOT-ark-biz.jar!/lib/xxx3-230605-sofa.jar\n        String[] js = jarLocation.split(JAR_SEPARATOR, -1);\n        com.alipay.sofa.ark.loader.jar.JarFile rJarFile = new com.alipay.sofa.ark.loader.jar.JarFile(\n            FileUtils.file(js[0]));\n        for (int i = 1; i < js.length; i++) {\n            String jPath = js[i];\n            if (StringUtils.isEmpty(jPath) || !jPath.endsWith(\".jar\")) {\n                break;\n            }\n            try {\n                JarEntry jarEntry = rJarFile.getJarEntry(jPath);\n                rJarFile = rJarFile.getNestedJarFile(jarEntry);\n            } catch (NullPointerException e) {\n                throw new IOException(\n                    String.format(\"Failed to parse artifact id, jPath: %s\", jPath), e);\n            }\n        }\n        return rJarFile;\n    }\n\n    private static String parseArtifactIdFromUnpackedDir(String unpackDirPath) {\n        File unpackDir = new File(unpackDirPath);\n        if (!unpackDir.exists() || !unpackDir.isDirectory()) {\n            return null;\n        }\n\n        // Look for pom.properties in maven-archiver directory\n        File pomPropsFile = searchPomProperties(unpackDir);\n        if (pomPropsFile != null && pomPropsFile.exists()) {\n            try (InputStream inputStream = Files.newInputStream(pomPropsFile.toPath())) {\n                Properties properties = new Properties();\n                properties.load(inputStream);\n                String artifactId = properties.getProperty(JAR_ARTIFACT_ID);\n                if (artifactId != null && !artifactId.isEmpty()) {\n                    return artifactId;\n                }\n            } catch (IOException e) {\n                throw new RuntimeException(String.format(\n                    \"Failed to parse artifact id from path %s.\", unpackDirPath), e);\n            }\n        }\n\n        return null;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/jar/ZipInflaterInputStream.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\nimport java.io.EOFException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.zip.Inflater;\nimport java.util.zip.InflaterInputStream;\n\n/**\n * {@link InflaterInputStream} that supports the writing of an extra \"dummy\" byte (which\n * is required with JDK 6) and returns accurate available() results.\n *\n * @author Phillip Webb\n */\npublic class ZipInflaterInputStream extends InflaterInputStream {\n\n    private boolean extraBytesWritten;\n\n    private int     available;\n\n    ZipInflaterInputStream(InputStream inputStream, int size) {\n        super(inputStream, new Inflater(true), getInflaterBufferSize(size));\n        this.available = size;\n    }\n\n    @Override\n    public int available() throws IOException {\n        if (this.available < 0) {\n            return super.available();\n        }\n        return this.available;\n    }\n\n    @Override\n    public int read(byte[] b, int off, int len) throws IOException {\n        int result = super.read(b, off, len);\n        if (result != -1) {\n            this.available -= result;\n        }\n        return result;\n    }\n\n    @Override\n    public void close() throws IOException {\n        super.close();\n        this.inf.end();\n    }\n\n    @Override\n    protected void fill() throws IOException {\n        try {\n            super.fill();\n        } catch (EOFException ex) {\n            if (this.extraBytesWritten) {\n                throw ex;\n            }\n            this.len = 1;\n            this.buf[0] = 0x0;\n            this.extraBytesWritten = true;\n            this.inf.setInput(this.buf, 0, this.len);\n        }\n    }\n\n    private static int getInflaterBufferSize(long size) {\n        // inflater likes some space\n        size += 2;\n        size = (size > 65536 ? 8192 : size);\n        size = (size <= 0 ? 4096 : size);\n        return (int) size;\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/main/java/com/alipay/sofa/ark/loader/util/ModifyPathUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.util;\n\n/**\n * @author sususama\n * @since 2023/5/8\n */\npublic class ModifyPathUtils {\n    /** When using SofaArk in the Windows environment, you will encounter a path error.\n     * This tool class will judge the read file or the configured path,\n     * and judge whether it is the file path of the Windows operating system.\n     * If it is, this method will modify the path to suit WindowsOS,\n     * otherwise the input path will be returned directly.\n     *\n     * @param path File Path\n     * @return Modified file path\n     */\n    public static String modifyPath(String path) {\n        if (path.charAt(2) == ':') {\n            path = path.substring(1);\n        }\n        return path;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/bootstrap/ArkLauncherTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.bootstrap;\n\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.loader.EmbedClassPathArchive;\nimport com.alipay.sofa.ark.loader.archive.JarFileArchive;\nimport com.alipay.sofa.ark.spi.archive.Archive;\nimport org.junit.AfterClass;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.mockito.MockedStatic;\nimport org.mockito.Mockito;\n\nimport java.io.File;\nimport java.lang.management.ManagementFactory;\nimport java.lang.management.RuntimeMXBean;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport static com.alipay.sofa.ark.bootstrap.ArkLauncher.main;\nimport static org.junit.Assert.*;\nimport static org.mockito.Mockito.mockStatic;\nimport static org.mockito.Mockito.when;\n\n/**\n * @author yan\n * @version ArkLauncherTest.java, v 0.1 2023年10月17日 20:28 yan\n */\npublic class ArkLauncherTest {\n\n    static MockedStatic<ManagementFactory> managementFactoryMockedStatic;\n\n    @BeforeClass\n    public static void setup() {\n\n        List<String> mockArguments = new ArrayList<>();\n        String filePath = ClasspathLauncherTest.class.getClassLoader()\n                .getResource(\"SampleClass.class\").getPath();\n        String workingPath = new File(filePath).getParent();\n        mockArguments.add(String.format(\"-javaagent:%s\", workingPath));\n\n        RuntimeMXBean runtimeMXBean = Mockito.mock(RuntimeMXBean.class);\n        when(runtimeMXBean.getInputArguments()).thenReturn(mockArguments);\n\n        managementFactoryMockedStatic = mockStatic(ManagementFactory.class);\n        managementFactoryMockedStatic.when(ManagementFactory::getRuntimeMXBean).thenReturn(runtimeMXBean);\n    }\n\n    @AfterClass\n    public static void tearDown() {\n        managementFactoryMockedStatic.close();\n    }\n\n    @Test\n    public void testContainerClassLoader() throws Exception {\n\n        URL url = this.getClass().getClassLoader().getResource(\"sample-springboot-fat-biz.jar\");\n        URL[] agentUrl = ClassLoaderUtils.getAgentClassPath();\n        assertEquals(1, agentUrl.length);\n\n        List<URL> urls = new ArrayList<>();\n        JarFileArchive jarFileArchive = new JarFileArchive(new File(url.getFile()));\n        List<Archive> archives = jarFileArchive.getNestedArchives(this::isNestedArchive);\n        for (Archive archive : archives) {\n            urls.add(archive.getUrl());\n        }\n        urls.addAll(Arrays.asList(agentUrl));\n\n        EmbedClassPathArchive classPathArchive = new EmbedClassPathArchive(\n                this.getClass().getCanonicalName(), null, urls.toArray(new URL[]{}));\n        ArkLauncher arkLauncher = new ArkLauncher(classPathArchive);\n        ClassLoader classLoader = arkLauncher.createContainerClassLoader(classPathArchive.getContainerArchive());\n        assertNotNull(classLoader);\n\n        try {\n            Class clazz = classLoader.loadClass(\"com.alipay.sofa.ark.bootstrap.ArkLauncher\");\n            assertNotNull(clazz);\n            clazz = classLoader.loadClass(\"SampleClass\");\n            assertNotNull(clazz);\n        } catch (Exception e) {\n            assertTrue(\"loadClass class failed \", false);\n        }\n\n        assertThrows(ClassNotFoundException.class, () -> classLoader.loadClass(\"NotExistClass\"));\n    }\n\n    protected boolean isNestedArchive(Archive.Entry entry) {\n        return entry.isDirectory() ? entry.getName().equals(\"BOOT-INF/classes/\") : entry.getName()\n            .startsWith(\"BOOT-INF/lib/\");\n    }\n\n    @Test(expected = Exception.class)\n    public void testMain() throws Exception {\n        main(null);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/bootstrap/ClasspathLauncherTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.bootstrap;\n\nimport com.alipay.sofa.ark.bootstrap.ClasspathLauncher.ClassPathArchive;\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.common.util.FileUtils;\nimport com.alipay.sofa.ark.loader.DirectoryBizArchive;\nimport com.alipay.sofa.ark.loader.EmbedClassPathArchive;\nimport com.alipay.sofa.ark.loader.archive.JarFileArchive;\nimport com.alipay.sofa.ark.spi.archive.Archive;\nimport com.alipay.sofa.ark.spi.archive.BizArchive;\nimport org.junit.AfterClass;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.mockito.MockedStatic;\nimport org.mockito.Mockito;\n\nimport java.io.IOException;\nimport java.lang.management.ManagementFactory;\nimport java.lang.management.RuntimeMXBean;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport static com.alipay.sofa.ark.common.util.ClassLoaderUtils.getAgentClassPath;\nimport static org.junit.Assert.*;\nimport static org.mockito.Mockito.when;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class ClasspathLauncherTest {\n\n    static MockedStatic<ManagementFactory> managementFactoryMockedStatic;\n\n    @BeforeClass\n    public static void setup() {\n\n        List<String> mockArguments = new ArrayList<>();\n        String filePath = ClasspathLauncherTest.class.getClassLoader()\n                .getResource(\"SampleClass.class\").getPath();\n        String workingPath = FileUtils.file(filePath).getParent();\n        mockArguments.add(String.format(\"-javaagent:%s\", workingPath));\n\n        RuntimeMXBean runtimeMXBean = Mockito.mock(RuntimeMXBean.class);\n        when(runtimeMXBean.getInputArguments()).thenReturn(mockArguments);\n        managementFactoryMockedStatic = Mockito.mockStatic(ManagementFactory.class);\n        managementFactoryMockedStatic.when(ManagementFactory::getRuntimeMXBean).thenReturn(runtimeMXBean);\n    }\n\n    @AfterClass\n    public static void tearDown() {\n        managementFactoryMockedStatic.close();\n    }\n\n    @Test\n    public void testFilterAgentClasspath() throws Exception {\n\n        URL url = this.getClass().getClassLoader().getResource(\"sample-biz.jar\");\n        URL[] agentUrl = getAgentClassPath();\n        assertEquals(1, agentUrl.length);\n\n        List<URL> urls = new ArrayList<>();\n        urls.add(url);\n        urls.addAll(Arrays.asList(agentUrl));\n\n        ClassPathArchive classPathArchive = new ClassPathArchive(\n            this.getClass().getCanonicalName(), null, urls.toArray(new URL[] {}));\n        List<BizArchive> bizArchives = classPathArchive.getBizArchives();\n        assertEquals(1, bizArchives.size());\n        assertEquals(2, urls.size());\n    }\n\n    @Test\n    public void testSpringBootFatJar() throws Exception {\n\n        URL url = this.getClass().getClassLoader().getResource(\"sample-springboot-fat-biz.jar\");\n        URL[] agentUrl = getAgentClassPath();\n        assertEquals(1, agentUrl.length);\n\n        List<URL> urls = new ArrayList<>();\n        JarFileArchive jarFileArchive = new JarFileArchive(FileUtils.file(url.getFile()));\n        List<Archive> archives = jarFileArchive.getNestedArchives(this::isNestedArchive);\n        for (Archive archive : archives) {\n            urls.add(archive.getUrl());\n        }\n        urls.addAll(Arrays.asList(agentUrl));\n\n        EmbedClassPathArchive classPathArchive = new EmbedClassPathArchive(\n                this.getClass().getCanonicalName(), null, urls.toArray(new URL[]{}));\n        List<BizArchive> bizArchives = classPathArchive.getBizArchives();\n        assertEquals(0, bizArchives.size());\n        assertNotNull(classPathArchive.getContainerArchive());\n        assertEquals(1, classPathArchive.getPluginArchives().size());\n        assertEquals(archives.size() + 1, urls.size());\n        assertEquals(3, classPathArchive.getConfClasspath().size());\n\n        URLClassLoader classLoader = new URLClassLoader(classPathArchive.getContainerArchive().getUrls());\n        try {\n            Class clazz = classLoader.loadClass(\"com.alipay.sofa.ark.bootstrap.ArkLauncher\");\n            assertNotNull(clazz);\n        } catch (Exception e) {\n            assertTrue(\"loadClass class failed \", false);\n        }\n    }\n\n    protected boolean isNestedArchive(Archive.Entry entry) {\n        return entry.isDirectory() ? entry.getName().equals(\"BOOT-INF/classes/\") : entry.getName()\n            .startsWith(\"BOOT-INF/lib/\");\n    }\n\n    @Test\n    public void testConfClasspath() throws IOException {\n        ClassLoader classLoader = this.getClass().getClassLoader();\n        ClassPathArchive classPathArchive = new ClassPathArchive(\n            this.getClass().getCanonicalName(), null, ClassLoaderUtils.getURLs(classLoader));\n        List<URL> confClasspath = classPathArchive.getConfClasspath();\n        assertEquals(3, confClasspath.size());\n    }\n\n    @Test\n    public void testFromSurefire() throws IOException {\n\n        ClassLoader classLoader = this.getClass().getClassLoader();\n        ClassPathArchive classPathArchive = new ClassPathArchive(\n            this.getClass().getCanonicalName(), null, ClassLoaderUtils.getURLs(classLoader));\n\n        URL url1 = Mockito.mock(URL.class);\n        URL url2 = Mockito.mock(URL.class);\n        URL url3 = Mockito.mock(URL.class);\n\n        when(url1.getFile()).thenReturn(\"surefirebooter17233117990150815938.jar\");\n        when(url2.getFile()).thenReturn(\"org.jacoco.agent-0.8.4-runtime.jar\");\n        when(url3.getFile()).thenReturn(\"byte-buddy-agent-1.10.15.jar\");\n        assertTrue(classPathArchive.fromSurefire(new URL[] { url1, url2, url3 }));\n\n        List<URL> urls2 = classPathArchive.getConfClasspath();\n        urls2.add(url2);\n        urls2.add(url3);\n        assertFalse(classPathArchive.fromSurefire(urls2.toArray(new URL[0])));\n    }\n\n    @Test\n    public void testOtherMethods() throws IOException {\n\n        URL url = this.getClass().getClassLoader().getResource(\"sample-biz.jar\");\n        URL[] agentUrl = getAgentClassPath();\n        assertEquals(1, agentUrl.length);\n\n        List<URL> urls = new ArrayList<>();\n        urls.add(url);\n        urls.addAll(Arrays.asList(agentUrl));\n\n        ClassPathArchive classPathArchive = new ClassPathArchive(\n            this.getClass().getCanonicalName(), null, urls.toArray(new URL[] {}));\n\n        try {\n            classPathArchive.getUrl();\n        } catch (Exception e) {\n        }\n        try {\n            classPathArchive.getManifest();\n        } catch (Exception e) {\n        }\n        try {\n            classPathArchive.getNestedArchives(null);\n        } catch (Exception e) {\n        }\n        try {\n            classPathArchive.getNestedArchive(null);\n        } catch (Exception e) {\n        }\n        try {\n            classPathArchive.getInputStream(null);\n        } catch (Exception e) {\n        }\n        try {\n            classPathArchive.iterator();\n        } catch (Exception e) {\n        }\n\n        assertTrue(classPathArchive.createDirectoryBizModuleArchive().getClass()\n            .equals(DirectoryBizArchive.class));\n        URL url2 = new URL(\"file://aa\");\n        assertArrayEquals(new URL[] { url2 },\n            classPathArchive.filterBizUrls(new URL[] { agentUrl[0], url, url2 }));\n\n        URL surefireJarURL = this.getClass().getClassLoader()\n            .getResource(\"sample-biz-surefire.jar\");\n        assertArrayEquals(new URL[] { url2, new URL(\"file://b\") },\n            classPathArchive.parseClassPathFromSurefireBoot(surefireJarURL));\n    }\n\n    @Test\n    public void testBaseExecutableArchiveLauncher() {\n\n        BaseExecutableArchiveLauncher baseExecutableArchiveLauncher = new BaseExecutableArchiveLauncher() {\n            @Override\n            protected String getMainClass() throws Exception {\n                return null;\n            }\n        };\n\n        assertNotNull(baseExecutableArchiveLauncher.getExecutableArchive());\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/bootstrap/MainMethodRunnerTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.bootstrap;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class MainMethodRunnerTest {\n\n    private static int count;\n\n    @Before\n    public void init() {\n        MainMethodRunnerTest.count = 0;\n    }\n\n    @Test\n    public void testRunner() {\n        MainMethodRunner mainMethodRunner = new MainMethodRunner(MainClass.class.getName(),\n            new String[] { \"10\" }, null);\n\n        try {\n            mainMethodRunner.run();\n            Assert.assertTrue(MainMethodRunnerTest.count == 10);\n        } catch (Exception e) {\n            Assert.assertNull(e);\n        }\n\n    }\n\n    public static class MainClass {\n\n        public static void main(String[] args) {\n            if (args.length > 0) {\n                MainMethodRunnerTest.count += Integer.valueOf(args[0]);\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/DirectoryBizArchiveTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader;\n\nimport com.alipay.sofa.ark.spi.archive.Archive;\nimport com.alipay.sofa.ark.spi.archive.Archive.Entry;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.lang.reflect.Field;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.Iterator;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.ARK_BIZ_MARK_ENTRY;\nimport static java.util.Collections.singletonList;\nimport static org.junit.Assert.*;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\npublic class DirectoryBizArchiveTest {\n\n    private DirectoryBizArchive directoryBizArchive;\n\n    @Before\n    public void setUp() throws MalformedURLException {\n        directoryBizArchive = new DirectoryBizArchive(\"a\", \"b\", new URL[] { new URL(\"file://a\") });\n    }\n\n    @Test\n    public void testDirectoryBizArchive() throws Exception {\n\n        assertFalse(directoryBizArchive.isTestMode());\n        assertEquals(\"a\", directoryBizArchive.getClassName());\n        assertEquals(\"b\", directoryBizArchive.getMethodName());\n        assertArrayEquals(new URL[]{new URL(\"file://a\")}, directoryBizArchive.getUrls());\n\n        assertTrue(directoryBizArchive.isEntryExist(entry -> !entry.isDirectory() && entry.getName().equals(ARK_BIZ_MARK_ENTRY)));\n        assertEquals(6, directoryBizArchive.getManifest().getMainAttributes().size());\n\n        try {\n            directoryBizArchive.getUrl();\n            assertTrue(false);\n        } catch (Exception e) {\n        }\n        try {\n            directoryBizArchive.getNestedArchives(entry -> false);\n            assertTrue(false);\n        } catch (Exception e) {\n        }\n        try {\n            directoryBizArchive.getInputStream(null);\n            assertTrue(false);\n        } catch (Exception e) {\n        }\n        try {\n            directoryBizArchive.iterator();\n            assertTrue(false);\n        } catch (Exception e) {\n        }\n\n        Archive nestedArchive = directoryBizArchive.getNestedArchive(new Entry() {\n            @Override\n            public boolean isDirectory() {\n                return false;\n            }\n\n            @Override\n            public String getName() {\n                return ARK_BIZ_MARK_ENTRY;\n            }\n        });\n\n        Field field = JarBizArchive.class.getDeclaredField(\"archive\");\n        field.setAccessible(true);\n        assertNull(field.get(nestedArchive));\n    }\n\n    @Test\n    public void testJarBizArchive() throws Exception {\n\n        Archive archive = mock(Archive.class);\n        JarBizArchive jarBizArchive = new JarBizArchive(archive);\n\n        Iterator iterator = singletonList(new Entry() {\n            @Override\n            public boolean isDirectory() {\n                return false;\n            }\n\n            @Override\n            public String getName() {\n                return \"lib/export/a\";\n            }\n        }).iterator();\n\n        when(archive.iterator()).thenReturn((Iterator<Entry>) iterator);\n        when(archive.getUrl()).thenReturn(new URL(\"file://a\"));\n        when(archive.getNestedArchive(any())).thenReturn(archive);\n\n        assertArrayEquals(new URL[] { new URL(\"file://a\"), new URL(\"file://a\") },\n            jarBizArchive.getExportUrls());\n        assertNull(jarBizArchive.getInputStream(null));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/DirectoryContainerArchiveTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader;\n\nimport org.junit.Test;\n\nimport java.net.URL;\n\nimport static org.junit.Assert.assertTrue;\n\npublic class DirectoryContainerArchiveTest {\n\n    private DirectoryContainerArchive directoryContainerArchive;\n\n    @Test\n    public void testDirectoryContainerArchive() throws Exception {\n\n        directoryContainerArchive = new DirectoryContainerArchive(new URL[] { new URL(\"file://a\") });\n\n        try {\n            directoryContainerArchive.getUrl();\n            assertTrue(false);\n        } catch (Exception e) {\n        }\n        try {\n            directoryContainerArchive.getManifest();\n            assertTrue(false);\n        } catch (Exception e) {\n        }\n        try {\n            directoryContainerArchive.getNestedArchives(null);\n            assertTrue(false);\n        } catch (Exception e) {\n        }\n        try {\n            directoryContainerArchive.getNestedArchive(null);\n            assertTrue(false);\n        } catch (Exception e) {\n        }\n        try {\n            directoryContainerArchive.getInputStream(null);\n            assertTrue(false);\n        } catch (Exception e) {\n        }\n        try {\n            directoryContainerArchive.iterator();\n            assertTrue(false);\n        } catch (Exception e) {\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/EmbedClassPathArchiveTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.common.util.FileUtils;\nimport com.alipay.sofa.ark.spi.archive.BizArchive;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.mockito.MockedStatic;\nimport org.mockito.Mockito;\nimport org.springframework.boot.loader.archive.Archive;\nimport org.springframework.boot.loader.archive.JarFileArchive;\n\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Objects;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.when;\n\n/**\n * @author bingjie.lbj\n * @since 0.1.0\n */\npublic class EmbedClassPathArchiveTest {\n\n    @Test\n    public void testGetContainerArchive() throws Exception {\n        ClassLoader cl = Thread.currentThread().getContextClassLoader();\n        URL springbootFatJar = cl.getResource(\"sample-springboot-fat-biz.jar\");\n        JarFileArchive jarFileArchive = new JarFileArchive(FileUtils.file(springbootFatJar.getFile()));\n        Iterator<Archive> archives = jarFileArchive.getNestedArchives(this::isNestedArchive,null);\n        List<URL> urls = new ArrayList<>();\n        while (archives.hasNext()){\n            urls.add(archives.next().getUrl());\n        }\n\n        EmbedClassPathArchive archive = new EmbedClassPathArchive(\n                \"com.alipay.sofa.ark.sample.springbootdemo.SpringbootDemoApplication\", \"main\",\n                urls.toArray(new URL[] {}));\n        assertTrue(archive.getContainerArchive().getUrls().length != 0);\n        assertTrue(archive.getConfClasspath().size() != 0);\n        assertTrue(archive.getBizArchives().size() == 0);\n        assertTrue(archive.getPluginArchives().size() == 1);\n\n        URLClassLoader classLoader = new URLClassLoader(archive.getContainerArchive().getUrls());\n        try {\n            Class clazz = classLoader.loadClass(\"com.alipay.sofa.ark.container.ArkContainer\");\n            assertTrue(clazz != null);\n        } catch (Exception e) {\n            assertTrue(\"loadClass class failed \", false);\n        }\n    }\n\n    protected boolean isNestedArchive(Archive.Entry entry) {\n        return entry.isDirectory() ? Objects.equals(entry.getName(), \"BOOT-INF/classes/\") : entry\n            .getName().startsWith(\"BOOT-INF/lib/\");\n    }\n\n    @Test\n    public void testStaticCombineGetBizArchives() throws Exception {\n        ClassLoader cl = Thread.currentThread().getContextClassLoader();\n        URL springbootFatJar = cl.getResource(\"static-combine-demo.jar\");\n        JarFileArchive jarFileArchive = new JarFileArchive(FileUtils.file(springbootFatJar.getFile()));\n        Iterator<org.springframework.boot.loader.archive.Archive> archives = jarFileArchive.getNestedArchives(this::isNestedArchive,null);\n        List<URL> urls = new ArrayList<>();\n        while (archives.hasNext()){\n            urls.add(archives.next().getUrl());\n        }\n        EmbedClassPathArchive archive = new EmbedClassPathArchive(\"com.alipay.sofa.ark.sample.springbootdemo.SpringbootDemoApplication\",\n                \"main\",\n                urls.toArray(new URL[] {}));\n        List<BizArchive> bizArchives = archive.getBizArchives();\n        Assert.assertFalse(bizArchives==null||bizArchives.isEmpty());\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/ExecutableArkBizJarTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader;\n\nimport com.alipay.sofa.ark.spi.archive.Archive;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.util.Iterator;\n\nimport static org.junit.Assert.*;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\npublic class ExecutableArkBizJarTest {\n\n    private Archive             archive             = mock(Archive.class);\n\n    private ExecutableArkBizJar executableArkBizJar = new ExecutableArkBizJar(archive);\n\n    @Before\n    public void setUp() {\n        when(archive.iterator()).thenReturn(mock(Iterator.class));\n    }\n\n    @Test\n    public void testExecutableArkBizJar() throws Exception {\n\n        assertNull(executableArkBizJar.getManifest());\n        assertNull(executableArkBizJar.getInputStream(null));\n        assertNull(executableArkBizJar.getNestedArchive(null));\n\n        try {\n            executableArkBizJar.getContainerArchive();\n            assertTrue(false);\n        } catch (RuntimeException e) {\n        }\n\n        assertEquals(0, executableArkBizJar.getConfClasspath().size());\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/ExplodedBizArchiveTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader;\n\nimport com.alipay.sofa.ark.common.util.FileUtils;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport static org.apache.commons.io.FileUtils.deleteQuietly;\n\n/**\n *\n * @author bingjie.lbj\n */\npublic class ExplodedBizArchiveTest {\n\n    @Test\n    public void testCreate() throws IOException {\n        ClassLoader cl = Thread.currentThread().getContextClassLoader();\n        URL arkBizJar = cl.getResource(\"sample-biz-withjar.jar\");\n        File unpack = FileUtils.unzip(FileUtils.file(arkBizJar.getFile()), arkBizJar.getFile()\n                                                                     + \"-unpack\");\n        ExplodedBizArchive archive = new ExplodedBizArchive(unpack);\n        Assert.assertNotNull(archive.getManifest());\n        Assert.assertNotNull(archive.getUrl());\n\n        try {\n            archive.getInputStream(null);\n            Assert.assertTrue(false);\n        } catch (UnsupportedOperationException e){\n            Assert.assertTrue(true);\n        }\n        try {\n            archive.isEntryExist(entry -> true);\n            Assert.assertTrue(false);\n        } catch (UnsupportedOperationException e){\n            Assert.assertTrue(true);\n        }\n        try {\n            archive.getNestedArchive(null);\n            Assert.assertTrue(false);\n        } catch (UnsupportedOperationException e){\n            Assert.assertTrue(true);\n        }\n        try {\n            archive.getNestedArchives(null);\n            Assert.assertTrue(false);\n        } catch (UnsupportedOperationException e){\n            Assert.assertTrue(true);\n        }\n        try {\n            archive.iterator();\n            Assert.assertTrue(false);\n        } catch (UnsupportedOperationException e){\n            Assert.assertTrue(true);\n        }\n        Assert.assertEquals(archive.getManifest().getMainAttributes().getValue(\"Ark-Biz-Name\"),\n            \"sofa-ark-sample-springboot-ark\");\n        Assert.assertEquals(archive.getUrls().length, 3);\n\n        ClassLoader bizClassLoader = new URLClassLoader(archive.getUrls());\n        Class mainClass = null;\n        Class logger = null;\n        try {\n            mainClass = bizClassLoader\n                .loadClass(\"com.alipay.sofa.ark.sample.springbootdemo.SpringbootDemoApplication\");\n            logger = bizClassLoader.loadClass(\"org.slf4j.Logger\");\n        } catch (ClassNotFoundException exception) {\n\n        }\n        Assert.assertNotNull(logger);\n        Assert.assertNotNull(mainClass);\n    }\n\n    @Test\n    public void testCloseManifestFileStream() throws IOException {\n        ClassLoader cl = Thread.currentThread().getContextClassLoader();\n        URL arkBizJar = cl.getResource(\"sample-biz-withjar.jar\");\n        File unpack = FileUtils.unzip(FileUtils.file(arkBizJar.getFile()), arkBizJar.getFile()\n                                                                           + \"-testdelete-unpack\");\n        ExplodedBizArchive archive = new ExplodedBizArchive(unpack);\n        Assert.assertNotNull(archive.getManifest());\n        File file = new File(unpack, \"META-INF/MANIFEST.MF\");\n        Assert.assertTrue(file.delete());\n        deleteQuietly(unpack);\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/archive/ExplodedArchiveTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.archive;\n\nimport com.alipay.sofa.ark.spi.archive.Archive;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.util.List;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\npublic class ExplodedArchiveTest {\n\n    private ExplodedArchive explodedArchive;\n\n    private String          archiveTestDirPath = this.getClass().getClassLoader()\n                                                   .getResource(\"./exploded-archive-test/\")\n                                                   .getFile();\n\n    @Before\n    public void setUp() {\n        explodedArchive = new ExplodedArchive(new File(archiveTestDirPath));\n    }\n\n    @Test\n    public void testGetMethods() throws Exception {\n\n        assertTrue(explodedArchive.getUrl().getFile().endsWith(\"test-classes/exploded-archive-test/\"));\n        assertTrue(explodedArchive.getManifest() != null);\n\n        List<Archive> nestedArchives = explodedArchive.getNestedArchives(entry -> !entry.getName().contains(\"META-INF\"));\n        assertEquals(2, nestedArchives.size());\n        String nestedArchivesStr = nestedArchives.toString();\n        assertTrue(nestedArchivesStr.contains(\"/example-jarinjarinjar.jar!/\"));\n        assertTrue(nestedArchivesStr.contains(\"/sample-biz.jar!/\"));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/archive/JarFileArchiveTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.archive;\n\nimport com.alipay.sofa.ark.loader.archive.JarFileArchive.JarFileEntry;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.jar.JarEntry;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\npublic class JarFileArchiveTest {\n\n    private JarFileArchive jarFileArchive;\n\n    private String         jarFilePath = this.getClass().getClassLoader()\n                                           .getResource(\"./sample-springboot-fat-biz.jar\")\n                                           .getFile();\n\n    @Before\n    public void setUp() throws IOException {\n        jarFileArchive = new JarFileArchive(new File(jarFilePath));\n    }\n\n    @Test\n    public void testGetMethods() throws Exception {\n\n        assertTrue(jarFileArchive.getManifest() != null);\n        assertEquals(50, jarFileArchive.getNestedArchives(entry -> entry.getName().contains(\".jar\")).size());\n\n        JarEntry jarEntry = new JarEntry(\"BOOT-INF/lib/slf4j-api-1.7.21.jar\");\n        jarEntry.setComment(\"UNPACK:xxx\");\n        assertTrue(jarFileArchive.getNestedArchive(new JarFileEntry(jarEntry)).getUrl().getFile().endsWith(\"slf4j-api-1.7.21.jar\"));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/jar/HandlerTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\nimport org.junit.Test;\n\nimport java.net.MalformedURLException;\nimport java.net.URL;\n\nimport static org.junit.Assert.assertEquals;\n\npublic class HandlerTest {\n\n    private URL     url     = this.getClass().getClassLoader()\n                                .getResource(\"sample-biz-withjar.jar\");\n\n    private Handler handler = new Handler();\n\n    @Test(expected = MalformedURLException.class)\n    public void testOpenConnectionWithIOException() throws Exception {\n        handler.openConnection(this.getClass().getClassLoader()\n            .getResource(\"sample-biz-withjar.jar\"));\n    }\n\n    @Test(expected = NullPointerException.class)\n    public void testOpenConnectionWithNPE() throws Exception {\n        handler.openConnection(this.getClass().getClassLoader()\n            .getResource(\"sample-biz-withjar.jar!/lib\"));\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testParseURLWithIllegalSpec() throws Exception {\n        handler.parseURL(url, \"/\", 0, 1);\n    }\n\n    @Test(expected = SecurityException.class)\n    public void testParseURLWithEmptySpec() throws Exception {\n        handler.parseURL(url, \"/\", 0, 0);\n    }\n\n    @Test\n    public void testReplaceParentDir() throws Exception {\n        assertEquals(\"../a\", handler.replaceParentDir(\"/../../a\"));\n        assertEquals(\"../\", handler.replaceParentDir(\"/../../\"));\n        assertEquals(\"aaa\", handler.replaceParentDir(\"/../aaa/../aaa\"));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/jar/JarEntryTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.net.URL;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNull;\n\npublic class JarEntryTest {\n\n    private URL      url;\n\n    private JarEntry jarEntry;\n\n    @Before\n    public void setUp() throws Exception {\n        url = this.getClass().getClassLoader().getResource(\"sample-biz-withjar.jar\");\n        jarEntry = new JarEntry(new JarFile(new File(url.getPath())),\n            new CentralDirectoryFileHeader(new byte[64], 0, new AsciiBytes(\"lib\"), null,\n                new AsciiBytes(\"mycomment\"), 0));\n    }\n\n    @Test\n    public void testGetters() throws Exception {\n        assertEquals(\"jar:\" + url + \"!/lib\", jarEntry.getUrl().toString());\n        assertNull(jarEntry.getAttributes());\n        assertNull(jarEntry.getCertificates());\n        assertNull(jarEntry.getCodeSigners());\n        jarEntry.setCertificates(new java.util.jar.JarEntry(\"a\"));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/jar/JarFileTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\n\nimport static com.alipay.sofa.ark.loader.jar.JarFile.JarFileType.DIRECT;\nimport static org.junit.Assert.assertEquals;\n\npublic class JarFileTest {\n\n    @Test\n    public void testSetupEntryCertificates() throws IOException {\n\n        URL url = this.getClass().getClassLoader().getResource(\"sample-biz.jar\");\n        JarFile jarFile = new JarFile(new File(url.getPath()));\n        assertEquals(7485, jarFile.size());\n        assertEquals(\"jar:\" + url.toString() + \"!/\", jarFile.getUrl().toString());\n\n        jarFile.setupEntryCertificates(new JarEntry(jarFile, new CentralDirectoryFileHeader(\n            new byte[64], 0, new AsciiBytes(\"lib\"), null, new AsciiBytes(\"mycomment\"), 0)));\n\n        jarFile.clearCache();\n        assertEquals(DIRECT, jarFile.getType());\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/jar/JarURLConnectionTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\nimport com.alipay.sofa.ark.loader.jar.JarURLConnection.JarEntryName;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.reflect.Field;\nimport java.net.URL;\n\nimport static com.alipay.sofa.ark.loader.jar.JarURLConnection.JarEntryName.get;\nimport static com.alipay.sofa.ark.loader.jar.JarURLConnection.get;\nimport static org.junit.Assert.*;\n\npublic class JarURLConnectionTest {\n\n    private JarURLConnection jarURLConnection;\n\n    private URL              url = this.getClass().getClassLoader()\n                                     .getResource(\"sample-biz-withjar.jar\");\n\n    @Before\n    public void setUp() throws Exception {\n        jarURLConnection = get(url, new JarFile(new File(url.getPath())));\n    }\n\n    @Test\n    public void testGetJarFileURL() throws IOException {\n\n        assertTrue(jarURLConnection.getJarFileURL().getFile().endsWith(\"/sample-biz-withjar.jar\"));\n        assertNull(jarURLConnection.getJarEntry());\n\n        jarURLConnection = get(new URL(\n            \"file://a/b/sample-biz-withjar.jar!/lib/slf4j-api-1.7.30.jar!/\"), new JarFile(new File(\n            url.getPath())));\n\n        assertEquals(\"com.alipay.sofa.ark.loader.data.RandomAccessDataFile$DataInputStream\",\n            jarURLConnection.getInputStream().getClass().getName());\n        assertNull(jarURLConnection.getJarEntry());\n        assertEquals(\"\", jarURLConnection.getEntryName());\n    }\n\n    @Test\n    public void testGetContentLength() throws Exception {\n        assertEquals(52949, jarURLConnection.getContentLength());\n        Field field = JarURLConnection.class.getDeclaredField(\"jarEntryName\");\n        field.setAccessible(true);\n        field.set(jarURLConnection, new JarEntryName(\"!/lib/slf4j-api-1.7.30.jar!/\"));\n        assertEquals(-1, jarURLConnection.getContentLength());\n    }\n\n    @Test\n    public void testGetContent() throws IOException {\n        assertEquals(JarFile.class, jarURLConnection.getContent().getClass());\n        assertEquals(\"x-java/jar\", jarURLConnection.getContentType());\n    }\n\n    @Test\n    public void testGetLastModified() throws Exception {\n        assertEquals(0, jarURLConnection.getLastModified());\n        Field field = JarURLConnection.class.getDeclaredField(\"jarEntryName\");\n        field.setAccessible(true);\n        field.set(jarURLConnection, new JarEntryName(\"!/lib/slf4j-api-1.7.30.jar!/\"));\n        assertEquals(0, jarURLConnection.getLastModified());\n    }\n\n    @Test\n    public void testJarEntryName() {\n        JarEntryName jarEntryName = get(url.toString());\n\n        String javaVersion = System.getProperty(\"java.version\");\n        if (javaVersion.startsWith(\"1.8\")) {\n            assertEquals(\"content/unknown\", jarEntryName.getContentType());\n        } else {\n            assertEquals(\"application/java-archive\", jarEntryName.getContentType());\n        }\n\n        if (javaVersion.startsWith(\"1.8\")) {\n            assertEquals(\"content/unknown\", jarEntryName.getContentType());\n        } else {\n            assertEquals(\"application/java-archive\", jarEntryName.getContentType());\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/jar/JarUtilsParseArtifactIdFromUnpackedDirTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.util.Properties;\n\nimport static org.junit.Assert.*;\n\npublic class JarUtilsParseArtifactIdFromUnpackedDirTest {\n\n    @Test\n    public void testParseArtifactIdFromUnpackedDir_StandardLocation() throws IOException {\n        // Create a temporary directory to simulate unpacked jar\n        File tempDir = com.alipay.sofa.ark.common.util.FileUtils.createTempDir(\"test-unpack\");\n        try {\n            // Create the standard maven-archiver/pom.properties location\n            File mavenArchiverDir = new File(tempDir, \"META-INF/maven-archiver\");\n            mavenArchiverDir.mkdirs();\n\n            File pomPropertiesFile = new File(mavenArchiverDir, \"pom.properties\");\n            try (FileWriter writer = new FileWriter(pomPropertiesFile)) {\n                Properties props = new Properties();\n                props.setProperty(\"artifactId\", \"test-artifact\");\n                props.store(writer, \"Test pom.properties file\");\n            }\n\n            // Test the method\n            String result = JarUtilsTestHelper.parseArtifactIdFromUnpackedDir(tempDir\n                .getAbsolutePath());\n            assertEquals(\"test-artifact\", result);\n        } finally {\n            org.apache.commons.io.FileUtils.deleteQuietly(tempDir);\n        }\n    }\n\n    @Test\n    public void testParseArtifactIdFromUnpackedDir_NonStandardLocation() throws IOException {\n        // Create a temporary directory to simulate unpacked jar\n        File tempDir = com.alipay.sofa.ark.common.util.FileUtils\n            .createTempDir(\"test-unpack-nonstandard\");\n        try {\n            // Create pom.properties in a nested directory (non-standard location)\n            File nestedDir = new File(tempDir, \"some/nested/path\");\n            nestedDir.mkdirs();\n\n            File pomPropertiesFile = new File(nestedDir, \"pom.properties\");\n            try (FileWriter writer = new FileWriter(pomPropertiesFile)) {\n                Properties props = new Properties();\n                props.setProperty(\"artifactId\", \"test-artifact-nested\");\n                props.store(writer, \"Test pom.properties file in nested location\");\n            }\n\n            // Test the method - should find the pom.properties via recursive search\n            String result = JarUtilsTestHelper.parseArtifactIdFromUnpackedDir(tempDir\n                .getAbsolutePath());\n            assertEquals(\"test-artifact-nested\", result);\n        } finally {\n            org.apache.commons.io.FileUtils.deleteQuietly(tempDir);\n        }\n    }\n\n    @Test\n    public void testParseArtifactIdFromUnpackedDir_DirectoryDoesNotExist() {\n        // Test with non-existent directory\n        String result = JarUtilsTestHelper\n            .parseArtifactIdFromUnpackedDir(\"/non/existent/directory\");\n        assertNull(result);\n    }\n\n    @Test\n    public void testParseArtifactIdFromUnpackedDir_EmptyPomProperties() throws IOException {\n        // Create a temporary directory with empty pom.properties\n        File tempDir = com.alipay.sofa.ark.common.util.FileUtils.createTempDir(\"test-unpack-empty\");\n        try {\n            // Create the standard maven-archiver/pom.properties location with empty artifactId\n            File mavenArchiverDir = new File(tempDir, \"META-INF/maven-archiver\");\n            mavenArchiverDir.mkdirs();\n\n            File pomPropertiesFile = new File(mavenArchiverDir, \"pom.properties\");\n            try (FileWriter writer = new FileWriter(pomPropertiesFile)) {\n                Properties props = new Properties();\n                props.setProperty(\"artifactId\", \"\"); // Empty artifactId\n                props.store(writer, \"Test pom.properties file with empty artifactId\");\n            }\n\n            // Test the method - should return null for empty artifactId\n            String result = JarUtilsTestHelper.parseArtifactIdFromUnpackedDir(tempDir\n                .getAbsolutePath());\n            assertNull(result);\n        } finally {\n            org.apache.commons.io.FileUtils.deleteQuietly(tempDir);\n        }\n    }\n\n    @Test\n    public void testParseArtifactIdFromUnpackedDir_MissingArtifactIdProperty() throws IOException {\n        // Create a temporary directory with pom.properties that doesn't have artifactId\n        File tempDir = com.alipay.sofa.ark.common.util.FileUtils\n            .createTempDir(\"test-unpack-missing-key\");\n        try {\n            // Create the standard maven-archiver/pom.properties location without artifactId\n            File mavenArchiverDir = new File(tempDir, \"META-INF/maven-archiver\");\n            mavenArchiverDir.mkdirs();\n\n            File pomPropertiesFile = new File(mavenArchiverDir, \"pom.properties\");\n            try (FileWriter writer = new FileWriter(pomPropertiesFile)) {\n                Properties props = new Properties();\n                props.setProperty(\"groupId\", \"com.test\"); // Different property\n                props.setProperty(\"version\", \"1.0.0\");\n                props.store(writer, \"Test pom.properties file without artifactId\");\n            }\n\n            // Test the method - should return null when artifactId property is missing\n            String result = JarUtilsTestHelper.parseArtifactIdFromUnpackedDir(tempDir\n                .getAbsolutePath());\n            assertNull(result);\n        } finally {\n            org.apache.commons.io.FileUtils.deleteQuietly(tempDir);\n        }\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/jar/JarUtilsTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.Properties;\n\nimport static com.alipay.sofa.ark.loader.jar.JarUtils.getArtifactIdFromLocalClassPath;\nimport static com.alipay.sofa.ark.loader.jar.JarUtils.searchPomProperties;\nimport static org.junit.Assert.*;\n\npublic class JarUtilsTest {\n\n    @Test\n    public void testSearchPomProperties() {\n\n        assertNull(searchPomProperties(null));\n        assertNull(searchPomProperties(new File(\"/not-exists\")));\n\n        URL url = this.getClass().getClassLoader().getResource(\"pom-properties/pom.properties\");\n        File file = new File(url.getPath());\n        assertEquals(file, searchPomProperties(file));\n\n        url = this.getClass().getClassLoader().getResource(\"./\");\n        file = new File(url.getPath());\n        assertTrue(searchPomProperties(file).getPath().endsWith(\"pom.properties\"));\n    }\n\n    @Test\n    public void testGetArtifactIdFromLocalClassPath() {\n        assertNull(getArtifactIdFromLocalClassPath(\"/a/target/bbb\"));\n        URL url = this.getClass().getClassLoader().getResource(\"\");\n        assertEquals(\"sofa-ark-archive\", getArtifactIdFromLocalClassPath(url.getPath()));\n    }\n\n    @Test\n    public void testParseArtifactId() {\n        URL url = this.getClass().getClassLoader().getResource(\"sample-biz.jar\");\n        String artifactId = JarUtils.parseArtifactId(url.getPath());\n        assertEquals(\"sofa-ark-sample-springboot-ark\", artifactId);\n    }\n\n    @Test\n    public void testParseArtifactId2() {\n        URL url = this.getClass().getClassLoader().getResource(\"xxxxx.jar-unpack\");\n        String artifactId = JarUtils.parseArtifactId(url.getPath());\n        assertEquals(\"xxxx-test\", artifactId);\n    }\n\n    @Test\n    public void testParseArtifactIdFromUnpackDirNameFallback() {\n        File tempRoot = com.alipay.sofa.ark.common.util.FileUtils\n            .createTempDir(\"test-unpack-fallback\");\n        try {\n            File unpackDir = new File(tempRoot, \"demo-service-1.0.0.jar-unpack\");\n            assertTrue(unpackDir.mkdirs());\n\n            String artifactId = JarUtils.parseArtifactId(normalizePath(unpackDir));\n            assertEquals(\"demo-service\", artifactId);\n        } finally {\n            org.apache.commons.io.FileUtils.deleteQuietly(tempRoot);\n        }\n    }\n\n    @Test\n    public void testParseArtifactIdFromUnpackDirNameFallbackWhenArtifactIdMissing()\n                                                                                   throws IOException {\n        File tempRoot = com.alipay.sofa.ark.common.util.FileUtils\n            .createTempDir(\"test-unpack-fallback-missing-artifact-id\");\n        try {\n            File unpackDir = new File(tempRoot, \"demo-service-1.0.0.jar-unpack\");\n            File mavenArchiverDir = new File(unpackDir, \"META-INF/maven-archiver\");\n            assertTrue(mavenArchiverDir.mkdirs());\n\n            File pomPropertiesFile = new File(mavenArchiverDir, \"pom.properties\");\n            try (FileWriter writer = new FileWriter(pomPropertiesFile)) {\n                Properties props = new Properties();\n                props.setProperty(\"groupId\", \"com.test\");\n                props.setProperty(\"version\", \"1.0.0\");\n                props.store(writer, \"Test pom.properties file without artifactId\");\n            }\n\n            String artifactId = JarUtils.parseArtifactId(normalizePath(unpackDir));\n            assertEquals(\"demo-service\", artifactId);\n        } finally {\n            org.apache.commons.io.FileUtils.deleteQuietly(tempRoot);\n        }\n    }\n\n    private String normalizePath(File file) {\n        return file.getAbsolutePath().replace(File.separatorChar, '/');\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/jar/JarUtilsTestHelper.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\nimport java.lang.reflect.Method;\n\n/**\n * Helper class to access private methods in JarUtils for testing purposes.\n */\npublic class JarUtilsTestHelper {\n\n    /**\n     * Call the private parseArtifactIdFromUnpackedDir method for testing.\n     * \n     * @param unpackDirPath the path to the unpacked directory\n     * @return the parsed artifactId or null if not found\n     */\n    public static String parseArtifactIdFromUnpackedDir(String unpackDirPath) {\n        try {\n            Method method = JarUtils.class.getDeclaredMethod(\"parseArtifactIdFromUnpackedDir\",\n                String.class);\n            method.setAccessible(true);\n            return (String) method.invoke(null, unpackDirPath);\n        } catch (Exception e) {\n            throw new RuntimeException(\"Failed to invoke parseArtifactIdFromUnpackedDir\", e);\n        }\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/jar/ZipInflaterInputStreamTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.jar;\n\nimport org.junit.Test;\n\nimport java.io.ByteArrayInputStream;\nimport java.lang.reflect.Field;\nimport java.util.zip.InflaterInputStream;\n\nimport static org.junit.Assert.assertEquals;\n\npublic class ZipInflaterInputStreamTest {\n\n    @Test\n    public void testFill() throws Exception {\n\n        ZipInflaterInputStream zipInflaterInputStream = new ZipInflaterInputStream(\n            new ByteArrayInputStream(new byte[0]), 0);\n        assertEquals(0, zipInflaterInputStream.available());\n        zipInflaterInputStream.fill();\n\n        Field field = ZipInflaterInputStream.class.getDeclaredField(\"extraBytesWritten\");\n        field.setAccessible(true);\n        assertEquals(true, field.get(zipInflaterInputStream));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/test/base/BaseTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.test.base;\n\nimport com.alipay.sofa.ark.loader.data.RandomAccessDataFile;\nimport com.alipay.sofa.ark.loader.jar.AsciiBytes;\nimport com.alipay.sofa.ark.loader.jar.Bytes;\nimport org.apache.commons.io.FileUtils;\nimport org.junit.AfterClass;\nimport org.junit.BeforeClass;\n\nimport java.io.*;\nimport java.net.URL;\nimport java.util.jar.JarOutputStream;\nimport java.util.zip.CRC32;\nimport java.util.zip.ZipEntry;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic abstract class BaseTest {\n\n    public static final String TEMP_DIR           = \"temp-workspace\";\n\n    public static final String TEMP_FILE          = \"temp-file.info\";\n\n    public static final String TEMP_ZIP           = \"temp-fat-jar.jar\";\n\n    public static final String TEST_ENTRY         = \"testEntry/\";\n\n    public static final String TEST_ENTRY_COMMENT = \"testComment\";\n\n    public static final String TEST_ENTRY_EXTRA   = \"testExtra\";\n\n    public static final byte[] CONSTANT_BYTE      = new byte[] { '1', '1', '2', '2', '3', '3', '4',\n            '4', '5', '5', '6', '6', '7', '7', '8', '8' };\n\n    @BeforeClass\n    public static void startUp() throws IOException {\n        generateFile();\n        generateZip();\n    }\n\n    @AfterClass\n    public static void shutDown() {\n        cleanWorkspace();\n    }\n\n    /**\n     * 构建普通的文本文件\n     */\n    public static void generateFile() throws IOException {\n        OutputStream outputStream = new FileOutputStream(getTempDemoFile());\n\n        try {\n            outputStream.write(CONSTANT_BYTE, 0, CONSTANT_BYTE.length);\n        } finally {\n            outputStream.close();\n        }\n\n    }\n\n    /**\n     * 构建 zip 文件\n     */\n    public static void generateZip() throws IOException {\n        JarOutputStream jos = new JarOutputStream(new FileOutputStream(getTempDemoZip()));\n        CRC32 crc = new CRC32();\n\n        /* name end with '/' indicates a directory */\n        jos.putNextEntry(new ZipEntry(\"META-INF/\"));\n\n        jos.putNextEntry(new ZipEntry(\"META-INF/MANIFEST.MF\"));\n        jos.write(generateManifest());\n\n        jos.putNextEntry(new ZipEntry(\"lib/\"));\n\n        ZipEntry jarEntry = new ZipEntry(\"lib/junit-4.12.jar\");\n        byte[] jarContent = fetchResource(\"junit-4.12.jar\");\n        crc.update(jarContent);\n\n        jarEntry.setMethod(ZipEntry.STORED);\n        jarEntry.setSize(jarContent.length);\n        jarEntry.setCrc(crc.getValue());\n\n        jos.putNextEntry(jarEntry);\n        jos.write(jarContent);\n\n        ZipEntry entryForTest = new ZipEntry(TEST_ENTRY);\n        entryForTest.setComment(TEST_ENTRY_COMMENT);\n        entryForTest.setExtra(TEST_ENTRY_EXTRA.getBytes());\n        jos.putNextEntry(entryForTest);\n\n        jos.closeEntry();\n        jos.close();\n    }\n\n    private static byte[] generateManifest() {\n        AsciiBytes asciiBytes = new AsciiBytes(\"\").append(\"k1: v1\\n\").append(\"k2: v2\\n\");\n        return asciiBytes.toString().getBytes();\n    }\n\n    private static byte[] fetchResource(String resourceName) throws IOException {\n        URL resource = BaseTest.class.getClassLoader().getResource(resourceName);\n        RandomAccessDataFile dataFile = new RandomAccessDataFile(\n            com.alipay.sofa.ark.common.util.FileUtils.file(resource.getFile()));\n        return Bytes.get(dataFile);\n    }\n\n    public static File getTmpDir() {\n        String tmpPath = System.getProperty(\"java.io.tmpdir\");\n        return com.alipay.sofa.ark.common.util.FileUtils.file(tmpPath);\n    }\n\n    public static File getWorkspace() {\n        File workSpace = new File(getTmpDir(), TEMP_DIR);\n        if (!workSpace.exists()) {\n            workSpace.mkdirs();\n        }\n        return workSpace;\n    }\n\n    public static File getTempDemoFile() {\n        return new File(getWorkspace(), TEMP_FILE);\n    }\n\n    public static File getTempDemoZip() {\n        return new File(getWorkspace(), TEMP_ZIP);\n    }\n\n    public static boolean cleanWorkspace() {\n        File workDir = new File(getTmpDir(), TEMP_DIR);\n        return FileUtils.deleteQuietly(workDir);\n    }\n\n    public static boolean compareByteArray(byte[] a, byte[] b) {\n        if (a == null && b == null) {\n            return true;\n        }\n\n        if (a == null || b == null) {\n            return false;\n        }\n\n        if (a.length != b.length) {\n            return false;\n        }\n\n        for (int i = 0; i < a.length; ++i) {\n            if (a[i] != b[i]) {\n                return false;\n            }\n        }\n\n        return true;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/test/data/RandomAccessDataFileTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.test.data;\n\nimport com.alipay.sofa.ark.loader.data.RandomAccessData;\nimport com.alipay.sofa.ark.loader.data.RandomAccessDataFile;\nimport com.alipay.sofa.ark.loader.test.base.BaseTest;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class RandomAccessDataFileTest extends BaseTest {\n\n    @Test\n    public void testInputStream() throws IOException {\n        RandomAccessDataFile testFile = new RandomAccessDataFile(getTempDemoFile());\n        InputStream is = null;\n\n        try {\n            is = testFile.getInputStream(RandomAccessData.ResourceAccess.PER_READ);\n            byte[] bytes = new byte[20];\n            Assert.assertTrue(is.read(bytes) == 16);\n            for (int i = 0; i < 16; ++i) {\n                Assert.assertTrue((bytes[i] & 0xFF) == '1' + i / 2);\n            }\n        } catch (IOException e) {\n            Assert.fail(e.getMessage());\n        } finally {\n            if (is != null) {\n                is.close();\n            }\n        }\n    }\n\n    @Test\n    public void testSize() {\n        RandomAccessDataFile testFile = new RandomAccessDataFile(getTempDemoFile());\n        Assert.assertTrue(testFile.getSize() == 16);\n    }\n\n    @Test\n    public void testSubsection() throws IOException {\n        RandomAccessDataFile testFile = new RandomAccessDataFile(getTempDemoFile());\n        try {\n            testFile.getSubsection(0, 17);\n            Assert.fail(\"Should throws IndexOutOfBoundsException\");\n        } catch (Exception ex) {\n            Assert.assertTrue(ex instanceof IndexOutOfBoundsException);\n        }\n\n        RandomAccessData subData = testFile.getSubsection(2, 4);\n        InputStream is = null;\n\n        try {\n            is = subData.getInputStream(RandomAccessData.ResourceAccess.ONCE);\n            byte[] bytes = new byte[10];\n            Assert.assertTrue(is.read(bytes) == 4);\n            for (int i = 0; i < 4; ++i) {\n                Assert.assertTrue((bytes[i] & 0xFF) == '2' + i / 2);\n            }\n        } catch (Exception e) {\n            Assert.fail(e.getMessage());\n        } finally {\n            if (is != null) {\n                is.close();\n            }\n        }\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/test/jar/AsciiBytesTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.test.jar;\n\nimport com.alipay.sofa.ark.loader.jar.AsciiBytes;\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class AsciiBytesTest {\n\n    public final String content    = \"SofaArk is a class-isolated container\";\n\n    private AsciiBytes  asciiBytes = new AsciiBytes(content);\n\n    @Test\n    public void testAsciiBytes() {\n\n        assertTrue(asciiBytes.length() == content.length());\n        assertTrue(asciiBytes.startsWith(new AsciiBytes(\"SofaArk\")));\n        assertTrue(asciiBytes.endsWith(new AsciiBytes(\"container\")));\n        assertTrue(asciiBytes.toString().equals(content));\n        assertTrue(asciiBytes.substring(8, 10).endsWith(new AsciiBytes(\"is\")));\n\n        String suffix = \"suffix\";\n        AsciiBytes suffixAsciiBytes = new AsciiBytes(suffix);\n        byte[] suffixBytes = suffix.getBytes();\n\n        asciiBytes.append(suffix).equals(content + suffix);\n        asciiBytes.append(suffixAsciiBytes).equals(content + suffix);\n        asciiBytes.append(suffixBytes).equals(content + suffix);\n    }\n\n    @Test\n    public void testHashCode() {\n        AsciiBytes asciiBytes = new AsciiBytes(\"\" + (char) 0xffff + (char) -99 + (char) -255\n                                               + (char) -128 + (char) 127 + (char) 128 + (char) 255\n                                               + (char) 256 + content);\n        assertEquals(-243313336, asciiBytes.hashCode());\n        assertNotEquals(new AsciiBytes(\"\"), asciiBytes);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/test/jar/BytesTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.test.jar;\n\nimport com.alipay.sofa.ark.loader.data.RandomAccessData;\nimport com.alipay.sofa.ark.loader.data.RandomAccessDataFile;\nimport com.alipay.sofa.ark.loader.jar.Bytes;\nimport com.alipay.sofa.ark.loader.test.base.BaseTest;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.io.IOException;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class BytesTest extends BaseTest {\n\n    @Test\n    public void testBytes() throws IOException {\n        RandomAccessDataFile tempFile = new RandomAccessDataFile(getTempDemoFile());\n        byte[] content = Bytes.get(tempFile);\n        Assert.assertTrue(content.length == CONSTANT_BYTE.length);\n        for (int i = 0; i < CONSTANT_BYTE.length; ++i) {\n            Assert.assertTrue(content[i] == CONSTANT_BYTE[i]);\n        }\n\n        byte[] bytes = new byte[3];\n        Bytes.fill(tempFile.getInputStream(RandomAccessData.ResourceAccess.ONCE), bytes);\n        String.valueOf(bytes).equals(\"112\");\n\n        long value = Bytes.littleEndianValue(CONSTANT_BYTE, 0, 4);\n        Assert.assertTrue(value == ('2' << 24) + ('2' << 16) + ('1' << 8) + '1');\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/test/jar/CentralDirectoryEndRecordTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.test.jar;\n\nimport com.alipay.sofa.ark.loader.data.RandomAccessData;\nimport com.alipay.sofa.ark.loader.data.RandomAccessDataFile;\nimport com.alipay.sofa.ark.loader.jar.CentralDirectoryEndRecord;\nimport com.alipay.sofa.ark.loader.test.base.BaseTest;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URL;\n\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class CentralDirectoryEndRecordTest extends BaseTest {\n\n    @Test\n    public void testEOCD() throws IOException {\n\n        RandomAccessDataFile dataFile = new RandomAccessDataFile(getTempDemoZip());\n        CentralDirectoryEndRecord eocd = new CentralDirectoryEndRecord(dataFile);\n\n        assertTrue(eocd.isValid());\n        assertTrue(eocd.getStartOfArchive(dataFile) == 0);\n        assertTrue(eocd.getNumberOfRecords() == 5);\n    }\n\n    @Test\n    public void testWithInvalidFile() throws Exception {\n\n        RandomAccessData randomAccessData = mock(RandomAccessData.class);\n        when(randomAccessData.getInputStream(any())).thenReturn(mock(InputStream.class));\n\n        URL url = this.getClass().getClassLoader().getResource(\"example-jarinjarinjar.jar\");\n        new CentralDirectoryEndRecord(new RandomAccessDataFile(new File(url.getPath())));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/test/jar/CentralDirectoryFileHeaderTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.test.jar;\n\nimport com.alipay.sofa.ark.loader.data.RandomAccessData;\nimport com.alipay.sofa.ark.loader.data.RandomAccessDataFile;\nimport com.alipay.sofa.ark.loader.jar.AsciiBytes;\nimport com.alipay.sofa.ark.loader.jar.Bytes;\nimport com.alipay.sofa.ark.loader.jar.CentralDirectoryEndRecord;\nimport com.alipay.sofa.ark.loader.jar.CentralDirectoryFileHeader;\nimport com.alipay.sofa.ark.loader.test.base.BaseTest;\nimport org.junit.Test;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class CentralDirectoryFileHeaderTest extends BaseTest {\n\n    public final static int CENTRAL_DIRECTORY_HEADER_BASE_SIZE = 46;\n\n    @Test\n    public void testCDFH() throws IOException {\n\n        RandomAccessDataFile dataFile = new RandomAccessDataFile(getTempDemoZip());\n        CentralDirectoryEndRecord eocd = new CentralDirectoryEndRecord(dataFile);\n        RandomAccessData cdfhBlock = eocd.getCentralDirectory(dataFile);\n        List<CentralDirectoryFileHeader> cdfhList = new ArrayList<>();\n\n        int dataOffset = 0;\n        for (int i = 0; i < eocd.getNumberOfRecords(); ++i) {\n            CentralDirectoryFileHeader cdfh = new CentralDirectoryFileHeader();\n            cdfh.load(Bytes.get(cdfhBlock), dataOffset, null, 0, null);\n            dataOffset += CENTRAL_DIRECTORY_HEADER_BASE_SIZE + cdfh.getName().length()\n                          + cdfh.getComment().length() + cdfh.getExtra().length;\n            cdfhList.add(cdfh);\n        }\n\n        assertTrue(cdfhList.size() == 5);\n        assertTrue(cdfhList.get(4).getName().toString().equals(TEST_ENTRY));\n        assertTrue(cdfhList.get(4).getComment().toString().equals(TEST_ENTRY_COMMENT));\n        assertTrue(compareByteArray(cdfhList.get(4).getExtra(), TEST_ENTRY_EXTRA.getBytes()));\n    }\n\n    @Test\n    public void testOtherMethods() {\n        CentralDirectoryFileHeader centralDirectoryFileHeader = new CentralDirectoryFileHeader(\n            new byte[64], 0, new AsciiBytes(\"a/\"), null, null, 0);\n        assertEquals(true, centralDirectoryFileHeader.isDirectory());\n        CentralDirectoryFileHeader centralDirectoryFileHeader2 = centralDirectoryFileHeader.clone();\n        assertEquals(new AsciiBytes(\"a/\"), centralDirectoryFileHeader2.getName());\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/test/jar/CentralDirectoryParserTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.test.jar;\n\nimport com.alipay.sofa.ark.loader.data.RandomAccessData;\nimport com.alipay.sofa.ark.loader.data.RandomAccessDataFile;\nimport com.alipay.sofa.ark.loader.jar.*;\nimport com.alipay.sofa.ark.loader.test.base.BaseTest;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class CentralDirectoryParserTest extends BaseTest {\n\n    @Test\n    public void testParser() throws IOException {\n        CentralDirectoryParser cdParser = new CentralDirectoryParser();\n        TestVisitor testVisitor = cdParser.addVisitor(new TestVisitor());\n        RandomAccessDataFile dataFile = new RandomAccessDataFile(getTempDemoZip());\n        cdParser.parse(dataFile, false);\n        List<CentralDirectoryFileHeader> cdfhList = testVisitor.getCdfhList();\n\n        Assert.assertTrue(testVisitor.getEntryNum() == 5);\n        Assert.assertTrue(cdfhList.size() == 5);\n        Assert.assertTrue(cdfhList.get(4).getName().toString().equals(TEST_ENTRY));\n        Assert.assertTrue(cdfhList.get(4).getComment().toString().equals(TEST_ENTRY_COMMENT));\n        Assert\n            .assertTrue(compareByteArray(cdfhList.get(4).getExtra(), TEST_ENTRY_EXTRA.getBytes()));\n\n    }\n\n    public static class TestVisitor implements CentralDirectoryVisitor {\n\n        public int                              entryNum;\n        public List<CentralDirectoryFileHeader> cdfhList = new ArrayList<>();\n\n        @Override\n        public void visitStart(CentralDirectoryEndRecord endRecord,\n                               RandomAccessData centralDirectoryData) {\n            entryNum = endRecord.getNumberOfRecords();\n        }\n\n        @Override\n        public void visitFileHeader(CentralDirectoryFileHeader fileHeader, int dataOffset) {\n            cdfhList.add(fileHeader);\n        }\n\n        @Override\n        public void visitEnd() {\n\n        }\n\n        public int getEntryNum() {\n            return entryNum;\n        }\n\n        public List<CentralDirectoryFileHeader> getCdfhList() {\n            return cdfhList;\n        }\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/test/jar/JarFileTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.test.jar;\n\nimport com.alipay.sofa.ark.loader.jar.JarEntry;\nimport com.alipay.sofa.ark.loader.jar.JarFile;\nimport com.alipay.sofa.ark.loader.test.base.BaseTest;\nimport org.junit.Test;\n\nimport java.io.IOException;\nimport java.util.jar.Manifest;\nimport java.util.zip.ZipEntry;\n\nimport static org.junit.Assert.assertTrue;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class JarFileTest extends BaseTest {\n\n    @Test\n    public void testJarFile() throws IOException {\n\n        JarFile jarFile = new JarFile(getTempDemoZip());\n        Manifest manifest = jarFile.getManifest();\n\n        assertTrue(manifest.getMainAttributes().getValue(\"k1\").equals(\"v1\"));\n        assertTrue(manifest.getMainAttributes().getValue(\"k2\").equals(\"v2\"));\n        assertTrue(jarFile.containsEntry(TEST_ENTRY));\n\n        ZipEntry zipEntry = jarFile.getEntry(TEST_ENTRY);\n        assertTrue(zipEntry.getName().equals(TEST_ENTRY));\n        assertTrue(zipEntry.getComment().equals(TEST_ENTRY_COMMENT));\n        assertTrue(compareByteArray(zipEntry.getExtra(), TEST_ENTRY_EXTRA.getBytes()));\n\n        JarEntry jarEntry = jarFile.getJarEntry(\"lib/junit-4.12.jar\");\n        JarFile nestJarFile = jarFile.getNestedJarFile(jarEntry);\n        Manifest nestManifest = nestJarFile.getManifest();\n        assertTrue(nestManifest.getMainAttributes().getValue(\"Implementation-Title\")\n            .equals(\"JUnit\"));\n        assertTrue(nestManifest.getMainAttributes().getValue(\"Implementation-Version\")\n            .equals(\"4.12\"));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/test/jar/JarUtilsTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.test.jar;\n\nimport com.alipay.sofa.ark.common.util.FileUtils;\nimport com.alipay.sofa.ark.loader.jar.JarUtils;\nimport com.google.common.io.Files;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.reflect.Method;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarFile;\n\nimport static com.alipay.sofa.ark.loader.jar.JarUtils.parseArtifactId;\nimport static java.lang.String.format;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNull;\n\npublic class JarUtilsTest {\n\n    @Test\n    public void getArtifactIdFromTestClassPath() throws IOException {\n        URL url = this.getClass().getClassLoader().getResource(\"sample-biz-withjar.jar\");\n        String artifactId = parseArtifactId(url.getPath());\n        assertEquals(\"sofa-ark-sample-springboot-ark\", artifactId);\n    }\n\n    @Test\n    public void getArtifactIdFromTestClassPath1() throws IOException {\n        URL url = this.getClass().getClassLoader().getResource(\"SampleClass.class\");\n        String artifactId = parseArtifactId(url.getPath());\n        assertEquals(\"sofa-ark-archive\", artifactId);\n    }\n\n    @Test\n    public void getArtifactIdFromClassPath() throws IOException, URISyntaxException {\n\n        URL clazzURL = this.getClass().getClassLoader()\n            .getResource(\"com/alipay/sofa/ark/loader/jar/JarUtils.class\");\n        String artifactId = parseArtifactId(clazzURL.getPath());\n        assertEquals(\"sofa-ark-archive\", artifactId);\n\n        URL testClazzURL = this.getClass().getClassLoader()\n            .getResource(\"com/alipay/sofa/ark/loader/test/jar/JarUtilsTest.class\");\n        artifactId = parseArtifactId(testClazzURL.getPath());\n        assertEquals(\"sofa-ark-archive\", artifactId);\n\n        String path = this.getClass().getClassLoader().getResource(\"example-jarinjarinjar.jar\")\n            .getPath();\n        String artifactId1 = parseArtifactId(path + \"!/lib\");\n        assertEquals(\"example-client\", artifactId1);\n\n        URL fatJarURL = this.getClass().getClassLoader().getResource(\"sample-springboot-fat-biz.jar\");\n        List<URL> urls = extractResourceURLs(fatJarURL.getPath(), \"BOOT-INF/classes/\");\n        assertEquals(1, urls.size());\n        urls.forEach(url -> assertEquals(\"sofa-ark-sample-springboot-ark\", parseArtifactId(url.getPath())));\n    }\n\n    private List<URL> extractResourceURLs(String pathToFatJar, String resourcePattern) {\n        List<URL> resourceUrls = new ArrayList<>();\n        try (JarFile jarFile = new JarFile(new File(pathToFatJar))) {\n\n            Enumeration<JarEntry> entryEnumeration = jarFile.entries();\n            while (entryEnumeration.hasMoreElements()) {\n                JarEntry entry = entryEnumeration.nextElement();\n                String entryName = entry.getName();\n                if (entryName.endsWith(resourcePattern)) { // 检查资源模式匹配\n                    // 构建jar内资源的URL\n                    URL entryUrl = new URL(\"jar:file:\" + pathToFatJar + \"!/\" + entryName);\n                    resourceUrls.add(entryUrl);\n                }\n            }\n        } catch (IOException e) {\n            throw new RuntimeException(\"Failed to extract resource URLs from \" + pathToFatJar, e);\n        }\n\n        return resourceUrls;\n    }\n\n    @Test\n    public void testParseArtifactIdFromJarName() throws Exception {\n\n        Method method = JarUtils.class.getDeclaredMethod(\"doGetArtifactIdFromFileName\",\n            String.class);\n        method.setAccessible(Boolean.TRUE);\n\n        String filePathPrefix = \"file:///home/admin/xxx/xxx/%s.jar\";\n        String artifactId0 = (String) method.invoke(JarUtils.class,\n            format(filePathPrefix, \"dafdfa-2-dafdfad\"));\n        assertNull(artifactId0);\n\n        String artifactId2 = (String) method.invoke(JarUtils.class,\n            format(filePathPrefix, \"dfadfa-dfadfa-3.0\"));\n        assertEquals(artifactId2, \"dfadfa-dfadfa\");\n\n        String artifactId3 = (String) method.invoke(JarUtils.class,\n            format(filePathPrefix, \"hessian-4.0.7.bugfix12-tuning3\"));\n        assertEquals(artifactId3, \"hessian\");\n\n        String artifactId4 = (String) method.invoke(JarUtils.class,\n            format(filePathPrefix, \"hessian-4.0.7\"));\n        assertEquals(artifactId4, \"hessian\");\n    }\n\n    @Test\n    public void testParseArtifactIdFromJarInJarName() throws Exception {\n\n        Method method = JarUtils.class.getDeclaredMethod(\"doGetArtifactIdFromFileName\",\n            String.class);\n        method.setAccessible(Boolean.TRUE);\n\n        String filePathPrefix = \"file:///home/admin/xxx/xxx/bootstrap-executable.jar!/META-INF/lib/%s.jar\";\n        String artifactId0 = (String) method.invoke(JarUtils.class,\n            format(filePathPrefix, \"dafdfa-2-dafdfad\"));\n        assertNull(artifactId0);\n\n        String artifactId1 = (String) method.invoke(JarUtils.class,\n            format(filePathPrefix, \"jar-2-version-suffix\"));\n        assertNull(artifactId1);\n\n        String artifactId2 = (String) method.invoke(JarUtils.class,\n            format(filePathPrefix, \"dfadfa-dfadfa-3.0\"));\n        assertEquals(artifactId2, \"dfadfa-dfadfa\");\n\n        String artifactId3 = (String) method.invoke(JarUtils.class,\n            format(filePathPrefix, \"hessian-4.0.7.bugfix12-tuning3\"));\n        assertEquals(artifactId3, \"hessian\");\n\n        String artifactId4 = (String) method.invoke(JarUtils.class,\n            format(filePathPrefix, \"hessian-4.0.7\"));\n        assertEquals(artifactId4, \"hessian\");\n    }\n\n    @Test\n    public void testParseArtifactIdFromJarInJar() throws Exception {\n        URL jar = JarUtilsTest.class.getResource(\"/sample-biz-withjar.jar\");\n        Method method = JarUtils.class.getDeclaredMethod(\"parseArtifactIdFromJar\", String.class);\n        method.setAccessible(Boolean.TRUE);\n        assertEquals(\"slf4j-api\",\n            method.invoke(JarUtils.class, jar.getFile() + \"!/lib/slf4j-api-1.7.30.jar\"));\n    }\n\n    @Test\n    public void testParseArtifactIdFromJarInJarPom() {\n        URL jar = JarUtilsTest.class.getResource(\"/sample-biz-withjar.jar\");\n        String artifactId0 = parseArtifactId(jar.getFile() + \"!/lib/slf4j-api-1.7.30.jar!/\");\n        assertEquals(\"slf4j-api\", artifactId0);\n    }\n\n    @Test\n    public void testParseArtifactIdFromJarWithBlankPath() throws Exception {\n\n        URL jar = JarUtilsTest.class.getResource(\"/junit-4.12.jar\");\n        URL root = JarUtilsTest.class.getResource(\"/\");\n        String fullPath = root.getPath() + \"space directory\";\n        String jarLocation = fullPath + \"/junit-4.12.jar\";\n        FileUtils.mkdir(fullPath);\n        Files.copy(FileUtils.file(jar.getFile()), FileUtils.file(jarLocation));\n\n        URL url = JarUtilsTest.class.getResource(\"/space directory/junit-4.12.jar\");\n        Method method = JarUtils.class.getDeclaredMethod(\"parseArtifactIdFromJar\", String.class);\n        method.setAccessible(Boolean.TRUE);\n        assertNull(method.invoke(JarUtils.class, url.getPath()));\n    }\n\n    @Test\n    public void testParseArtifactIdFromJarInJarInJarMore() {\n        URL jar = JarUtilsTest.class.getResource(\"/example-jarinjarinjar.jar\");\n        String artifactId0 = parseArtifactId(jar.getFile()\n                                             + \"!/BOOT-INF/lib/example-client-2.0.0.jar!/BOOT-INF/lib/sofa-ark-spring-guides-230525-SOFA.jar!/\");\n        assertEquals(\"sofa-ark-spring-guides\", artifactId0);\n        String artifactId1 = parseArtifactId(jar.getFile()\n                                             + \"!/BOOT-INF/lib/example-client-2.0.0.jar!/BOOT-INF/lib/example-client-3.0.0.jar!/\");\n        assertEquals(\"example-client\", artifactId1);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/java/com/alipay/sofa/ark/loader/test/util/ModifyPathUtilsTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.loader.test.util;\n\nimport com.alipay.sofa.ark.loader.util.ModifyPathUtils;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author sususama\n * @since 2023-05-08\n */\npublic class ModifyPathUtilsTest {\n    @Test\n    public void modifyPathTest() {\n        String path = \"/C:/Users/XXX/Desktop/XXX-ark-biz.jar\";\n        Assert.assertEquals(\"C:/Users/XXX/Desktop/XXX-ark-biz.jar\",\n            ModifyPathUtils.modifyPath(path));\n        String path1 = \"C:/Users/XXX/Desktop/XXX-ark-biz.jar\";\n        Assert.assertEquals(\"C:/Users/XXX/Desktop/XXX-ark-biz.jar\",\n            ModifyPathUtils.modifyPath(path1));\n        String path2 = \"/home/user/XXX/test/XXX-ark-biz.jar\";\n        Assert.assertEquals(\"/home/user/XXX/test/XXX-ark-biz.jar\",\n            ModifyPathUtils.modifyPath(path2));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/resources/conf/ark/bootstrap.properties",
    "content": "profile=default"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/resources/conf/ark/log/logback-conf.xml",
    "content": ""
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/resources/empty-file",
    "content": ""
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/resources/exploded-archive-test/META-INF/MANIFEST.MF",
    "content": "Manifest-Version: 1.0.0\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/resources/pom-properties/pom.properties",
    "content": "a=b"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/resources/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <artifactId>sofa-ark-core-impl</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n    </parent>\n\n    <artifactId>sofa-ark-archive</artifactId>\n\n    <dependencies>\n\n        <!--SOFAArk modules-->\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-spi</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-api</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-common</artifactId>\n        </dependency>\n\n        <!--third party libraries-->\n        <dependency>\n            <groupId>commons-io</groupId>\n            <artifactId>commons-io</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-model</artifactId>\n        </dependency>\n\n        <!--test-->\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-inline</artifactId>\n            <version>${mockito.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-loader</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/archive/src/test/resources/xxxxx.jar-unpack/pom.properties",
    "content": "#Generated by Maven\n#Wed Jan 28 11:40:04 CST 2026\nartifactId=xxxx-test\ngroupId=com.xxxx.base\nversion=0.0.1\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Module Overview\n\n**Artifact ID**: `sofa-ark-container`\n**Package**: `com.alipay.sofa.ark.container`\n\nThis is the core implementation of the Ark Container - the runtime that manages the entire SOFAArk lifecycle including plugins and business modules.\n\n## Purpose\n\n- Ark container startup and shutdown\n- Manage plugins and business modules lifecycle\n- Provide classloader isolation\n- Service registry and dependency injection\n- Telnet command server for runtime management\n\n## Key Classes\n\n### Container Entry\n- `ArkContainer` - Main entry point, manages container lifecycle\n  - `main(String[] args)` - Static entry for JAR execution\n  - `start()` - Start the container\n  - `stop()` - Stop the container\n\n### Pipeline Stages (`pipeline/`)\nStartup stages executed in order:\n1. `HandleArchiveStage` - Parse archives\n2. `RegisterServiceStage` - Register core services\n3. `ExtensionLoaderStage` - Load SPI extensions\n4. `DeployPluginStage` - Start plugins\n5. `DeployBizStage` - Start business modules\n6. `FinishStartupStage` - Complete startup\n\nStandardPipeline.java:47-60 defines the startup sequence.\n\n### Service Implementations (`service/`)\n- `ArkServiceContainer` - Guice-based service container\n- `biz.BizManagerServiceImpl` - Biz lifecycle management\n- `biz.BizFactoryServiceImpl` - Create Biz instances\n- `plugin.PluginManagerServiceImpl` - Plugin management\n- `plugin.PluginFactoryServiceImpl` - Create Plugin instances\n- `classloader.ClassLoaderServiceImpl` - Classloader management\n- `classloader.BizClassLoader` - Business module classloader\n- `classloader.PluginClassLoader` - Plugin classloader\n- `injection.InjectionServiceImpl` - Dependency injection\n- `event.EventAdminServiceImpl` - Event dispatching\n\n### Session/Command (`session/`)\n- `StandardTelnetServerImpl` - Telnet server for runtime commands\n- `NettyTelnetServer` - Netty-based telnet server\n- Command handlers for: biz, plugin, info queries\n\n## ClassLoader Architecture\n\n```\nBootstrap ClassLoader\n       ↓\nArk Container ClassLoader (loads sofa-ark-all)\n       ↓\nPluginClassLoader (bidirectional delegation between plugins)\n       ↓\nBizClassLoader (delegates to plugins, isolated from other biz)\n```\n\n## Dependencies\n\n- `sofa-ark-spi` - Service interfaces\n- `sofa-ark-api` - API layer\n- `sofa-ark-archive` - Archive loading\n- `sofa-ark-common` - Utilities\n- `guice` - Dependency injection\n\n## Used By\n\n- `sofa-ark-all` - Aggregates this module\n- Application at runtime (via `java -jar`)"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <artifactId>sofa-ark-core-impl</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n    </parent>\n\n    <artifactId>sofa-ark-container</artifactId>\n    <name>${project.groupId}:${project.artifactId}</name>\n\n    <dependencies>\n\n        <!--SOFAArk modules-->\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-spi</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-api</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-common</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-exception</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-archive</artifactId>\n        </dependency>\n\n        <!--third party libraries-->\n        <dependency>\n            <groupId>com.google.inject</groupId>\n            <artifactId>guice</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.netty</groupId>\n            <artifactId>netty-all</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.taobao.text</groupId>\n            <artifactId>text-ui</artifactId>\n            <version>0.0.3</version>\n        </dependency>\n\n        <dependency>\n            <groupId>net.bytebuddy</groupId>\n            <artifactId>byte-buddy-agent</artifactId>\n            <version>1.14.4</version>\n        </dependency>\n\n        <!--test-->\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-inline</artifactId>\n            <version>${mockito.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.github.stefanbirkner</groupId>\n            <artifactId>system-rules</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/ArkContainer.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.bootstrap.ClasspathLauncher.ClassPathArchive;\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.common.util.AssertUtils;\nimport com.alipay.sofa.ark.common.util.FileUtils;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.container.pipeline.DeployBizStage;\nimport com.alipay.sofa.ark.container.pipeline.HandleArchiveStage;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainer;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.loader.EmbedClassPathArchive;\nimport com.alipay.sofa.ark.loader.ExecutableArkBizJar;\nimport com.alipay.sofa.ark.loader.archive.ExplodedArchive;\nimport com.alipay.sofa.ark.loader.archive.JarFileArchive;\nimport com.alipay.sofa.ark.spi.archive.BizArchive;\nimport com.alipay.sofa.ark.spi.archive.ExecutableArchive;\nimport com.alipay.sofa.ark.spi.argument.LaunchCommand;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.pipeline.Pipeline;\nimport com.alipay.sofa.ark.spi.pipeline.PipelineContext;\nimport com.alipay.sofa.ark.spi.service.biz.AddBizToStaticDeployHook;\nimport com.alipay.sofa.ark.spi.service.extension.ArkServiceLoader;\nimport com.alipay.sofa.common.log.MultiAppLoggerSpaceManager;\nimport com.alipay.sofa.common.log.SpaceId;\nimport com.alipay.sofa.common.log.SpaceInfo;\nimport com.alipay.sofa.common.log.env.LogEnvUtils;\nimport com.alipay.sofa.common.log.factory.LogbackLoggerSpaceFactory;\nimport com.alipay.sofa.common.utils.ReportUtil;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.ARK_CONF_FILE;\nimport static com.alipay.sofa.ark.spi.constant.Constants.ARK_CONF_FILE_FORMAT;\nimport static com.alipay.sofa.common.log.Constants.LOGGING_PATH_DEFAULT;\nimport static com.alipay.sofa.common.log.Constants.LOG_ENCODING_PROP_KEY;\nimport static com.alipay.sofa.common.log.Constants.LOG_PATH;\nimport static com.alipay.sofa.common.log.Constants.UTF8_STR;\n\n/**\n * Ark Container Entry\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class ArkContainer {\n\n    private ArkServiceContainer            arkServiceContainer;\n\n    private PipelineContext                pipelineContext;\n\n    private AtomicBoolean                  started           = new AtomicBoolean(false);\n\n    private AtomicBoolean                  stopped           = new AtomicBoolean(false);\n\n    private long                           start             = System.currentTimeMillis();\n\n    private List<AddBizToStaticDeployHook> addBizToStaticDeployHooks;\n\n    /**\n     * -Aclasspath or -Ajar is needed at lease. it specify the abstract executable ark archive,\n     * default added by container itself\n     */\n    private static final int               MINIMUM_ARGS_SIZE = 1;\n\n    public static Object main(String[] args) throws ArkRuntimeException {\n        if (args.length < MINIMUM_ARGS_SIZE) {\n            throw new ArkRuntimeException(\"Please provide suitable arguments to continue !\");\n        }\n\n        try {\n            LaunchCommand launchCommand = LaunchCommand.parse(args);\n            if (launchCommand.isExecutedByCommandLine()) {\n                ExecutableArkBizJar executableArchive;\n                File rootFile = FileUtils.file(launchCommand.getExecutableArkBizJar().getFile());\n                if (rootFile.isDirectory()) {\n                    executableArchive = new ExecutableArkBizJar(new ExplodedArchive(rootFile));\n                } else {\n                    executableArchive = new ExecutableArkBizJar(new JarFileArchive(rootFile,\n                        launchCommand.getExecutableArkBizJar()));\n                }\n                return new ArkContainer(executableArchive, launchCommand).start();\n            } else {\n                ClassPathArchive classPathArchive;\n                if (ArkConfigs.isEmbedEnable()) {\n                    classPathArchive = new EmbedClassPathArchive(launchCommand.getEntryClassName(),\n                        launchCommand.getEntryMethodName(), launchCommand.getClasspath());\n                } else {\n                    classPathArchive = new ClassPathArchive(launchCommand.getEntryClassName(),\n                        launchCommand.getEntryMethodName(), launchCommand.getClasspath());\n                }\n                return new ArkContainer(classPathArchive, launchCommand).start();\n            }\n        } catch (IOException e) {\n            throw new ArkRuntimeException(String.format(\"SOFAArk startup failed, commandline=%s\",\n                LaunchCommand.toString(args)), e);\n        }\n    }\n\n    public ArkContainer(ExecutableArchive executableArchive) throws Exception {\n        this(executableArchive, new LaunchCommand().setExecutableArkBizJar(executableArchive\n            .getUrl()));\n    }\n\n    public ArkContainer(ExecutableArchive executableArchive, LaunchCommand launchCommand) {\n        arkServiceContainer = new ArkServiceContainer(launchCommand.getLaunchArgs());\n        pipelineContext = new PipelineContext();\n        pipelineContext.setExecutableArchive(executableArchive);\n        pipelineContext.setLaunchCommand(launchCommand);\n    }\n\n    /**\n     * Start Ark Container\n     *\n     * @throws ArkRuntimeException\n     * @since 0.1.0\n     */\n    public Object start() throws ArkRuntimeException {\n        AssertUtils.assertNotNull(arkServiceContainer, \"arkServiceContainer is null !\");\n        if (started.compareAndSet(false, true)) {\n            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {\n                @Override\n                public void run() {\n                    stop();\n                }\n            }));\n            prepareArkConfig();\n            // don't remove this log print, add to init log space first before initialize ArkLogger\n            ArkLoggerFactory.getDefaultLogger().info(\"Ark container starting...\");\n            reInitializeArkLogger();\n            arkServiceContainer.start();\n            Pipeline pipeline = arkServiceContainer.getService(Pipeline.class);\n            pipeline.process(pipelineContext);\n\n            System.out.println(\"Ark container started in \" + (System.currentTimeMillis() - start) //NOPMD\n                               + \" ms.\");\n        }\n        return this;\n    }\n\n    public Object deployBizAfterMasterBizReady() throws Exception {\n        // Scan all biz in classpath\n        HandleArchiveStage handleArchiveStage = ArkServiceContainerHolder.getContainer()\n            .getService(HandleArchiveStage.class);\n        handleArchiveStage.processStaticBizFromClasspath(pipelineContext);\n\n        // execute beforeEmbedStaticDeployBizHook\n        addStaticBizFromCustomHooks();\n\n        // start up\n        DeployBizStage deployBizStage = ArkServiceContainerHolder.getContainer().getService(\n            DeployBizStage.class);\n        deployBizStage.processStaticBiz(pipelineContext);\n        return this;\n    }\n\n    private void addStaticBizFromCustomHooks() throws Exception {\n        addBizToStaticDeployHooks = ArkServiceLoader.loadExtensionsFromArkBiz(\n            AddBizToStaticDeployHook.class, ArkClient.getMasterBiz().getIdentity());\n        for (AddBizToStaticDeployHook hook : addBizToStaticDeployHooks) {\n            List<BizArchive> bizsFromHook = hook.getStaticBizToAdd();\n            addStaticBiz(bizsFromHook);\n        }\n    }\n\n    private void addStaticBiz(List<BizArchive> bizArchives) throws IOException {\n        if (null == bizArchives) {\n            return;\n        }\n\n        for (BizArchive bizArchive : bizArchives) {\n            Biz biz = ArkClient.getBizFactoryService().createBiz(bizArchive);\n            ArkClient.getBizManagerService().registerBiz(biz);\n        }\n    }\n\n    /**\n     * Prepare to read ark conf\n     * @throws ArkRuntimeException\n     */\n    public void prepareArkConfig() throws ArkRuntimeException {\n        try {\n            // Forbid to Monitoring and Management Using JMX, because it leads to conflict when setup multi spring boot app.\n            ArkConfigs.setSystemProperty(Constants.SPRING_BOOT_ENDPOINTS_JMX_ENABLED,\n                String.valueOf(false));\n            // ignore thread class loader when loading classes and resource in log4j\n            ArkConfigs.setSystemProperty(Constants.LOG4J_IGNORE_TCL, String.valueOf(true));\n\n            // compatible sofa-hessian4, refer to https://github.com/sofastack/sofa-hessian/issues/38\n            ArkConfigs.setSystemProperty(Constants.RESOLVE_PARENT_CONTEXT_SERIALIZER_FACTORY,\n                \"false\");\n\n            // read ark conf file\n            List<URL> urls = getProfileConfFiles(pipelineContext.getLaunchCommand().getProfiles());\n            ArkConfigs.init(urls);\n        } catch (Throwable throwable) {\n            throw new ArkRuntimeException(throwable);\n        }\n    }\n\n    public List<URL> getProfileConfFiles(String... profiles) {\n        List<URL> urls = new ArrayList<>();\n        for (String profile : profiles) {\n            URL url;\n            if (StringUtils.isEmpty(profile)) {\n                url = this.getClass().getClassLoader().getResource(ARK_CONF_FILE);\n            } else {\n                url = this.getClass().getClassLoader()\n                    .getResource(String.format(ARK_CONF_FILE_FORMAT, profile));\n            }\n            if (url != null) {\n                urls.add(url);\n            } else if (!StringUtils.isEmpty(profile)) {\n                ReportUtil.reportWarn(String.format(\"The %s conf file is not found.\", profile));\n            }\n        }\n        return urls;\n    }\n\n    /**\n     * reInitialize Ark Logger\n     *\n     * @throws ArkRuntimeException\n     */\n    public void reInitializeArkLogger() throws ArkRuntimeException {\n        for (Map.Entry<SpaceId, SpaceInfo> entry : ((Map<SpaceId, SpaceInfo>) MultiAppLoggerSpaceManager\n            .getSpacesMap()).entrySet()) {\n            SpaceId spaceId = entry.getKey();\n            SpaceInfo spaceInfo = entry.getValue();\n            if (!ArkLoggerFactory.SOFA_ARK_LOGGER_SPACE.equals(spaceId.getSpaceName())) {\n                continue;\n            }\n            LogbackLoggerSpaceFactory arkLoggerSpaceFactory = (LogbackLoggerSpaceFactory) spaceInfo\n                .getAbstractLoggerSpaceFactory();\n            Map<String, String> arkLogConfig = new HashMap<>();\n            // set base logging.path\n            arkLogConfig.put(LOG_PATH, ArkConfigs.getStringValue(LOG_PATH, LOGGING_PATH_DEFAULT));\n            // set log file encoding\n            arkLogConfig.put(LOG_ENCODING_PROP_KEY,\n                ArkConfigs.getStringValue(LOG_ENCODING_PROP_KEY, UTF8_STR));\n            // set other log config\n            for (String key : ArkConfigs.keySet()) {\n                if (LogEnvUtils.filterAllLogConfig(key)) {\n                    arkLogConfig.put(key, ArkConfigs.getStringValue(key));\n                }\n            }\n            arkLoggerSpaceFactory.reInitialize(arkLogConfig);\n        }\n    }\n\n    /**\n     * Whether Ark Container is started or not\n     *\n     * @return\n     */\n    public boolean isStarted() {\n        return started.get();\n    }\n\n    /**\n     * Stop Ark Container\n     *\n     * @throws ArkRuntimeException\n     */\n    public void stop() throws ArkRuntimeException {\n        AssertUtils.assertNotNull(arkServiceContainer, \"arkServiceContainer is null !\");\n        if (stopped.compareAndSet(false, true)) {\n            arkServiceContainer.stop();\n        }\n    }\n\n    /**\n     * Whether Ark Container is running or not\n     * @return\n     */\n    public boolean isRunning() {\n        return isStarted() && !stopped.get();\n    }\n\n    /**\n     * Get {@link ArkServiceContainer} of ark container\n     *\n     * @return\n     */\n    public ArkServiceContainer getArkServiceContainer() {\n        return arkServiceContainer;\n    }\n\n    /**\n     * Get {@link PipelineContext} of ark container\n     *\n     * @return\n     */\n    public PipelineContext getPipelineContext() {\n        return pipelineContext;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/guice/ContainerModule.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.guice;\n\nimport com.alipay.sofa.ark.common.guice.AbstractArkGuiceModule;\nimport com.alipay.sofa.ark.container.service.biz.BizDeployServiceImpl;\nimport com.alipay.sofa.ark.container.service.biz.BizFactoryServiceImpl;\nimport com.alipay.sofa.ark.container.service.biz.BizManagerServiceImpl;\nimport com.alipay.sofa.ark.container.service.event.EventAdminServiceImpl;\nimport com.alipay.sofa.ark.container.service.extension.ExtensionLoaderServiceImpl;\nimport com.alipay.sofa.ark.container.service.injection.InjectionServiceImpl;\nimport com.alipay.sofa.ark.container.service.plugin.PluginFactoryServiceImpl;\nimport com.alipay.sofa.ark.container.service.plugin.PluginManagerServiceImpl;\nimport com.alipay.sofa.ark.container.pipeline.StandardPipeline;\nimport com.alipay.sofa.ark.container.service.classloader.ClassLoaderServiceImpl;\nimport com.alipay.sofa.ark.container.service.plugin.PluginDeployServiceImpl;\nimport com.alipay.sofa.ark.container.service.registry.RegistryServiceImpl;\nimport com.alipay.sofa.ark.container.session.StandardTelnetServerImpl;\nimport com.alipay.sofa.ark.spi.service.biz.BizDeployService;\nimport com.alipay.sofa.ark.spi.service.biz.BizFactoryService;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.event.EventAdminService;\nimport com.alipay.sofa.ark.spi.service.extension.ExtensionLoaderService;\nimport com.alipay.sofa.ark.spi.service.injection.InjectionService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginFactoryService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport com.alipay.sofa.ark.spi.pipeline.Pipeline;\nimport com.alipay.sofa.ark.spi.service.ArkService;\nimport com.alipay.sofa.ark.spi.service.classloader.ClassLoaderService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginDeployService;\nimport com.alipay.sofa.ark.spi.service.registry.RegistryService;\nimport com.alipay.sofa.ark.spi.service.session.TelnetServerService;\nimport com.google.inject.multibindings.Multibinder;\n\n/**\n * Guice module for ark container\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class ContainerModule extends AbstractArkGuiceModule {\n\n    @Override\n    protected void configure() {\n        binder().bind(Pipeline.class).to(StandardPipeline.class);\n\n        Multibinder<ArkService> arkServiceMultibinder = Multibinder.newSetBinder(binder(),\n            ArkService.class);\n        arkServiceMultibinder.addBinding().to(PluginDeployServiceImpl.class);\n        arkServiceMultibinder.addBinding().to(BizDeployServiceImpl.class);\n        arkServiceMultibinder.addBinding().to(ClassLoaderServiceImpl.class);\n        arkServiceMultibinder.addBinding().to(StandardTelnetServerImpl.class);\n\n        binder().bind(PluginManagerService.class).to(PluginManagerServiceImpl.class);\n        binder().bind(BizManagerService.class).to(BizManagerServiceImpl.class);\n        binder().bind(ClassLoaderService.class).to(ClassLoaderServiceImpl.class);\n        binder().bind(PluginDeployService.class).to(PluginDeployServiceImpl.class);\n        binder().bind(BizDeployService.class).to(BizDeployServiceImpl.class);\n        binder().bind(RegistryService.class).to(RegistryServiceImpl.class);\n        binder().bind(InjectionService.class).to(InjectionServiceImpl.class);\n        binder().bind(TelnetServerService.class).to(StandardTelnetServerImpl.class);\n        binder().bind(BizFactoryService.class).to(BizFactoryServiceImpl.class);\n        binder().bind(PluginFactoryService.class).to(PluginFactoryServiceImpl.class);\n        binder().bind(ExtensionLoaderService.class).to(ExtensionLoaderServiceImpl.class);\n        binder().bind(EventAdminService.class).to(EventAdminServiceImpl.class);\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/model/BizModel.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.model;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.bootstrap.MainMethodRunner;\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.common.util.AssertUtils;\nimport com.alipay.sofa.ark.common.util.BizIdentityUtils;\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.common.util.ParseUtils;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.container.service.classloader.AbstractClasspathClassLoader;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.loader.jar.JarUtils;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.event.biz.AfterBizStartupEvent;\nimport com.alipay.sofa.ark.spi.event.biz.AfterBizStartupFailedEvent;\nimport com.alipay.sofa.ark.spi.event.biz.AfterBizStopEvent;\nimport com.alipay.sofa.ark.spi.event.biz.AfterBizStopFailedEvent;\nimport com.alipay.sofa.ark.spi.event.biz.BeforeBizRecycleEvent;\nimport com.alipay.sofa.ark.spi.event.biz.BeforeBizStartupEvent;\nimport com.alipay.sofa.ark.spi.event.biz.BeforeBizStopEvent;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.BizState;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.event.EventAdminService;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.net.URL;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.Date;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.BIZ_TEMP_WORK_DIR_RECYCLE_FILE_SUFFIX;\nimport static com.alipay.sofa.ark.spi.constant.Constants.ACTIVATE_MULTI_BIZ_VERSION_ENABLE;\nimport static com.alipay.sofa.ark.spi.constant.Constants.REMOVE_BIZ_INSTANCE_AFTER_STOP_FAILED;\nimport static org.apache.commons.io.FileUtils.deleteQuietly;\n\n/**\n * Ark Biz Standard Model\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class BizModel implements Biz {\n    private String               bizName;\n\n    private String               bizVersion;\n\n    private BizState             bizState;\n\n    private String               mainClass;\n\n    private String               webContextPath;\n\n    private URL[]                urls;\n\n    private URL                  bizUrl;\n\n    private URL[]                pluginUrls;\n\n    private ClassLoader          classLoader;\n\n    private Map<String, String>  attributes                    = new ConcurrentHashMap<>();\n\n    private int                  priority                      = DEFAULT_PRECEDENCE;\n\n    private Set<String>          denyImportPackages;\n\n    private Set<String>          denyImportPackageNodes        = new HashSet<>();\n\n    private Set<String>          denyImportPackageStems        = new HashSet<>();\n\n    private Set<String>          denyImportClasses;\n\n    private Set<String>          denyImportResources           = new HashSet<>();\n\n    private Set<String>          injectPluginDependencies      = new HashSet<>();\n    private Set<String>          injectExportPackages          = new HashSet<>();\n\n    private Set<String>          declaredLibraries             = new LinkedHashSet<>();\n    private Map<String, Boolean> declaredCacheMap              = new ConcurrentHashMap<>();\n\n    private Set<String>          denyPrefixImportResourceStems = new HashSet<>();\n\n    private Set<String>          denySuffixImportResourceStems = new HashSet<>();\n\n    private File                 bizTempWorkDir;\n\n    private List<BizStateRecord> bizStateRecords               = new CopyOnWriteArrayList<>();\n\n    private Set<Plugin>          dependentPlugins              = new HashSet<>();\n\n    public BizModel setBizName(String bizName) {\n        AssertUtils.isFalse(StringUtils.isEmpty(bizName), \"Biz Name must not be empty!\");\n        this.bizName = bizName;\n        return this;\n    }\n\n    public BizModel setBizVersion(String bizVersion) {\n        AssertUtils.isFalse(StringUtils.isEmpty(bizVersion), \"Biz Version must not be empty!\");\n        this.bizVersion = bizVersion;\n        return this;\n    }\n\n    public BizModel setBizState(BizState bizState) {\n        this.bizState = bizState;\n        addStateChangeLog(StateChangeReason.UNDEFINE, \"\");\n        return this;\n    }\n\n    public BizModel setBizState(BizState bizState, StateChangeReason reason) {\n        this.bizState = bizState;\n        addStateChangeLog(reason, \"\");\n        return this;\n    }\n\n    public BizModel setBizState(BizState bizState, StateChangeReason reason, String message) {\n        this.bizState = bizState;\n        addStateChangeLog(reason, message);\n        return this;\n    }\n\n    public BizModel setMainClass(String mainClass) {\n        AssertUtils.isFalse(StringUtils.isEmpty(mainClass), \"Biz Main Class must not be empty!\");\n        this.mainClass = mainClass;\n        return this;\n    }\n\n    public BizModel setClassPath(URL[] urls) {\n        this.urls = urls;\n        return this;\n    }\n\n    public BizModel setBizUrl(URL url) {\n        this.bizUrl = url;\n        return this;\n    }\n\n    public BizModel setPluginClassPath(URL[] urls) {\n        this.pluginUrls = urls;\n        return this;\n    }\n\n    public BizModel setClassLoader(ClassLoader classLoader) {\n        this.classLoader = classLoader;\n        return this;\n    }\n\n    public BizModel setPriority(String priority) {\n        this.priority = (priority == null ? DEFAULT_PRECEDENCE : Integer.valueOf(priority));\n        return this;\n    }\n\n    public BizModel setWebContextPath(String webContextPath) {\n        this.webContextPath = (webContextPath == null ? Constants.ROOT_WEB_CONTEXT_PATH\n            : webContextPath);\n        return this;\n    }\n\n    public BizModel setDenyImportPackages(String denyImportPackages) {\n        this.denyImportPackages = StringUtils.strToSet(denyImportPackages,\n            Constants.MANIFEST_VALUE_SPLIT);\n        ParseUtils.parsePackageNodeAndStem(this.denyImportPackages, this.denyImportPackageStems,\n            this.denyImportPackageNodes);\n        return this;\n    }\n\n    public BizModel setDenyImportClasses(String denyImportClasses) {\n        this.denyImportClasses = StringUtils.strToSet(denyImportClasses,\n            Constants.MANIFEST_VALUE_SPLIT);\n        return this;\n    }\n\n    public BizModel setDenyImportResources(String denyImportResources) {\n        ParseUtils.parseResourceAndStem(\n            StringUtils.strToSet(denyImportResources, Constants.MANIFEST_VALUE_SPLIT),\n            this.denyPrefixImportResourceStems, denySuffixImportResourceStems,\n            this.denyImportResources);\n        return this;\n    }\n\n    public BizModel setAttribute(String key, String val) {\n        attributes.put(key, val);\n        return this;\n    }\n\n    public BizModel setAttributes(Map<String, String> attributes) {\n        this.attributes.putAll(attributes);\n        return this;\n    }\n\n    public BizModel setInjectPluginDependencies(Set<String> injectPluginDependencies) {\n        this.injectPluginDependencies = injectPluginDependencies;\n        return this;\n    }\n\n    public BizModel setInjectExportPackages(String injectExportPackages) {\n        this.injectExportPackages = StringUtils.strToSet(injectExportPackages,\n            Constants.MANIFEST_VALUE_SPLIT);\n        return this;\n    }\n\n    public Set<String> getInjectExportPackages() {\n        return injectExportPackages;\n    }\n\n    private void addStateChangeLog(StateChangeReason reason, String message) {\n        bizStateRecords.add(new BizStateRecord(new Date(), bizState, reason, message));\n    }\n\n    public Set<Plugin> getDependentPlugins() {\n        return dependentPlugins;\n    }\n\n    public BizModel setDependentPlugins(Set<Plugin> dependentPlugins) {\n        this.dependentPlugins = dependentPlugins;\n        return this;\n    }\n\n    @Override\n    public String getBizName() {\n        return bizName;\n    }\n\n    @Override\n    public String getBizVersion() {\n        return bizVersion;\n    }\n\n    @Override\n    public String getIdentity() {\n        return BizIdentityUtils.generateBizIdentity(this);\n    }\n\n    @Override\n    public String getMainClass() {\n        return mainClass;\n    }\n\n    @Override\n    public URL[] getClassPath() {\n        return urls;\n    }\n\n    @Override\n    public URL getBizUrl() {\n        return bizUrl;\n    }\n\n    @Override\n    public int getPriority() {\n        return priority;\n    }\n\n    @Override\n    public ClassLoader getBizClassLoader() {\n        return classLoader;\n    }\n\n    @Override\n    public Set<String> getDenyImportPackages() {\n        return denyImportPackages;\n    }\n\n    @Override\n    public Set<String> getDenyImportPackageNodes() {\n        return denyImportPackageNodes;\n    }\n\n    @Override\n    public Set<String> getDenyImportPackageStems() {\n        return denyImportPackageStems;\n    }\n\n    @Override\n    public Set<String> getDenyImportClasses() {\n        return denyImportClasses;\n    }\n\n    @Override\n    public Set<String> getDenyImportResources() {\n        return denyImportResources;\n    }\n\n    public Set<String> getInjectPluginDependencies() {\n        return injectPluginDependencies;\n    }\n\n    @Override\n    public Set<String> getDenyPrefixImportResourceStems() {\n        return denyPrefixImportResourceStems;\n    }\n\n    @Override\n    public Set<String> getDenySuffixImportResourceStems() {\n        return denySuffixImportResourceStems;\n    }\n\n    @Override\n    public void start(String[] args) throws Throwable {\n        doStart(args, null);\n    }\n\n    @Override\n    public void start(String[] args, Map<String, String> envs) throws Throwable {\n        doStart(args, envs);\n    }\n\n    private void doStart(String[] args, Map<String, String> envs) throws Throwable {\n        AssertUtils.isTrue(bizState == BizState.RESOLVED, \"BizState must be RESOLVED\");\n\n        // support specify mainClass by env\n        if (envs != null) {\n            String mainClassFromEnv = envs.get(Constants.BIZ_MAIN_CLASS);\n            if (mainClassFromEnv != null) {\n                mainClass = mainClassFromEnv;\n                ArkLoggerFactory.getDefaultLogger().info(\n                    \"Ark biz {} will start with main class {} from envs\", getIdentity(),\n                    mainClassFromEnv);\n            }\n        }\n\n        if (mainClass == null) {\n            throw new ArkRuntimeException(String.format(\"biz: %s has no main method\", getBizName()));\n        }\n        ClassLoader oldClassLoader = ClassLoaderUtils.pushContextClassLoader(this.classLoader);\n        EventAdminService eventAdminService = ArkServiceContainerHolder.getContainer().getService(\n            EventAdminService.class);\n        try {\n            eventAdminService.sendEvent(new BeforeBizStartupEvent(this));\n            resetProperties();\n            if (!isMasterBizAndEmbedEnable()) {\n                long start = System.currentTimeMillis();\n                ArkLoggerFactory.getDefaultLogger().info(\"Ark biz {} start.\", getIdentity());\n                MainMethodRunner mainMethodRunner = new MainMethodRunner(mainClass, args, envs);\n                mainMethodRunner.run();\n                // this can trigger health checker handler\n                eventAdminService.sendEvent(new AfterBizStartupEvent(this));\n                ArkLoggerFactory.getDefaultLogger().info(\"Ark biz {} started in {} ms\",\n                    getIdentity(), (System.currentTimeMillis() - start));\n            }\n        } catch (Throwable e) {\n            setBizState(BizState.BROKEN, StateChangeReason.INSTALL_FAILED, getStackTraceAsString(e));\n            eventAdminService.sendEvent(new AfterBizStartupFailedEvent(this, e));\n            throw e;\n        } finally {\n            ClassLoaderUtils.popContextClassLoader(oldClassLoader);\n        }\n\n        BizManagerService bizManagerService = ArkServiceContainerHolder.getContainer().getService(\n            BizManagerService.class);\n        ReentrantLock bizLock = bizManagerService.getBizLock(bizName);\n\n        // lock the bizName to ensure biz with same name install in sequence\n        bizLock.lock();\n        try {\n\n            // case0: active the first module as activated\n            if (bizManagerService.getActiveBiz(bizName) == null) {\n                setBizState(BizState.ACTIVATED, StateChangeReason.STARTED);\n                return;\n            }\n\n            // case1: support multiple version biz as activated: always activate the new version and keep the old module activated\n            boolean activateMultiBizVersion = Boolean.parseBoolean(ArkConfigs.getStringValue(\n                ACTIVATE_MULTI_BIZ_VERSION_ENABLE, \"false\"));\n            if (activateMultiBizVersion) {\n                setBizState(BizState.ACTIVATED, StateChangeReason.STARTED);\n                return;\n            }\n\n            // case2: always activate the new version and deactivate the old module according to ACTIVATE_NEW_MODULE config\n            if (Boolean.getBoolean(Constants.ACTIVATE_NEW_MODULE)) {\n                Biz currentActiveBiz = bizManagerService.getActiveBiz(bizName);\n                ((BizModel) currentActiveBiz).setBizState(BizState.DEACTIVATED,\n                    StateChangeReason.SWITCHED,\n                    String.format(\"switch to new biz %s\", getIdentity()));\n                setBizState(BizState.ACTIVATED, StateChangeReason.STARTED,\n                    String.format(\"switch from old biz: %s\", currentActiveBiz.getIdentity()));\n            } else {\n                // case3: always deactivate the new version and keep old module activated according to ACTIVATE_NEW_MODULE config\n                setBizState(BizState.DEACTIVATED, StateChangeReason.STARTED,\n                    \"start but is deactivated\");\n            }\n        } finally {\n            // ensure the lock will be released, avoid deadlock\n            bizLock.unlock();\n        }\n    }\n\n    @Override\n    public void stop() {\n        AssertUtils.isTrue(bizState == BizState.ACTIVATED || bizState == BizState.DEACTIVATED\n                           || bizState == BizState.BROKEN,\n            \"BizState must be ACTIVATED, DEACTIVATED or BROKEN.\");\n        if (isMasterBizAndEmbedEnable()) {\n            // skip stop when embed mode\n            return;\n        }\n        ClassLoader oldClassLoader = ClassLoaderUtils.pushContextClassLoader(this.classLoader);\n        if (bizState == BizState.ACTIVATED) {\n            setBizState(BizState.DEACTIVATED, StateChangeReason.KILLING);\n        }\n        EventAdminService eventAdminService = ArkServiceContainerHolder.getContainer().getService(\n            EventAdminService.class);\n\n        boolean isStopFailed = false;\n        long start = System.currentTimeMillis();\n        try {\n            // this can trigger uninstall handler\n            ArkLoggerFactory.getDefaultLogger().info(\"Ark biz {} stops.\", getIdentity());\n            eventAdminService.sendEvent(new BeforeBizStopEvent(this));\n            ArkLoggerFactory.getDefaultLogger().info(\"Ark biz {} stopped in {} ms\", getIdentity(),\n                (System.currentTimeMillis() - start));\n        } catch (Throwable t) {\n            // handle stop failed\n            ArkLoggerFactory.getDefaultLogger().info(\"Ark biz {} stop failed in {} ms\",\n                getIdentity(), (System.currentTimeMillis() - start));\n            isStopFailed = true;\n            setBizState(BizState.BROKEN, StateChangeReason.UN_INSTALL_FAILED);\n            eventAdminService.sendEvent(new AfterBizStopFailedEvent(this, t));\n            throw t;\n        } finally {\n            boolean removeInstanceAfterStopFailed = Boolean.parseBoolean(ArkConfigs.getStringValue(\n                REMOVE_BIZ_INSTANCE_AFTER_STOP_FAILED, \"true\"));\n            // 只有成功后才清理, 或者失败后允许清理的情况，失败后如果不允许情况则不执行清理\n\n            if (!isStopFailed || (isStopFailed && removeInstanceAfterStopFailed)) {\n                BizManagerService bizManagerService = ArkServiceContainerHolder.getContainer()\n                    .getService(BizManagerService.class);\n                bizManagerService.unRegisterBiz(bizName, bizVersion);\n                setBizState(BizState.STOPPED, StateChangeReason.STOPPED);\n                eventAdminService.sendEvent(new BeforeBizRecycleEvent(this));\n                urls = null;\n                denyImportPackages = null;\n                denyImportClasses = null;\n                denyImportResources = null;\n                // close classloader\n                if (classLoader instanceof AbstractClasspathClassLoader) {\n                    try {\n                        ((AbstractClasspathClassLoader) classLoader).close();\n                        ((AbstractClasspathClassLoader) classLoader).clearCache();\n                    } catch (IOException e) {\n                        ArkLoggerFactory.getDefaultLogger().warn(\n                            \"Ark biz {} close biz classloader fail\", getIdentity());\n                    }\n                }\n                eventAdminService.sendEvent(new AfterBizStopEvent(this));\n                eventAdminService.unRegister(classLoader);\n                classLoader = null;\n                recycleBizTempWorkDir(bizTempWorkDir);\n                bizTempWorkDir = null;\n            }\n            ClassLoaderUtils.popContextClassLoader(oldClassLoader);\n        }\n    }\n\n    @Override\n    public void setCustomBizName(String bizName) {\n        this.bizName = bizName;\n    }\n\n    @Override\n    public BizState getBizState() {\n        return bizState;\n    }\n\n    @Override\n    public String getWebContextPath() {\n        return webContextPath;\n    }\n\n    @Override\n    public Map<String, String> getAttributes() {\n        return attributes;\n    }\n\n    @Override\n    public List<BizStateRecord> getBizStateRecords() {\n        return bizStateRecords;\n    }\n\n    @Override\n    public String toString() {\n        return \"Ark Biz: \" + getIdentity() + \",\\n biz url: \" + bizUrl + \",\\n classloader: \"\n               + classLoader + \",\\n current state: \" + bizState + \",\\n history states: \"\n               + bizStateRecords;\n    }\n\n    private void resetProperties() {\n        if (!ArkConfigs.isEmbedEnable()) {\n            System.getProperties().remove(\"logging.path\");\n        } else if (this != ArkClient.getMasterBiz()) {\n            System.getProperties().remove(\"spring.application.admin.enabled\");\n        }\n    }\n\n    public File getBizTempWorkDir() {\n        return bizTempWorkDir;\n    }\n\n    public BizModel setBizTempWorkDir(File bizTempWorkDir) {\n        this.bizTempWorkDir = bizTempWorkDir;\n        return this;\n    }\n\n    private boolean isMasterBizAndEmbedEnable() {\n        return this == ArkClient.getMasterBiz() && ArkConfigs.isEmbedEnable();\n    }\n\n    public BizModel setDeclaredLibraries(String declaredLibraries) {\n        if (StringUtils.isEmpty(declaredLibraries)) {\n            return this;\n        }\n        this.declaredLibraries = StringUtils.strToSet(declaredLibraries,\n            Constants.MANIFEST_VALUE_SPLIT);\n        return this;\n    }\n\n    /**\n     * check if the resource is defined in classloader, ignore jar version\n     * @param url\n     * @return\n     */\n    public boolean isDeclared(URL url, String resourceName) {\n        // compatibility with no-declaredMode\n        if (!isDeclaredMode()) {\n            return true;\n        }\n        if (url != null) {\n            String libraryFile = url.getFile().replace(\"file:\", \"\");\n            if (!StringUtils.isEmpty(resourceName) && libraryFile.endsWith(resourceName)) {\n                libraryFile = libraryFile.substring(0, libraryFile.lastIndexOf(resourceName));\n            }\n            return checkDeclaredWithCache(libraryFile);\n        }\n\n        return false;\n    }\n\n    public boolean isDeclaredMode() {\n        if (declaredLibraries == null || declaredLibraries.size() == 0) {\n            return false;\n        }\n        return true;\n    }\n\n    private boolean checkDeclaredWithCache(String libraryFile) {\n        // set key as jar, but need to checkDeclared by specific file.\n        return declaredCacheMap.computeIfAbsent(libraryFile, this::doCheckDeclared);\n    }\n\n    boolean doCheckDeclared(String jarFilePath) {\n\n        // if from ark plugin, then set as declared\n        if (isFromPlugin(jarFilePath)) {\n            return true;\n        }\n\n        String artifactId = JarUtils.parseArtifactId(jarFilePath);\n        if (artifactId == null) {\n            if (jarFilePath.contains(\".jar!\") || jarFilePath.endsWith(\".jar\")) {\n                // if in jar, and can't get artifactId from jar file, then just rollback to all delegate.\n                ArkLoggerFactory.getDefaultLogger().info(\n                    String.format(\"Can't find artifact id for %s, default as declared.\",\n                        jarFilePath));\n                return true;\n            } else {\n                // for not in jar, then default not delegate.\n                ArkLoggerFactory.getDefaultLogger().info(\n                    String.format(\"Can't find artifact id for %s, default as not declared.\",\n                        jarFilePath));\n                return false;\n            }\n        }\n\n        // some ark related lib which each ark module needed should set declared as default\n        if (StringUtils.startWithToLowerCase(artifactId, \"sofa-ark-\")) {\n            return true;\n        }\n\n        return declaredLibraries.contains(artifactId);\n    }\n\n    private boolean isFromPlugin(String jarFilePath) {\n        if (pluginUrls == null) {\n            return false;\n        }\n        for (URL pluginUrl : pluginUrls) {\n            String pluginUrlFile = pluginUrl.getFile().replace(\"file:\", \"\");\n            if (pluginUrlFile.endsWith(JarUtils.JAR_SEPARATOR)) {\n                pluginUrlFile = pluginUrlFile.substring(0, pluginUrlFile.length()\n                                                           - JarUtils.JAR_SEPARATOR.length());\n            }\n            if (jarFilePath.contains(pluginUrlFile)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * recycle biz temp work dir\n     *\n     * @param bizTempWorkDir\n     * @return\n     */\n    public static boolean recycleBizTempWorkDir(File bizTempWorkDir) {\n        if (bizTempWorkDir == null) {\n            return false;\n        }\n\n        if (bizTempWorkDir.isDirectory()) {\n            try {\n                String newPath = markBizTempWorkDirRecycled(bizTempWorkDir);\n                File markedFile = new File(newPath);\n                if (!markedFile.exists()) {\n                    ArkLoggerFactory.getDefaultLogger().warn(\n                        String.format(\"when delete marked biz temp work dir %s, file not exists \",\n                            markedFile.getPath()));\n                    return false;\n                }\n\n                return deleteQuietly(markedFile);\n            } catch (IOException e) {\n                throw new ArkRuntimeException(\"mark and delete biz temp work dir error: \"\n                                              + e.getMessage());\n            }\n\n        }\n\n        return deleteQuietly(bizTempWorkDir);\n    }\n\n    /**\n     * mark biz temp work dir is recycled\n     *\n     * @param bizTempWorkDir\n     * @return\n     * @throws IOException\n     */\n    private static String markBizTempWorkDirRecycled(File bizTempWorkDir) throws IOException {\n        String sourcePath = bizTempWorkDir.getAbsolutePath();\n        String targetPath = String.format(\"%s-%s-%s\", sourcePath, System.currentTimeMillis(),\n            BIZ_TEMP_WORK_DIR_RECYCLE_FILE_SUFFIX);\n\n        Files.move(Paths.get(sourcePath), Paths.get(targetPath));\n        ArkLoggerFactory.getDefaultLogger().info(\n            String.format(\"move biz temp work dir from %s to %s\", sourcePath, targetPath));\n\n        return targetPath;\n    }\n\n    private static String getStackTraceAsString(Throwable throwable) {\n        StringWriter sw = new StringWriter();\n        PrintWriter pw = new PrintWriter(sw);\n        throwable.printStackTrace(pw);\n        return sw.toString();\n    }\n\n    /* export class and classloader relationship cache */\n    private ConcurrentHashMap<String, Plugin>       exportClassAndClassLoaderMap              = new ConcurrentHashMap<>();\n    private ConcurrentHashMap<String, Plugin>       exportNodeAndClassLoaderMap               = new ConcurrentHashMap<>();\n    private ConcurrentHashMap<String, Plugin>       exportStemAndClassLoaderMap               = new ConcurrentHashMap<>();\n\n    /* export cache and classloader relationship cache */\n    private ConcurrentHashMap<String, List<Plugin>> exportResourceAndClassLoaderMap           = new ConcurrentHashMap<>();\n    private ConcurrentHashMap<String, List<Plugin>> exportPrefixStemResourceAndClassLoaderMap = new ConcurrentHashMap<>();\n    private ConcurrentHashMap<String, List<Plugin>> exportSuffixStemResourceAndClassLoaderMap = new ConcurrentHashMap<>();\n\n    public ConcurrentHashMap<String, Plugin> getExportClassAndClassLoaderMap() {\n        return exportClassAndClassLoaderMap;\n    }\n\n    public ConcurrentHashMap<String, Plugin> getExportNodeAndClassLoaderMap() {\n        return exportNodeAndClassLoaderMap;\n    }\n\n    public ConcurrentHashMap<String, Plugin> getExportStemAndClassLoaderMap() {\n        return exportStemAndClassLoaderMap;\n    }\n\n    public ConcurrentHashMap<String, List<Plugin>> getExportResourceAndClassLoaderMap() {\n        return exportResourceAndClassLoaderMap;\n    }\n\n    public ConcurrentHashMap<String, List<Plugin>> getExportPrefixStemResourceAndClassLoaderMap() {\n        return exportPrefixStemResourceAndClassLoaderMap;\n    }\n\n    public ConcurrentHashMap<String, List<Plugin>> getExportSuffixStemResourceAndClassLoaderMap() {\n        return exportSuffixStemResourceAndClassLoaderMap;\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/model/PluginContextImpl.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.model;\n\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.container.registry.PluginServiceProvider;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.model.PluginContext;\nimport com.alipay.sofa.ark.spi.registry.ServiceFilter;\nimport com.alipay.sofa.ark.spi.registry.ServiceReference;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport com.alipay.sofa.ark.spi.service.registry.RegistryService;\n\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * Standard Plugin Context Implement\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class PluginContextImpl implements PluginContext {\n\n    private Plugin               plugin;\n\n    private PluginManagerService pluginManagerService = ArkServiceContainerHolder.getContainer()\n                                                          .getService(PluginManagerService.class);\n\n    private RegistryService      registryService      = ArkServiceContainerHolder.getContainer()\n                                                          .getService(RegistryService.class);\n\n    public PluginContextImpl(Plugin plugin) {\n        this.plugin = plugin;\n    }\n\n    @Override\n    public Plugin getPlugin() {\n        return plugin;\n    }\n\n    @Override\n    public Plugin getPlugin(String pluginName) {\n        return pluginManagerService.getPluginByName(pluginName);\n    }\n\n    @Override\n    public <T> ServiceReference<T> publishService(Class<T> ifClass, T implObject) {\n        return publishService(ifClass, implObject, StringUtils.EMPTY_STRING);\n    }\n\n    @Override\n    public <T> ServiceReference<T> publishService(Class<T> ifClass, T implObject, String uniqueId) {\n        return registryService.publishService(ifClass, implObject, uniqueId,\n            new PluginServiceProvider(plugin));\n    }\n\n    @Override\n    public <T> ServiceReference<T> referenceService(Class<T> ifClass) {\n        return referenceService(ifClass, StringUtils.EMPTY_STRING);\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T> ServiceReference<T> referenceService(Class<T> ifClass, String uniqueId) {\n        return registryService.referenceService(ifClass, uniqueId);\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public List<ServiceReference> referenceServices(ServiceFilter serviceFilter) {\n        return registryService.referenceServices(serviceFilter);\n    }\n\n    @Override\n    public Set<String> getPluginNames() {\n        return pluginManagerService.getAllPluginNames();\n    }\n\n    @Override\n    public ClassLoader getClassLoader() {\n        return plugin.getPluginClassLoader();\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/model/PluginModel.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.model;\n\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.common.util.ParseUtils;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.event.plugin.AfterPluginStartupEvent;\nimport com.alipay.sofa.ark.spi.event.plugin.AfterPluginStopEvent;\nimport com.alipay.sofa.ark.spi.event.plugin.BeforePluginStartupEvent;\nimport com.alipay.sofa.ark.spi.event.plugin.BeforePluginStopEvent;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.model.PluginContext;\nimport com.alipay.sofa.ark.spi.service.PluginActivator;\nimport com.alipay.sofa.ark.spi.service.event.EventAdminService;\n\nimport java.net.URL;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * ARk Plugin Standard Model\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class PluginModel implements Plugin {\n\n    private String             pluginName;\n\n    private String             groupId;\n\n    private String             artifactId;\n\n    private String             version;\n\n    private int                priority                  = DEFAULT_PRECEDENCE;\n\n    /**\n     * 0. default as 'classLoader' for load those classes in plugin classLoader\n     * 1. 'override' for load those classes only as files, and those will be reload in other classLoaders.\n     */\n    public static final String EXPORTMODE_CLASSLOADER    = \"classLoader\";\n    public static final String EXPORTMODE_OVERRIDE       = \"override\";\n    public static final String EXPORTMODE_UNKNOWN        = \"unknown\";\n    private String             exportMode                = EXPORTMODE_CLASSLOADER;\n\n    private Set<String>        exportPackages;\n\n    private Set<String>        exportPackageNodes        = new HashSet<>();\n\n    private Set<String>        exportPackageStems        = new HashSet<>();\n\n    private Set<String>        exportClasses;\n\n    private Set<String>        importPackages;\n\n    private Set<String>        importPackageNodes        = new HashSet<>();\n\n    private Set<String>        importPackageStems        = new HashSet<>();\n\n    private Set<String>        importClasses;\n\n    private Set<String>        importResources           = new HashSet<>();\n\n    private Set<String>        importPrefixResourceStems = new HashSet<>();\n    private Set<String>        importSuffixResourceStems = new HashSet<>();\n\n    private Set<String>        exportResources           = new HashSet<>();\n\n    private Set<String>        exportPrefixResourceStems = new HashSet<>();\n    private Set<String>        exportSuffixResourceStems = new HashSet<>();\n\n    private String             activator;\n\n    private URL[]              urls;\n\n    private URL                pluginUrl;\n\n    private ClassLoader        pluginClassLoader;\n\n    private PluginContext      pluginContext;\n\n    private PluginActivator    pluginActivator;\n\n    public PluginModel setPluginName(String pluginName) {\n        this.pluginName = pluginName;\n        return this;\n    }\n\n    public PluginModel setGroupId(String groupId) {\n        this.groupId = groupId;\n        return this;\n    }\n\n    public PluginModel setArtifactId(String artifactId) {\n        this.artifactId = artifactId;\n        return this;\n    }\n\n    public PluginModel setVersion(String version) {\n        this.version = version;\n        return this;\n    }\n\n    public PluginModel setPriority(String priority) {\n        this.priority = (priority == null ? DEFAULT_PRECEDENCE : Integer.valueOf(priority));\n        return this;\n    }\n\n    public PluginModel setPluginActivator(String activator) {\n        this.activator = activator;\n        return this;\n    }\n\n    public PluginModel setClassPath(URL[] urls) {\n        this.urls = urls;\n        return this;\n    }\n\n    public PluginModel setExportMode(String exportMode) {\n        this.exportMode = exportMode;\n        return this;\n    }\n\n    public PluginModel setExportPackages(String exportPackages) {\n        this.exportPackages = StringUtils.strToSet(exportPackages, Constants.MANIFEST_VALUE_SPLIT);\n        ParseUtils.parsePackageNodeAndStem(this.exportPackages, this.exportPackageStems,\n            this.exportPackageNodes);\n        return this;\n    }\n\n    public PluginModel setExportPackages(String exportPackages, Set<String> exportExtensionPackages) {\n        this.exportPackages = StringUtils.strToSet(exportPackages, Constants.MANIFEST_VALUE_SPLIT);\n        this.exportPackages.addAll(exportExtensionPackages);\n        ParseUtils.parsePackageNodeAndStem(this.exportPackages, this.exportPackageStems,\n            this.exportPackageNodes);\n        return this;\n    }\n\n    public PluginModel setExportClasses(String exportClasses) {\n        this.exportClasses = StringUtils.strToSet(exportClasses, Constants.MANIFEST_VALUE_SPLIT);\n        return this;\n    }\n\n    public PluginModel setImportPackages(String importPackages) {\n        this.importPackages = StringUtils.strToSet(importPackages, Constants.MANIFEST_VALUE_SPLIT);\n        ParseUtils.parsePackageNodeAndStem(this.importPackages, this.importPackageStems,\n            this.importPackageNodes);\n        return this;\n    }\n\n    public PluginModel setImportClasses(String importClasses) {\n        this.importClasses = StringUtils.strToSet(importClasses, Constants.MANIFEST_VALUE_SPLIT);\n        return this;\n    }\n\n    public PluginModel setImportResources(String importResources) {\n        ParseUtils.parseResourceAndStem(\n            StringUtils.strToSet(importResources, Constants.MANIFEST_VALUE_SPLIT),\n            this.importPrefixResourceStems, importSuffixResourceStems, this.importResources);\n        return this;\n    }\n\n    public PluginModel setExportResources(String exportResources) {\n        ParseUtils.parseResourceAndStem(\n            StringUtils.strToSet(exportResources, Constants.MANIFEST_VALUE_SPLIT),\n            this.exportPrefixResourceStems, exportSuffixResourceStems, this.exportResources);\n        return this;\n    }\n\n    public PluginModel setPluginClassLoader(ClassLoader classLoader) {\n        this.pluginClassLoader = classLoader;\n        return this;\n    }\n\n    public PluginModel setPluginContext(PluginContext context) {\n        this.pluginContext = context;\n        return this;\n    }\n\n    public PluginModel setPluginUrl(URL pluginUrl) {\n        this.pluginUrl = pluginUrl;\n        return this;\n    }\n\n    @Override\n    public int getPriority() {\n        return this.priority;\n    }\n\n    @Override\n    public String getPluginName() {\n        return this.pluginName;\n    }\n\n    @Override\n    public String getGroupId() {\n        return this.groupId;\n    }\n\n    @Override\n    public String getArtifactId() {\n        return this.artifactId;\n    }\n\n    @Override\n    public String getVersion() {\n        return this.version;\n    }\n\n    @Override\n    public String getPluginActivator() {\n        return this.activator;\n    }\n\n    @Override\n    public URL[] getClassPath() {\n        return this.urls;\n    }\n\n    @Override\n    public ClassLoader getPluginClassLoader() {\n        return this.pluginClassLoader;\n    }\n\n    @Override\n    public PluginContext getPluginContext() {\n        return this.pluginContext;\n    }\n\n    @Override\n    public String getExportMode() {\n        if (StringUtils.isEmpty(this.exportMode)) {\n            return EXPORTMODE_CLASSLOADER;\n        }\n        return this.exportMode;\n    }\n\n    @Override\n    public Set<String> getExportPackages() {\n        return this.exportPackages;\n    }\n\n    @Override\n    public Set<String> getExportPackageNodes() {\n        return exportPackageNodes;\n    }\n\n    @Override\n    public Set<String> getExportPackageStems() {\n        return exportPackageStems;\n    }\n\n    @Override\n    public Set<String> getExportClasses() {\n        return this.exportClasses;\n    }\n\n    @Override\n    public Set<String> getImportPackages() {\n        return this.importPackages;\n    }\n\n    @Override\n    public Set<String> getImportPackageNodes() {\n        return this.importPackageNodes;\n    }\n\n    @Override\n    public Set<String> getImportPackageStems() {\n        return this.importPackageStems;\n    }\n\n    @Override\n    public Set<String> getImportClasses() {\n        return this.importClasses;\n    }\n\n    @Override\n    public Set<String> getImportResources() {\n        return importResources;\n    }\n\n    @Override\n    public Set<String> getImportPrefixResourceStems() {\n        return importPrefixResourceStems;\n    }\n\n    @Override\n    public Set<String> getImportSuffixResourceStems() {\n        return importSuffixResourceStems;\n    }\n\n    @Override\n    public Set<String> getExportResources() {\n        return exportResources;\n    }\n\n    @Override\n    public Set<String> getExportPrefixResourceStems() {\n        return exportPrefixResourceStems;\n    }\n\n    @Override\n    public Set<String> getExportSuffixResourceStems() {\n        return exportSuffixResourceStems;\n    }\n\n    @Override\n    public URL getPluginURL() {\n        return pluginUrl;\n    }\n\n    @Override\n    public void start() throws ArkRuntimeException {\n        if (activator == null || activator.isEmpty()) {\n            return;\n        }\n\n        EventAdminService eventAdminService = ArkServiceContainerHolder.getContainer().getService(\n            EventAdminService.class);\n\n        ClassLoader oldClassLoader = ClassLoaderUtils\n            .pushContextClassLoader(this.pluginClassLoader);\n        try {\n            eventAdminService.sendEvent(new BeforePluginStartupEvent(this));\n            pluginActivator = (PluginActivator) pluginClassLoader.loadClass(activator)\n                .newInstance();\n            pluginActivator.start(pluginContext);\n        } catch (Throwable ex) {\n            throw new ArkRuntimeException(ex.getMessage(), ex);\n        } finally {\n            eventAdminService.sendEvent(new AfterPluginStartupEvent(this));\n            ClassLoaderUtils.popContextClassLoader(oldClassLoader);\n\n        }\n    }\n\n    @Override\n    public void stop() throws ArkRuntimeException {\n        EventAdminService eventAdminService = ArkServiceContainerHolder.getContainer().getService(\n            EventAdminService.class);\n        eventAdminService.sendEvent(new BeforePluginStopEvent(this));\n        try {\n            if (pluginActivator != null) {\n                pluginActivator.stop(pluginContext);\n            }\n        } catch (Throwable ex) {\n            throw new ArkRuntimeException(ex.getMessage(), ex);\n        } finally {\n            eventAdminService.sendEvent(new AfterPluginStopEvent(this));\n            if (this.getPluginClassLoader() != null) {\n                eventAdminService.unRegister(this.getPluginClassLoader());\n            }\n        }\n    }\n\n    @Override\n    public String toString() {\n        return \"Ark Plugin: \" + pluginName;\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/pipeline/DeployBizStage.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.pipeline;\n\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.event.AfterFinishDeployEvent;\nimport com.alipay.sofa.ark.spi.pipeline.PipelineContext;\nimport com.alipay.sofa.ark.spi.pipeline.PipelineStage;\nimport com.alipay.sofa.ark.spi.service.biz.BizDeployService;\nimport com.alipay.sofa.ark.spi.service.event.EventAdminService;\nimport com.google.inject.Inject;\nimport com.google.inject.Singleton;\n\n/**\n * Pipeline Stage to Deploy Biz\n *\n * @author ruoshan\n * @since 0.1.0\n */\n@Singleton\npublic class DeployBizStage implements PipelineStage {\n\n    @Inject\n    private BizDeployService  bizDeployService;\n\n    @Inject\n    private EventAdminService eventAdminService;\n\n    @Override\n    public void process(PipelineContext pipelineContext) throws ArkRuntimeException {\n        String[] args = pipelineContext.getLaunchCommand().getLaunchArgs();\n        bizDeployService.deploy(args);\n        if (ArkConfigs.isEmbedEnable()) {\n            return;\n        }\n        eventAdminService.sendEvent(new AfterFinishDeployEvent());\n    }\n\n    public void processStaticBiz(PipelineContext pipelineContext) throws ArkRuntimeException {\n        String[] args = pipelineContext.getLaunchCommand().getLaunchArgs();\n        bizDeployService.deploy(args);\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/pipeline/DeployPluginStage.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.pipeline;\n\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.pipeline.PipelineContext;\nimport com.alipay.sofa.ark.spi.pipeline.PipelineStage;\nimport com.alipay.sofa.ark.spi.service.classloader.ClassLoaderService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginDeployService;\nimport com.google.inject.Inject;\nimport com.google.inject.Singleton;\n\n/**\n * Deploy Plugin Stage\n *\n * @author ruoshan\n * @since 0.1.0\n */\n@Singleton\npublic class DeployPluginStage implements PipelineStage {\n\n    @Inject\n    private ClassLoaderService  classloaderService;\n\n    @Inject\n    private PluginDeployService pluginDeployService;\n\n    @Override\n    public void process(PipelineContext pipelineContext) throws ArkRuntimeException {\n        classloaderService.prepareExportClassAndResourceCache();\n        pluginDeployService.deploy();\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/pipeline/ExtensionLoaderStage.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.pipeline;\n\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.pipeline.PipelineContext;\nimport com.alipay.sofa.ark.spi.pipeline.PipelineStage;\nimport com.alipay.sofa.ark.spi.service.extension.ArkServiceLoader;\nimport com.alipay.sofa.ark.spi.service.extension.ExtensionLoaderService;\n\nimport javax.inject.Inject;\nimport javax.inject.Singleton;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@Singleton\npublic class ExtensionLoaderStage implements PipelineStage {\n\n    @Inject\n    private ExtensionLoaderService extensionLoaderService;\n\n    @Override\n    public void process(PipelineContext pipelineContext) throws ArkRuntimeException {\n        ArkServiceLoader.setExtensionLoaderService(extensionLoaderService);\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/pipeline/FinishStartupStage.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.pipeline;\n\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.event.AfterFinishStartupEvent;\nimport com.alipay.sofa.ark.spi.pipeline.PipelineContext;\nimport com.alipay.sofa.ark.spi.pipeline.PipelineStage;\nimport com.alipay.sofa.ark.spi.service.event.EventAdminService;\n\nimport javax.inject.Inject;\nimport javax.inject.Singleton;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@Singleton\npublic class FinishStartupStage implements PipelineStage {\n    @Inject\n    private EventAdminService eventAdminService;\n\n    @Override\n    public void process(PipelineContext pipelineContext) throws ArkRuntimeException {\n        if (ArkConfigs.isEmbedEnable()) {\n            return;\n        }\n        eventAdminService.sendEvent(new AfterFinishStartupEvent());\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/pipeline/HandleArchiveStage.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.pipeline;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.common.util.AssertUtils;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.loader.DirectoryBizArchive;\nimport com.alipay.sofa.ark.loader.JarBizArchive;\nimport com.alipay.sofa.ark.spi.archive.BizArchive;\nimport com.alipay.sofa.ark.spi.archive.ExecutableArchive;\nimport com.alipay.sofa.ark.spi.archive.PluginArchive;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.pipeline.PipelineContext;\nimport com.alipay.sofa.ark.spi.pipeline.PipelineStage;\nimport com.alipay.sofa.ark.spi.service.biz.BizFactoryService;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginFactoryService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport com.google.inject.Inject;\nimport com.google.inject.Singleton;\n\nimport java.net.URL;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.jar.Attributes;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.ARK_BIZ_NAME;\nimport static com.alipay.sofa.ark.spi.constant.Constants.BIZ_ACTIVE_EXCLUDE;\nimport static com.alipay.sofa.ark.spi.constant.Constants.BIZ_ACTIVE_INCLUDE;\nimport static com.alipay.sofa.ark.spi.constant.Constants.COMMA_SPLIT;\nimport static com.alipay.sofa.ark.spi.constant.Constants.INJECT_EXPORT_PACKAGES;\nimport static com.alipay.sofa.ark.spi.constant.Constants.MANIFEST_VALUE_SPLIT;\nimport static com.alipay.sofa.ark.spi.constant.Constants.PLUGIN_ACTIVE_EXCLUDE;\nimport static com.alipay.sofa.ark.spi.constant.Constants.PLUGIN_ACTIVE_INCLUDE;\n\n/**\n * response to handle executable fat jar, parse plugin model and biz model from it\n *\n * @author qilong.zql\n * @since 0.1.0\n */\n@Singleton\npublic class HandleArchiveStage implements PipelineStage {\n    @Inject\n    private PluginManagerService pluginManagerService;\n\n    @Inject\n    private PluginFactoryService pluginFactoryService;\n\n    @Inject\n    private BizManagerService    bizManagerService;\n\n    @Inject\n    private BizFactoryService    bizFactoryService;\n\n    @Override\n    public void process(PipelineContext pipelineContext) throws ArkRuntimeException {\n        try {\n            if (ArkConfigs.isEmbedEnable()) {\n                processEmbed(pipelineContext);\n                return;\n            }\n            ExecutableArchive executableArchive = pipelineContext.getExecutableArchive();\n            List<BizArchive> bizArchives = executableArchive.getBizArchives();\n            List<PluginArchive> pluginArchives = executableArchive.getPluginArchives();\n\n            if (useDynamicConfig()) {\n                AssertUtils.isFalse(\n                    StringUtils.isEmpty(ArkConfigs.getStringValue(Constants.MASTER_BIZ)),\n                    \"Master biz should be configured when using dynamic config.\");\n            }\n\n            int bizCount = 0;\n            for (BizArchive bizArchive : bizArchives) {\n                // NOTE: biz name can not be null!\n                Biz biz = bizFactoryService.createBiz(bizArchive);\n                if (bizArchive instanceof DirectoryBizArchive) {\n                    if (!((DirectoryBizArchive) bizArchive).isTestMode()) {\n                        bizManagerService.registerBiz(biz);\n                        bizCount += 1;\n                    }\n                } else if (useDynamicConfig()) {\n                    if (biz.getBizName().equals(ArkConfigs.getStringValue(Constants.MASTER_BIZ))) {\n                        bizManagerService.registerBiz(biz);\n                        bizCount += 1;\n                    } else {\n                        ArkLoggerFactory.getDefaultLogger().warn(\n                            \"The biz of {} is ignored when using dynamic config.\",\n                            biz.getIdentity());\n                    }\n                } else {\n                    if (!isBizExcluded(biz)) {\n                        bizManagerService.registerBiz(biz);\n                        bizCount += 1;\n                    } else {\n                        ArkLoggerFactory.getDefaultLogger().warn(\n                            String.format(\"The biz of %s is excluded.\", biz.getIdentity()));\n                    }\n                }\n            }\n\n            // master biz should be specified when deploy multi biz, otherwise the only biz would be token as master biz\n            if (bizCount > 1) {\n                AssertUtils.isFalse(\n                    StringUtils.isEmpty(ArkConfigs.getStringValue(Constants.MASTER_BIZ)),\n                    \"Master biz should be configured when deploy multi biz.\");\n                String masterBizName = ArkConfigs.getStringValue(Constants.MASTER_BIZ);\n                for (Biz biz : bizManagerService.getBizInOrder()) {\n                    if (masterBizName.equals(biz.getBizName())) {\n                        ArkClient.setMasterBiz(biz);\n                    }\n                }\n            } else {\n                List<Biz> bizList = bizManagerService.getBizInOrder();\n                if (!bizList.isEmpty()\n                    && StringUtils.isEmpty(ArkConfigs.getStringValue(Constants.MASTER_BIZ))) {\n                    ArkConfigs.putStringValue(Constants.MASTER_BIZ, bizList.get(0).getBizName());\n                    ArkClient.setMasterBiz(bizList.get(0));\n                }\n            }\n\n            URL[] exportUrls = null;\n            Set<String> exportPackages = new HashSet<>();\n            Biz masterBiz = ArkClient.getMasterBiz();\n            for (BizArchive bizArchive : bizArchives) {\n                Attributes mainAttributes = bizArchive.getManifest().getMainAttributes();\n                String bizName = mainAttributes.getValue(ARK_BIZ_NAME);\n                // extension from master biz\n                if (bizArchive instanceof JarBizArchive\n                    && masterBiz.getBizName().equalsIgnoreCase(bizName)) {\n                    String exportPackageStr = mainAttributes.getValue(INJECT_EXPORT_PACKAGES);\n                    exportPackages.addAll(StringUtils.strToSet(exportPackageStr,\n                        MANIFEST_VALUE_SPLIT));\n                    exportUrls = ((JarBizArchive) bizArchive).getExportUrls();\n                }\n            }\n\n            for (PluginArchive pluginArchive : pluginArchives) {\n                Plugin plugin = pluginFactoryService.createPlugin(pluginArchive, exportUrls,\n                    exportPackages);\n                if (!isPluginExcluded(plugin)) {\n                    pluginManagerService.registerPlugin(plugin);\n                } else {\n                    ArkLoggerFactory.getDefaultLogger().warn(\n                        String.format(\"The plugin of %s is excluded.\", plugin.getPluginName()));\n                }\n            }\n\n        } catch (Throwable ex) {\n            throw new ArkRuntimeException(ex.getMessage(), ex);\n        }\n    }\n\n    protected void processEmbed(PipelineContext pipelineContext) throws Exception {\n        ClassLoader masterBizClassLoader = pipelineContext.getClass().getClassLoader();\n        Biz masterBiz = bizFactoryService.createEmbedMasterBiz(masterBizClassLoader);\n        bizManagerService.registerBiz(masterBiz);\n        ArkClient.setMasterBiz(masterBiz);\n        ArkConfigs.putStringValue(Constants.MASTER_BIZ, masterBiz.getBizName());\n        ExecutableArchive executableArchive = pipelineContext.getExecutableArchive();\n        List<PluginArchive> pluginArchives = executableArchive.getPluginArchives();\n        for (PluginArchive pluginArchive : pluginArchives) {\n            Plugin plugin = pluginFactoryService.createEmbedPlugin(pluginArchive,\n                masterBizClassLoader);\n            if (!isPluginExcluded(plugin)) {\n                pluginManagerService.registerPlugin(plugin);\n            } else {\n                ArkLoggerFactory.getDefaultLogger().warn(\n                    String.format(\"The plugin of %s is excluded.\", plugin.getPluginName()));\n            }\n        }\n    }\n\n    public void processStaticBizFromClasspath(PipelineContext pipelineContext) throws Exception {\n        ExecutableArchive executableArchive = pipelineContext.getExecutableArchive();\n        List<BizArchive> bizArchives = executableArchive.getBizArchives();\n        for (BizArchive bizArchive : bizArchives) {\n            Biz biz = bizFactoryService.createBiz(bizArchive);\n            bizManagerService.registerBiz(biz);\n        }\n    }\n\n    public boolean isPluginExcluded(Plugin plugin) {\n        String pluginName = plugin.getPluginName();\n        String includePluginConf = ArkConfigs.getStringValue(PLUGIN_ACTIVE_INCLUDE);\n        String excludePluginConf = ArkConfigs.getStringValue(PLUGIN_ACTIVE_EXCLUDE);\n        Set<String> includePlugins = StringUtils.strToSet(includePluginConf, COMMA_SPLIT);\n        Set<String> excludePlugins = StringUtils.strToSet(excludePluginConf, COMMA_SPLIT);\n        if (includePluginConf == null && excludePluginConf == null) {\n            return false;\n        } else if (includePluginConf == null) {\n            return excludePlugins.contains(pluginName);\n        } else {\n            return !includePlugins.contains(pluginName);\n        }\n    }\n\n    public boolean isBizExcluded(Biz biz) {\n        String bizIdentity = biz.getIdentity();\n        String includeBizConf = ArkConfigs.getStringValue(BIZ_ACTIVE_INCLUDE);\n        String excludeBizConf = ArkConfigs.getStringValue(BIZ_ACTIVE_EXCLUDE);\n        Set<String> includeBizs = StringUtils.strToSet(includeBizConf, COMMA_SPLIT);\n        Set<String> excludeBizs = StringUtils.strToSet(excludeBizConf, COMMA_SPLIT);\n        if (includeBizConf == null && excludeBizConf == null) {\n            return false;\n        } else if (includeBizConf == null) {\n            return excludeBizs.contains(bizIdentity);\n        } else {\n            return !includeBizs.contains(bizIdentity);\n        }\n    }\n\n    public boolean useDynamicConfig() {\n        return !StringUtils.isEmpty(ArkConfigs.getStringValue(Constants.CONFIG_SERVER_ADDRESS));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/pipeline/RegisterServiceStage.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.pipeline;\n\nimport com.alipay.sofa.ark.container.registry.ContainerServiceProvider;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.container.service.biz.BizCommandProvider;\nimport com.alipay.sofa.ark.container.service.biz.DefaultBizDeployer;\nimport com.alipay.sofa.ark.container.service.plugin.PluginCommandProvider;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.pipeline.PipelineContext;\nimport com.alipay.sofa.ark.spi.pipeline.PipelineStage;\nimport com.alipay.sofa.ark.spi.service.PriorityOrdered;\nimport com.alipay.sofa.ark.spi.service.biz.BizDeployer;\nimport com.alipay.sofa.ark.spi.service.biz.BizFactoryService;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.event.EventAdminService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginFactoryService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport com.alipay.sofa.ark.spi.service.registry.RegistryService;\nimport com.alipay.sofa.ark.spi.service.session.CommandProvider;\nimport com.google.inject.Inject;\nimport com.google.inject.Singleton;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.BIZ_COMMAND_UNIQUE_ID;\nimport static com.alipay.sofa.ark.spi.constant.Constants.PLUGIN_COMMAND_UNIQUE_ID;\n\n/**\n * Handle service contained in {@link com.alipay.sofa.ark.spi.service.registry.RegistryService},\n * mainly including registering service provided by ark container and service initialization\n *\n * @author qilong.zql\n * @since 0.4.0\n */\n@Singleton\npublic class RegisterServiceStage implements PipelineStage {\n\n    @Inject\n    private RegistryService registryService;\n\n    @Override\n    public void process(PipelineContext pipelineContext) throws ArkRuntimeException {\n        registryDefaultService();\n    }\n\n    /**\n     * Registry some default service\n     */\n    private void registryDefaultService() {\n        /**\n         * some basic container service is not allowed to be override,  they are only published\n         * to be referenced by plugin and biz, even depended by other container service.\n         */\n        registryService.publishService(BizManagerService.class, ArkServiceContainerHolder\n            .getContainer().getService(BizManagerService.class), new ContainerServiceProvider(\n            PriorityOrdered.HIGHEST_PRECEDENCE));\n        registryService.publishService(BizFactoryService.class, ArkServiceContainerHolder\n            .getContainer().getService(BizFactoryService.class), new ContainerServiceProvider(\n            PriorityOrdered.HIGHEST_PRECEDENCE));\n        registryService.publishService(PluginManagerService.class, ArkServiceContainerHolder\n            .getContainer().getService(PluginManagerService.class), new ContainerServiceProvider(\n            PriorityOrdered.HIGHEST_PRECEDENCE));\n        registryService.publishService(PluginFactoryService.class, ArkServiceContainerHolder\n            .getContainer().getService(PluginFactoryService.class), new ContainerServiceProvider(\n            PriorityOrdered.HIGHEST_PRECEDENCE));\n        registryService.publishService(EventAdminService.class, ArkServiceContainerHolder\n            .getContainer().getService(EventAdminService.class), new ContainerServiceProvider(\n            PriorityOrdered.HIGHEST_PRECEDENCE));\n        registryService.publishService(RegistryService.class, ArkServiceContainerHolder\n            .getContainer().getService(RegistryService.class), new ContainerServiceProvider(\n            PriorityOrdered.HIGHEST_PRECEDENCE));\n\n        /**\n         * some container service which may depends on other basic container service.\n         */\n        registryService.publishService(BizDeployer.class, new DefaultBizDeployer(),\n            new ContainerServiceProvider());\n        registryService.publishService(CommandProvider.class, new PluginCommandProvider(),\n            PLUGIN_COMMAND_UNIQUE_ID, new ContainerServiceProvider());\n        registryService.publishService(CommandProvider.class, new BizCommandProvider(),\n            BIZ_COMMAND_UNIQUE_ID, new ContainerServiceProvider());\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/pipeline/StandardPipeline.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.pipeline;\n\nimport com.alipay.sofa.ark.common.log.ArkLogger;\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainer;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.pipeline.Pipeline;\nimport com.alipay.sofa.ark.spi.pipeline.PipelineContext;\nimport com.alipay.sofa.ark.spi.pipeline.PipelineStage;\nimport com.google.inject.Singleton;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Standard Pipeline Implementation\n *\n * @author ruoshan\n * @since 0.1.0\n */\n@Singleton\npublic class StandardPipeline implements Pipeline {\n\n    private List<PipelineStage> stages = new ArrayList<>();\n\n    public StandardPipeline() {\n        initializePipeline();\n    }\n\n    private void initializePipeline() {\n        addPipelineStage(\n            ArkServiceContainerHolder.getContainer().getService(HandleArchiveStage.class))\n            .addPipelineStage(\n                ArkServiceContainerHolder.getContainer().getService(RegisterServiceStage.class))\n            .addPipelineStage(\n                ArkServiceContainerHolder.getContainer().getService(ExtensionLoaderStage.class))\n            .addPipelineStage(\n                ArkServiceContainerHolder.getContainer().getService(DeployPluginStage.class))\n            .addPipelineStage(\n                ArkServiceContainerHolder.getContainer().getService(DeployBizStage.class))\n            .addPipelineStage(\n                ArkServiceContainerHolder.getContainer().getService(FinishStartupStage.class));\n    }\n\n    @Override\n    public Pipeline addPipelineStage(PipelineStage pipelineStage) {\n        stages.add(pipelineStage);\n        return this;\n    }\n\n    @Override\n    public void process(PipelineContext pipelineContext) throws ArkRuntimeException {\n        for (PipelineStage pipelineStage : stages) {\n            try {\n                ArkLoggerFactory.getDefaultLogger().info(\n                    String.format(\"Start to process pipeline stage: %s\", pipelineStage.getClass()\n                        .getName()));\n                pipelineStage.process(pipelineContext);\n                ArkLoggerFactory.getDefaultLogger().info(\n                    String.format(\"Finish to process pipeline stage: %s\", pipelineStage.getClass()\n                        .getName()));\n            } catch (Throwable e) {\n                ArkLoggerFactory.getDefaultLogger().error(\n                    String.format(\"Process pipeline stage fail: %s\", pipelineStage.getClass()\n                        .getName()), e);\n                throw new ArkRuntimeException(e);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/registry/AbstractServiceProvider.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.registry;\n\nimport com.alipay.sofa.ark.spi.registry.ServiceProvider;\nimport com.alipay.sofa.ark.spi.registry.ServiceProviderType;\nimport com.alipay.sofa.ark.spi.service.PriorityOrdered;\n\n/**\n * Abstract Service Provider\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic abstract class AbstractServiceProvider implements ServiceProvider {\n\n    protected ServiceProviderType providerType;\n\n    public AbstractServiceProvider(ServiceProviderType providerType) {\n        this.providerType = providerType;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n\n        if (obj == null || obj.getClass() != this.getClass()) {\n            return false;\n        }\n\n        if (getPriority() != ((PriorityOrdered) obj).getPriority()) {\n            return false;\n        }\n\n        return getServiceProviderType() == ((ServiceProvider) obj).getServiceProviderType();\n    }\n\n    @Override\n    public ServiceProviderType getServiceProviderType() {\n        return providerType;\n    }\n\n    @Override\n    public String getServiceProviderDesc() {\n        return getServiceProviderType().getDesc();\n    }\n\n    @Override\n    public String toString() {\n        return String.format(\"ServiceProvider{provider=\\'%s\\', order=%d}\",\n            getServiceProviderDesc(), getPriority());\n    }\n\n    @Override\n    public int hashCode() {\n        int result = getServiceProviderType().hashCode();\n        result = 31 * result + getPriority();\n        return result;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/registry/ContainerServiceProvider.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.registry;\n\nimport com.alipay.sofa.ark.spi.registry.ServiceProviderType;\n\n/**\n * Ark Container Service Provider, default service provider if provider is not set\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class ContainerServiceProvider extends AbstractServiceProvider {\n\n    private int order;\n\n    public ContainerServiceProvider() {\n        this(DEFAULT_PRECEDENCE);\n    }\n\n    public ContainerServiceProvider(int order) {\n        super(ServiceProviderType.ARK_CONTAINER);\n        this.order = order;\n    }\n\n    @Override\n    public int getPriority() {\n        return order;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/registry/DefaultServiceFilter.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.registry;\n\nimport com.alipay.sofa.ark.common.util.AssertUtils;\nimport com.alipay.sofa.ark.spi.registry.*;\n\n/**\n * Filter Service according to the given {@linkplain com.alipay.sofa.ark.spi.registry.ServiceProvider}\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic class DefaultServiceFilter<T> implements ServiceFilter<T> {\n\n    private ServiceProviderType providerType;\n\n    private Class<?>            serviceInterface;\n\n    private String              uniqueId;\n\n    @Override\n    public boolean match(ServiceReference serviceReference) {\n        AssertUtils.assertNotNull(serviceReference, \"ServiceReference should not be null\");\n        ServiceMetadata serviceMetadata = serviceReference.getServiceMetadata();\n        ServiceProvider provider = serviceMetadata.getServiceProvider();\n\n        boolean isMatch = matchProviderType(provider.getServiceProviderType());\n        isMatch &= matchServiceInterface(serviceMetadata.getInterfaceClass());\n        isMatch &= matchUniqueId(serviceMetadata.getUniqueId());\n        return isMatch;\n    }\n\n    private boolean matchProviderType(ServiceProviderType serviceProviderType) {\n        if (providerType == null) {\n            return true;\n        }\n        return providerType.equals(serviceProviderType);\n    }\n\n    private boolean matchServiceInterface(Class serviceInterface) {\n        if (this.serviceInterface == null) {\n            return true;\n        }\n        return this.serviceInterface.equals(serviceInterface);\n    }\n\n    private boolean matchUniqueId(String uniqueId) {\n        if (this.uniqueId == null) {\n            return true;\n        }\n        return this.uniqueId.equals(uniqueId);\n    }\n\n    public ServiceProviderType getProviderType() {\n        return providerType;\n    }\n\n    public DefaultServiceFilter setProviderType(ServiceProviderType providerType) {\n        this.providerType = providerType;\n        return this;\n    }\n\n    public Class<?> getServiceInterface() {\n        return serviceInterface;\n    }\n\n    public DefaultServiceFilter setServiceInterface(Class<?> serviceInterface) {\n        this.serviceInterface = serviceInterface;\n        return this;\n    }\n\n    public String getUniqueId() {\n        return uniqueId;\n    }\n\n    public DefaultServiceFilter setUniqueId(String uniqueId) {\n        this.uniqueId = uniqueId;\n        return this;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/registry/PluginServiceProvider.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.registry;\n\nimport com.alipay.sofa.ark.common.util.AssertUtils;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.registry.ServiceProviderType;\n\n/**\n * Plugin Service Provider, when service is published by plugin, then use this provider\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class PluginServiceProvider extends AbstractServiceProvider {\n\n    private Plugin plugin;\n\n    public PluginServiceProvider(Plugin plugin) {\n        super(ServiceProviderType.ARK_PLUGIN);\n        AssertUtils.assertNotNull(plugin, \"Plugin should not be null.\");\n        this.plugin = plugin;\n    }\n\n    @Override\n    public String getServiceProviderDesc() {\n        return String.format(\"%s:%s\", super.getServiceProviderDesc(), plugin.getPluginName());\n    }\n\n    @Override\n    public int getPriority() {\n        return plugin.getPriority();\n    }\n\n    public String getPluginName() {\n        return plugin.getPluginName();\n    }\n\n    public Plugin getPlugin() {\n        return plugin;\n    }\n\n    @Override\n    public int hashCode() {\n        int result = super.hashCode();\n        result = 31 * result + plugin.hashCode();\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (!super.equals(obj)) {\n            return false;\n        }\n\n        PluginServiceProvider serviceProvider = (PluginServiceProvider) obj;\n\n        return plugin.equals(serviceProvider.getPlugin());\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/registry/ServiceMetadataImpl.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.registry;\n\nimport com.alipay.sofa.ark.common.util.AssertUtils;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.spi.registry.ServiceMetadata;\nimport com.alipay.sofa.ark.spi.registry.ServiceProvider;\n\n/**\n * Service Metadata Implement\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class ServiceMetadataImpl implements ServiceMetadata {\n\n    private String          uniqueId;\n    private Class<?>        interfaceClass;\n    private ServiceProvider serviceProvider;\n\n    public ServiceMetadataImpl(Class<?> interfaceClass, String uniqueId,\n                               ServiceProvider serviceProvider) {\n        AssertUtils.assertNotNull(interfaceClass, \"Service interface should not be null.\");\n        AssertUtils.assertNotNull(uniqueId, \"Service uniqueId should not be null\");\n        AssertUtils.assertNotNull(serviceProvider, \"Service provider should not be null.\");\n        this.uniqueId = uniqueId;\n        this.interfaceClass = interfaceClass;\n        this.serviceProvider = serviceProvider;\n    }\n\n    @Override\n    public String getUniqueId() {\n        return uniqueId;\n    }\n\n    @Override\n    public Class<?> getInterfaceClass() {\n        return interfaceClass;\n    }\n\n    @Override\n    public ServiceProvider getServiceProvider() {\n        return serviceProvider;\n    }\n\n    @Override\n    public String getServiceName() {\n        if (StringUtils.isEmpty(uniqueId)) {\n            return interfaceClass.getCanonicalName();\n        } else {\n            return String.format(\"%s:%s\", interfaceClass.getCanonicalName(), uniqueId);\n        }\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n\n        if (obj == null || getClass() != obj.getClass()) {\n            return false;\n        }\n\n        ServiceMetadata serviceMetadata = (ServiceMetadata) obj;\n\n        if (!uniqueId.equals(serviceMetadata.getUniqueId())) {\n            return false;\n        }\n\n        if (!interfaceClass.equals(serviceMetadata.getInterfaceClass())) {\n            return false;\n        }\n\n        return serviceProvider.equals(serviceMetadata.getServiceProvider());\n    }\n\n    @Override\n    public int hashCode() {\n        int result = 1;\n        result = 31 * result + uniqueId.hashCode();\n        result = 31 * result + interfaceClass.hashCode();\n        result = 31 * result + serviceProvider.hashCode();\n        return result;\n    }\n\n    @Override\n    public String toString() {\n        return String.format(\"ServiceMetadata{service=\\'%s\\', provider=\\'%s\\'}\", getServiceName(),\n            getServiceProvider().toString());\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/registry/ServiceReferenceImpl.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.registry;\n\nimport com.alipay.sofa.ark.spi.registry.ServiceMetadata;\nimport com.alipay.sofa.ark.spi.registry.ServiceReference;\n\n/**\n * Service Reference Implement\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class ServiceReferenceImpl<T> implements ServiceReference<T> {\n\n    private ServiceMetadata serviceMetadata;\n\n    private T               serviceObject;\n\n    public ServiceReferenceImpl(ServiceMetadata serviceMetadata, T serviceObject) {\n        this.serviceMetadata = serviceMetadata;\n        this.serviceObject = serviceObject;\n    }\n\n    @Override\n    public T getService() {\n        return serviceObject;\n    }\n\n    @Override\n    public ServiceMetadata getServiceMetadata() {\n        return serviceMetadata;\n    }\n\n    @Override\n    public int getPriority() {\n        return getServiceMetadata().getServiceProvider().getPriority();\n    }\n\n    @Override\n    public int hashCode() {\n        return serviceMetadata.hashCode();\n    }\n\n    @Override\n    public String toString() {\n        return serviceMetadata.toString();\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/ArkServiceContainer.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.common.guice.AbstractArkGuiceModule;\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.common.util.OrderComparator;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.service.ArkService;\nimport com.alipay.sofa.ark.spi.service.biz.BizFactoryService;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.event.EventAdminService;\nimport com.alipay.sofa.ark.spi.service.injection.InjectionService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginFactoryService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport com.google.inject.Binding;\nimport com.google.inject.Guice;\nimport com.google.inject.Injector;\nimport com.google.inject.TypeLiteral;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.ServiceLoader;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\n/**\n * Ark Service Container\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class ArkServiceContainer {\n\n    private Injector         injector;\n\n    private List<ArkService> arkServiceList = new ArrayList<>();\n\n    private AtomicBoolean    started        = new AtomicBoolean(false);\n    private AtomicBoolean    stopped        = new AtomicBoolean(false);\n\n    private final String[]   arguments;\n\n    public ArkServiceContainer(String[] arguments) {\n        this.arguments = arguments;\n    }\n\n    /**\n     * Start Ark Service Container\n     * @throws ArkRuntimeException\n     * @since 0.1.0\n     */\n    public void start() throws ArkRuntimeException {\n        if (started.compareAndSet(false, true)) {\n            ClassLoader oldClassLoader = ClassLoaderUtils.pushContextClassLoader(getClass()\n                .getClassLoader());\n            try {\n                ArkLoggerFactory.getDefaultLogger().info(\"Begin to start ArkServiceContainer\");\n\n                injector = Guice.createInjector(findServiceModules());\n                for (Binding<ArkService> binding : injector\n                    .findBindingsByType(new TypeLiteral<ArkService>() {\n                    })) {\n                    arkServiceList.add(binding.getProvider().get());\n                }\n                Collections.sort(arkServiceList, new OrderComparator());\n\n                for (ArkService arkService : arkServiceList) {\n                    ArkLoggerFactory.getDefaultLogger().info(\n                        String.format(\"Init Service: %s\", arkService.getClass().getName()));\n                    arkService.init();\n                }\n\n                ArkServiceContainerHolder.setContainer(this);\n                ArkClient.setBizFactoryService(getService(BizFactoryService.class));\n                ArkClient.setBizManagerService(getService(BizManagerService.class));\n                ArkClient.setInjectionService(getService(InjectionService.class));\n                ArkClient.setEventAdminService(getService(EventAdminService.class));\n                ArkClient.setPluginManagerService(getService(PluginManagerService.class));\n                ArkClient.setPluginFactoryService(getService(PluginFactoryService.class));\n                ArkClient.setArguments(arguments);\n                ArkLoggerFactory.getDefaultLogger().info(\"Finish to start ArkServiceContainer\");\n            } finally {\n                ClassLoaderUtils.popContextClassLoader(oldClassLoader);\n            }\n\n        }\n\n    }\n\n    private List<AbstractArkGuiceModule> findServiceModules() throws ArkRuntimeException {\n        try {\n            List<AbstractArkGuiceModule> modules = new ArrayList<>();\n            for (AbstractArkGuiceModule module : ServiceLoader.load(AbstractArkGuiceModule.class)) {\n                modules.add(module);\n            }\n            return modules;\n        } catch (Throwable e) {\n            throw new ArkRuntimeException(e);\n        }\n    }\n\n    /**\n     * Get Service from ArkService Container\n     * @param clazz\n     * @param <T>\n     * @return\n     * @since 0.1.0\n     */\n    public <T> T getService(Class<T> clazz) {\n        return injector.getInstance(clazz);\n    }\n\n    /**\n     * Stop Ark Service Container\n     * @throws ArkRuntimeException\n     * @since 0.1.0\n     */\n    public void stop() throws ArkRuntimeException {\n        if (stopped.compareAndSet(false, true)) {\n            ArkLoggerFactory.getDefaultLogger().info(\"Begin to stop ArkServiceContainer\");\n\n            ClassLoader oldClassLoader = ClassLoaderUtils.pushContextClassLoader(getClass()\n                .getClassLoader());\n            try {\n                Collections.reverse(arkServiceList);\n                for (ArkService arkService : arkServiceList) {\n                    ArkLoggerFactory.getDefaultLogger().info(\n                        String.format(\"Dispose service: %s\", arkService.getClass().getName()));\n                    arkService.dispose();\n                }\n                ArkLoggerFactory.getDefaultLogger().info(\"Finish to stop ArkServiceContainer\");\n            } finally {\n                ClassLoaderUtils.popContextClassLoader(oldClassLoader);\n            }\n        }\n    }\n\n    /**\n     * Whether Ark Service Container is started or not\n     * @return\n     * @since 0.1.0\n     */\n    public boolean isStarted() {\n        return started.get();\n    }\n\n    /**\n     * Whether Ark Service Container is running or not\n     * @return\n     * @since 0.1.0\n     */\n    public boolean isRunning() {\n        return isStarted() && !stopped.get();\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/ArkServiceContainerHolder.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service;\n\n/**\n * Ark Service Container Holder\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class ArkServiceContainerHolder {\n\n    private static ArkServiceContainer container;\n\n    public static ArkServiceContainer getContainer() {\n        return container;\n    }\n\n    public static void setContainer(ArkServiceContainer container) {\n        ArkServiceContainerHolder.container = container;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/biz/BizCommandProvider.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.biz;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.common.thread.ThreadPoolManager;\nimport com.alipay.sofa.ark.common.util.EnvironmentUtils;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.BizOperation;\nimport com.alipay.sofa.ark.spi.model.BizState;\nimport com.alipay.sofa.ark.spi.service.ArkInject;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.session.CommandProvider;\n\nimport java.net.URL;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.regex.Pattern;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class BizCommandProvider implements CommandProvider {\n\n    @ArkInject\n    private BizManagerService bizManagerService;\n\n    @Override\n    public String getHelp() {\n        return HELP_MESSAGE;\n    }\n\n    @Override\n    public String handleCommand(String command) {\n        return new BizCommand(command).process();\n    }\n\n    @Override\n    public boolean validate(String command) {\n        return new BizCommand(command).isValidate();\n    }\n\n    static final String HELP_MESSAGE = \"Biz Command Tips:\\n\"\n                                       + \"  USAGE: biz [option...] [arguments...]\\n\"\n                                       + \"  SAMPLE: biz -m bizIdentityA bizIdentityB.\\n\"\n                                       + \"  -h  Shows the help message.\\n\"\n                                       + \"  -a  Shows all biz.\\n\"\n                                       + \"  -m  Shows the meta info of specified bizIdentity.\\n\"\n                                       + \"  -s  Shows the service info of specified bizIdentity.\\n\"\n                                       + \"  -d  Shows the detail info of specified bizIdentity.\\n\"\n                                       + \"  -i  Install biz of specified bizIdentity or bizUrl.\\n\"\n                                       + \"  -u  Uninstall biz of specified bizIdentity.\\n\"\n                                       + \"  -o  Switch biz of specified bizIdentity.\\n\";\n\n    class BizCommand {\n        private boolean        isValidate;\n        private Set<Character> options    = new HashSet<>();\n        private Set<String>    parameters = new HashSet<>();\n\n        BizCommand(String command) {\n            if (StringUtils.isEmpty(command)) {\n                isValidate = false;\n                return;\n            }\n\n            String[] syntax = command.trim().split(Constants.SPACE_SPLIT);\n            if (!\"biz\".equals(syntax[0])) {\n                isValidate = false;\n                return;\n            }\n\n            int argumentIndex = syntax.length;\n            // fetch all options and allow repetition\n            for (int i = 1; i < syntax.length; ++i) {\n                if (!syntax[i].startsWith(\"-\")) {\n                    argumentIndex = i;\n                    break;\n                }\n                if (syntax[i].startsWith(\"-\") && syntax[i].length() == 1) {\n                    isValidate = false;\n                    return;\n                }\n                for (int j = 1; j < syntax[i].length(); ++j) {\n                    options.add(syntax[i].charAt(j));\n                }\n            }\n\n            // only the following option can be allowed.\n            for (Character option : options) {\n                switch (option) {\n                    case 'h':\n                    case 'a':\n                    case 'm':\n                    case 's':\n                    case 'd':\n                    case 'i':\n                    case 'u':\n                    case 'o':\n                        continue;\n                    default:\n                        isValidate = false;\n                        return;\n                }\n            }\n\n            // check whether options is empty\n            if (options.isEmpty()) {\n                isValidate = false;\n                return;\n            }\n\n            // '-h' or '-a' option can not be combined with other option, such as '-m'\n            if (options.contains('h') || options.contains('a') || options.contains('i')\n                || options.contains('u') || options.contains('o')) {\n                if (options.size() > 1) {\n                    isValidate = false;\n                    return;\n                }\n            }\n\n            // take the rest option as parameters\n            while (argumentIndex < syntax.length) {\n                parameters.add(syntax[argumentIndex++]);\n            }\n\n            // '-h' or '-a' option need not any parameter\n            if ((options.contains('h') || options.contains('a')) && parameters.size() > 0) {\n                isValidate = false;\n                return;\n            }\n\n            // if option is not 'h' or 'a', parameter should not be empty\n            if (!(options.contains('h') || options.contains('a')) && parameters.isEmpty()) {\n                isValidate = false;\n                return;\n            }\n\n            // if option is 'i' or 'u' or 'o', parameter count should be only one.\n            if (options.contains('i') || options.contains('u') || options.contains('o')) {\n                if (parameters.size() > 1) {\n                    isValidate = false;\n                    return;\n                }\n            }\n\n            isValidate = true;\n        }\n\n        boolean isValidate() {\n            return isValidate;\n        }\n\n        String process() {\n            if (!isValidate) {\n                return \"Error command format. Pls type 'biz -h' to get help message\\n\";\n            }\n            StringBuilder sb = new StringBuilder(512);\n\n            if (options.contains('h')) {\n                return HELP_MESSAGE;\n            } else if (options.contains('a')) {\n                return bizList();\n            } else if (options.contains('i')) {\n                return installBiz();\n            } else if (options.contains('u')) {\n                return uninstallBiz();\n            } else if (options.contains('o')) {\n                return switchBiz();\n            } else {\n                Set<String> candidates = bizManagerService.getAllBizIdentities();\n                boolean matched = false;\n                for (String pattern : parameters) {\n                    for (String candidate : candidates) {\n                        if (Pattern.matches(pattern, candidate)) {\n                            matched = true;\n                            sb.append(bizInfo(candidate));\n                        }\n                    }\n                }\n                if (!matched) {\n                    sb.append(\"no matched biz candidates.\").append(\"\\n\");\n                }\n            }\n            return sb.toString();\n        }\n\n        String bizList() {\n            List<Biz> bizList = bizManagerService.getBizInOrder();\n            StringBuilder sb = new StringBuilder(128);\n            for (Biz biz : bizList) {\n                sb.append(biz.getIdentity()).append(Constants.STRING_COLON)\n                    .append(biz.getBizState()).append(\"\\n\");\n            }\n            sb.append(\"biz count = \").append(bizList.size()).append(\"\\n\");\n            return sb.toString();\n        }\n\n        String installBiz() {\n\n            if (EnvironmentUtils.isOpenSecurity()) {\n                return \"Cannot execute install command in security mode.\\n\";\n            }\n\n            if (!isReadyInstall()) {\n                return \"Exists some biz whose state is neither 'activated' nor 'deactivated'.\\n\";\n            }\n            ThreadPoolManager.getThreadPool(Constants.TELNET_COMMAND_THREAD_POOL_NAME)\n                .getExecutor().execute(new Runnable() {\n                    @Override\n                    public void run() {\n                        BizOperation bizOperation = new BizOperation()\n                            .setOperationType(BizOperation.OperationType.INSTALL);\n                        String param = parameters.toArray(new String[] {})[0];\n                        try {\n                            URL url = new URL(param);\n                            bizOperation.putParameter(Constants.CONFIG_BIZ_URL, param);\n                        } catch (Throwable t) {\n                            String[] nameAndVersion = param.split(Constants.STRING_COLON);\n                            if (nameAndVersion.length != 2) {\n                                ArkLoggerFactory.getDefaultLogger().error(\n                                    \"Invalid telnet biz install command {}\", param);\n                                return;\n                            }\n                            bizOperation.setBizName(nameAndVersion[0]).setBizVersion(\n                                nameAndVersion[1]);\n                        }\n                        try {\n                            ArkClient.installOperation(bizOperation);\n                        } catch (Throwable throwable) {\n                            ArkLoggerFactory.getDefaultLogger().error(\n                                \"Fail to process telnet install command: \" + param, throwable);\n                        }\n                    }\n                });\n            return \"Start to process install command now, pls wait and check.\";\n        }\n\n        String uninstallBiz() {\n\n            if (EnvironmentUtils.isOpenSecurity()) {\n                return \"Cannot execute uninstall command in security mode.\\n\";\n            }\n\n            ThreadPoolManager.getThreadPool(Constants.TELNET_COMMAND_THREAD_POOL_NAME)\n                .getExecutor().execute(new Runnable() {\n                    @Override\n                    public void run() {\n                        String param = parameters.toArray(new String[] {})[0];\n                        String[] nameAndVersion = param.split(Constants.STRING_COLON);\n                        if (nameAndVersion.length != 2) {\n                            ArkLoggerFactory.getDefaultLogger().error(\n                                \"Invalid telnet biz uninstall command {}\", param);\n                            return;\n                        }\n                        try {\n                            ArkClient.uninstallBiz(nameAndVersion[0], nameAndVersion[1]);\n                        } catch (Throwable throwable) {\n                            ArkLoggerFactory.getDefaultLogger().error(\n                                \"Fail to process telnet uninstall command: \" + param, throwable);\n                        }\n                    }\n                });\n            return \"Start to process uninstall command now, pls wait and check.\";\n        }\n\n        String switchBiz() {\n            ThreadPoolManager.getThreadPool(Constants.TELNET_COMMAND_THREAD_POOL_NAME)\n                .getExecutor().execute(new Runnable() {\n                    @Override\n                    public void run() {\n                        String param = parameters.toArray(new String[] {})[0];\n                        String[] nameAndVersion = param.split(Constants.STRING_COLON);\n                        if (nameAndVersion.length != 2) {\n                            ArkLoggerFactory.getDefaultLogger().error(\n                                \"Invalid telnet biz switch command {}\", param);\n                            return;\n                        }\n                        try {\n                            ArkClient.switchBiz(nameAndVersion[0], nameAndVersion[1]);\n                        } catch (Throwable throwable) {\n                            ArkLoggerFactory.getDefaultLogger().error(\n                                \"Fail to process telnet switch command: \" + param, throwable);\n                        }\n                    }\n                });\n            return \"Start to process switch command now, pls wait and check.\";\n        }\n\n        String bizInfo(String bizIdentity) {\n            Biz biz = bizManagerService.getBizByIdentity(bizIdentity);\n            if (biz == null) {\n                return \"Invalid bizIdentity: \" + bizIdentity + \"\\n\";\n            }\n            StringBuilder sb = new StringBuilder(256);\n            // print biz meta info\n            if (options.contains('m')) {\n                sb.append(\"BizName:                  \").append(biz.getBizName()).append(\"\\n\");\n                sb.append(\"Version:                  \").append(biz.getBizVersion()).append(\"\\n\");\n                sb.append(\"Priority:                 \").append(biz.getPriority()).append(\"\\n\");\n                sb.append(\"MainClass:                \").append(biz.getMainClass()).append(\"\\n\");\n                sb.append(\"WebContextPath:           \").append(biz.getWebContextPath())\n                    .append(\"\\n\");\n                sb.append(\"Deny Import Packages:     \")\n                    .append(StringUtils.setToStr(biz.getDenyImportPackages(), \",\", \"\\\\\"))\n                    .append(\"\\n\");\n                sb.append(\"Deny Import Classes:      \")\n                    .append(StringUtils.setToStr(biz.getDenyImportClasses(), \",\", \"\\\\\"))\n                    .append(\"\\n\");\n                sb.append(\"Deny Import Resources:    \")\n                    .append(StringUtils.setToStr(biz.getDenyImportResources(), \",\", \"\\\\\"))\n                    .append(\"\\n\");\n            }\n\n            // print biz service info\n            if (options.contains('s')) {\n                // TODO\n            }\n\n            // print biz detail info\n            if (options.contains('d')) {\n                sb.append(\"ClassLoader: \").append(biz.getBizClassLoader()).append(\"\\n\");\n                sb.append(\"ClassPath:   \").append(join(biz.getClassPath(), \",\")).append(\"\\n\");\n            }\n            sb.append(\"\\n\");\n            return sb.toString();\n        }\n\n        public boolean isReadyInstall() {\n            for (Biz biz : bizManagerService.getBizInOrder()) {\n                if (biz.getBizState() != BizState.ACTIVATED\n                    && biz.getBizState() != BizState.DEACTIVATED) {\n                    return false;\n                }\n            }\n            return true;\n        }\n\n        String join(URL[] urls, String separator) {\n            Set<String> set = new HashSet<>();\n            if (urls != null) {\n                for (URL url : urls) {\n                    set.add(url.getPath());\n                }\n            }\n            return StringUtils.setToStr(set, separator, \"\\\\\");\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/biz/BizDeployServiceImpl.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.biz;\n\nimport com.alipay.sofa.ark.common.log.ArkLogger;\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.registry.ServiceReference;\nimport com.alipay.sofa.ark.spi.service.biz.BizDeployService;\nimport com.alipay.sofa.ark.spi.service.biz.BizDeployer;\nimport com.alipay.sofa.ark.spi.service.registry.RegistryService;\n\nimport javax.inject.Inject;\nimport javax.inject.Singleton;\n\n/**\n * Service implementation to deploy Biz\n *\n * @author qilong.zql\n * @since 0.4.0\n */\n@Singleton\npublic class BizDeployServiceImpl implements BizDeployService {\n\n    @Inject\n    private RegistryService registryService;\n\n    private BizDeployer     bizDeployer;\n\n    @Override\n    public void deploy(String[] args) throws ArkRuntimeException {\n        ServiceReference<BizDeployer> serviceReference = registryService\n            .referenceService(BizDeployer.class);\n        bizDeployer = serviceReference.getService();\n\n        ArkLoggerFactory.getDefaultLogger().info(\n            String.format(\"BizDeployer=\\'%s\\' is starting.\", bizDeployer.getDesc()));\n\n        bizDeployer.init(args);\n        bizDeployer.deploy();\n    }\n\n    @Override\n    public void unDeploy() throws ArkRuntimeException {\n        if (bizDeployer != null) {\n            ArkLoggerFactory.getDefaultLogger().info(\n                String.format(\"BizDeployer=\\'%s\\' is stopping.\", bizDeployer.getDesc()));\n            bizDeployer.unDeploy();\n        }\n    }\n\n    @Override\n    public void init() throws ArkRuntimeException {\n        // no action\n    }\n\n    @Override\n    public void dispose() throws ArkRuntimeException {\n        unDeploy();\n    }\n\n    @Override\n    public int getPriority() {\n        return DEFAULT_PRECEDENCE;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/biz/BizFactoryServiceImpl.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.biz;\n\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.common.util.AssertUtils;\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.common.util.FileUtils;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.container.service.classloader.BizClassLoader;\nimport com.alipay.sofa.ark.loader.ExplodedBizArchive;\nimport com.alipay.sofa.ark.loader.DirectoryBizArchive;\nimport com.alipay.sofa.ark.loader.JarBizArchive;\nimport com.alipay.sofa.ark.loader.archive.JarFileArchive;\nimport com.alipay.sofa.ark.loader.jar.JarFile;\nimport com.alipay.sofa.ark.spi.archive.Archive;\nimport com.alipay.sofa.ark.spi.archive.BizArchive;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.model.*;\nimport com.alipay.sofa.ark.spi.model.BizInfo.StateChangeReason;\nimport com.alipay.sofa.ark.spi.service.biz.BizFactoryService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport com.google.inject.Inject;\nimport com.google.inject.Singleton;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.jar.Attributes;\nimport java.util.stream.Stream;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.*;\n\n/**\n * {@link BizFactoryService}\n *\n * @author qilong.zql\n * @since 0.4.0\n */\n@Singleton\npublic class BizFactoryServiceImpl implements BizFactoryService {\n\n    @Inject\n    private PluginManagerService pluginManagerService;\n\n    @Override\n    public Biz createBiz(BizArchive bizArchive) throws IOException {\n        return createBiz(bizArchive, new BizConfig());\n    }\n\n    @Override\n    public Biz createBiz(BizArchive bizArchive, URL[] extensionUrls) throws IOException {\n        BizConfig bizConfig = new BizConfig();\n        bizConfig.setExtensionUrls(extensionUrls);\n        return createBiz(bizArchive, bizConfig);\n    }\n\n    @Override\n    public Biz createBiz(File file) throws IOException {\n        BizArchive bizArchive = prepareBizArchive(file);\n        return createBiz(bizArchive, new BizConfig());\n    }\n\n    @Override\n    public Biz createBiz(File file, URL[] extensionUrls) throws IOException {\n        BizArchive bizArchive = prepareBizArchive(file);\n        BizConfig bizConfig = new BizConfig();\n        bizConfig.setExtensionUrls(extensionUrls);\n        return createBiz(bizArchive, bizConfig);\n    }\n\n    @Override\n    public Biz createBiz(BizOperation bizOperation, File file) throws IOException {\n        BizArchive bizArchive = prepareBizArchive(file);\n        BizConfig bizConfig = new BizConfig();\n        bizConfig.setSpecifiedVersion(bizOperation.getBizVersion());\n        return createBiz(bizArchive, bizConfig);\n    }\n\n    @Override\n    public Biz createBiz(File file, BizConfig bizConfig) throws IOException {\n        BizArchive bizArchive = prepareBizArchive(file);\n        return createBiz(bizArchive, bizConfig);\n    }\n\n    @Override\n    public Biz createBiz(BizArchive bizArchive, BizConfig bizConfig) throws IOException {\n        AssertUtils.isTrue(isArkBiz(bizArchive), \"Archive must be a ark biz!\");\n        AssertUtils.isTrue(bizConfig != null, \"BizConfig must not be null!\");\n\n        Attributes manifestMainAttributes = bizArchive.getManifest().getMainAttributes();\n        String mainClass = manifestMainAttributes.getValue(MAIN_CLASS_ATTRIBUTE);\n        String startClass = manifestMainAttributes.getValue(START_CLASS_ATTRIBUTE);\n        BizModel bizModel = new BizModel();\n        bizModel\n            .setBizState(BizState.RESOLVED, StateChangeReason.CREATED)\n            .setBizName(manifestMainAttributes.getValue(ARK_BIZ_NAME))\n            .setBizVersion(\n                !StringUtils.isEmpty(bizConfig.getSpecifiedVersion()) ? bizConfig\n                    .getSpecifiedVersion() : manifestMainAttributes.getValue(ARK_BIZ_VERSION))\n            .setBizUrl(!(bizArchive instanceof DirectoryBizArchive) ? bizArchive.getUrl() : null)\n            .setMainClass(!StringUtils.isEmpty(startClass) ? startClass : mainClass)\n            .setPriority(manifestMainAttributes.getValue(PRIORITY_ATTRIBUTE))\n            .setWebContextPath(manifestMainAttributes.getValue(WEB_CONTEXT_PATH))\n            .setDenyImportPackages(manifestMainAttributes.getValue(DENY_IMPORT_PACKAGES))\n            .setDenyImportClasses(manifestMainAttributes.getValue(DENY_IMPORT_CLASSES))\n            .setDenyImportResources(manifestMainAttributes.getValue(DENY_IMPORT_RESOURCES))\n            .setInjectPluginDependencies(\n                getInjectDependencies(manifestMainAttributes.getValue(INJECT_PLUGIN_DEPENDENCIES)))\n            .setInjectExportPackages(manifestMainAttributes.getValue(INJECT_EXPORT_PACKAGES))\n            .setDeclaredLibraries(manifestMainAttributes.getValue(DECLARED_LIBRARIES))\n            .setClassPath(getMergedBizClassPath(bizArchive.getUrls(), bizConfig.getExtensionUrls()));\n\n        // prepare dependent plugins and plugin export map\n        List<String> dependentPlugins = bizConfig.getDependentPlugins();\n        if (dependentPlugins == null || dependentPlugins.isEmpty()) {\n            dependentPlugins = StringUtils.strToList(\n                manifestMainAttributes.getValue(\"dependent-plugins\"),\n                Constants.MANIFEST_VALUE_SPLIT);\n        }\n        resolveExportMapIfNecessary(bizModel, dependentPlugins);\n\n        // must be after prepare dependent plugins\n        bizModel.setPluginClassPath(getPluginURLs(bizModel));\n\n        // create biz classloader\n        BizClassLoader bizClassLoader = new BizClassLoader(bizModel.getIdentity(),\n            getBizUcp(bizModel), bizArchive instanceof ExplodedBizArchive\n                                 || bizArchive instanceof DirectoryBizArchive);\n        bizClassLoader.setBizModel(bizModel);\n        bizModel.setClassLoader(bizClassLoader);\n\n        // set biz work dir\n        if (bizModel.getBizUrl() != null) {\n            bizModel.setBizTempWorkDir(new File(bizModel.getBizUrl().getFile()));\n        }\n\n        return bizModel;\n    }\n\n    @Override\n    public Biz createEmbedMasterBiz(ClassLoader masterClassLoader) {\n        BizModel bizModel = new BizModel();\n        bizModel.setBizState(BizState.RESOLVED, StateChangeReason.CREATED)\n            .setBizName(ArkConfigs.getStringValue(MASTER_BIZ)).setBizVersion(\"1.0.0\")\n            .setMainClass(\"embed main\").setPriority(\"100\").setWebContextPath(\"/\")\n            .setDenyImportPackages(null).setDenyImportClasses(null).setDenyImportResources(null)\n            .setInjectPluginDependencies(new HashSet<>()).setInjectExportPackages(null)\n            .setClassPath(ClassLoaderUtils.getURLs(masterClassLoader))\n            .setClassLoader(masterClassLoader);\n        return bizModel;\n    }\n\n    private BizArchive prepareBizArchive(File file) throws IOException {\n        BizArchive bizArchive;\n        boolean unpackBizWhenInstall = Boolean.parseBoolean(ArkConfigs.getStringValue(\n            UNPACK_BIZ_WHEN_INSTALL, \"true\"));\n        if (ArkConfigs.isEmbedEnable() && unpackBizWhenInstall) {\n            File unpackFile = FileUtils.file(file.getAbsolutePath() + \"-unpack\");\n            if (!unpackFile.exists()) {\n                unpackFile = FileUtils.unzip(file, file.getAbsolutePath() + \"-unpack\");\n            }\n            if (file.exists()) {\n                file.delete();\n            }\n            file = unpackFile;\n            bizArchive = new ExplodedBizArchive(unpackFile);\n        } else {\n            JarFile bizFile = new JarFile(file);\n            JarFileArchive jarFileArchive = new JarFileArchive(bizFile);\n            bizArchive = new JarBizArchive(jarFileArchive);\n        }\n        return bizArchive;\n    }\n\n    private URL[] getMergedBizClassPath(URL[] bizArchiveUrls, URL[] extensionUrls) {\n        if (extensionUrls == null || extensionUrls.length == 0) {\n            return bizArchiveUrls;\n        }\n        return Stream.concat(Arrays.stream(bizArchiveUrls), Arrays.stream(extensionUrls)).toArray(URL[]::new);\n    }\n\n    private void resolveExportMapIfNecessary(BizModel bizModel, List<String> dependentPlugins) {\n        Set<Plugin> plugins = new HashSet<>();\n        if (ArkConfigs.isBizSpecifyDependentPluginsEnable()) {\n            if (dependentPlugins != null && !dependentPlugins.isEmpty()) {\n                for (String pluginName : dependentPlugins) {\n                    Plugin plugin = pluginManagerService.getPluginByName(pluginName);\n                    plugins.add(plugin);\n                }\n            }\n        } else {\n            plugins.addAll(pluginManagerService.getPluginsInOrder());\n        }\n\n        bizModel.setDependentPlugins(plugins);\n        for (Plugin plugin : plugins) {\n            for (String exportIndex : plugin.getExportPackageNodes()) {\n                bizModel.getExportNodeAndClassLoaderMap().putIfAbsent(exportIndex, plugin);\n            }\n            for (String exportIndex : plugin.getExportPackageStems()) {\n                bizModel.getExportStemAndClassLoaderMap().putIfAbsent(exportIndex, plugin);\n            }\n            for (String exportIndex : plugin.getExportClasses()) {\n                bizModel.getExportClassAndClassLoaderMap().putIfAbsent(exportIndex, plugin);\n            }\n            for (String resource : plugin.getExportResources()) {\n                bizModel.getExportResourceAndClassLoaderMap().putIfAbsent(resource,\n                    new LinkedList<>());\n                bizModel.getExportResourceAndClassLoaderMap().get(resource).add(plugin);\n            }\n            for (String resource : plugin.getExportPrefixResourceStems()) {\n                bizModel.getExportPrefixStemResourceAndClassLoaderMap().putIfAbsent(resource,\n                    new LinkedList<>());\n                bizModel.getExportPrefixStemResourceAndClassLoaderMap().get(resource).add(plugin);\n            }\n            for (String resource : plugin.getExportSuffixResourceStems()) {\n                bizModel.getExportSuffixStemResourceAndClassLoaderMap().putIfAbsent(resource,\n                    new LinkedList<>());\n                bizModel.getExportSuffixStemResourceAndClassLoaderMap().get(resource).add(plugin);\n            }\n        }\n    }\n\n    private Set<String> getInjectDependencies(String injectPluginDependencies) {\n        Set<String> dependencies = new HashSet<>();\n        if (StringUtils.strToSet(injectPluginDependencies, Constants.MANIFEST_VALUE_SPLIT) != null) {\n            dependencies.addAll(StringUtils.strToSet(injectPluginDependencies,\n                Constants.MANIFEST_VALUE_SPLIT));\n        }\n        return dependencies;\n    }\n\n    private boolean isArkBiz(BizArchive bizArchive) {\n        if (ArkConfigs.isEmbedEnable() && bizArchive instanceof ExplodedBizArchive) {\n            return true;\n        }\n        return bizArchive.isEntryExist(new Archive.EntryFilter() {\n            @Override\n            public boolean matches(Archive.Entry entry) {\n                return !entry.isDirectory() && entry.getName().equals(Constants.ARK_BIZ_MARK_ENTRY);\n            }\n        });\n    }\n\n    private URL[] getBizUcp(BizModel bizModel) {\n        List<URL> bizUcp = new ArrayList<>();\n        bizUcp.addAll(Arrays.asList(bizModel.getClassPath()));\n        bizUcp.addAll(Arrays.asList(getPluginURLs(bizModel)));\n        return bizUcp.toArray(new URL[bizUcp.size()]);\n    }\n\n    private URL[] getPluginURLs(BizModel bizModel) {\n        List<URL> pluginUrls = new ArrayList<>();\n        for (Plugin plugin : bizModel.getDependentPlugins()) {\n            pluginUrls.add(plugin.getPluginURL());\n        }\n        return pluginUrls.toArray(new URL[pluginUrls.size()]);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/biz/BizManagerServiceImpl.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.biz;\n\nimport com.alipay.sofa.ark.common.util.AssertUtils;\nimport com.alipay.sofa.ark.common.util.BizIdentityUtils;\nimport com.alipay.sofa.ark.common.util.OrderComparator;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.BizInfo.StateChangeReason;\nimport com.alipay.sofa.ark.spi.model.BizState;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.google.inject.Singleton;\n\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.locks.ReentrantLock;\n\n/**\n *  Service Implementation to manager ark biz\n *\n * @author ruoshan\n * @since 0.1.0\n */\n@Singleton\npublic class BizManagerServiceImpl implements BizManagerService {\n\n    private final ConcurrentHashMap<String, ConcurrentHashMap<String, Biz>> bizRegistration = new ConcurrentHashMap<>();\n\n    private final ConcurrentHashMap<String, ReentrantLock>                  bizLocks        = new ConcurrentHashMap<>();\n\n    @Override\n    public ReentrantLock getBizLock(String bizName) {\n        AssertUtils.isFalse(StringUtils.isEmpty(bizName), \"Biz name must not be empty.\");\n        bizLocks.putIfAbsent(bizName, new ReentrantLock());\n        return bizLocks.get(bizName);\n    }\n\n    @Override\n    public boolean registerBiz(Biz biz) {\n        AssertUtils.assertNotNull(biz, \"Biz must not be null.\");\n        AssertUtils.isTrue(biz.getBizState() == BizState.RESOLVED, \"BizState must be RESOLVED.\");\n        // Two level cache here. First level cache key is biz name, and value is versions cache.\n        // Second level cache key is version, value is biz model.\n        bizRegistration.putIfAbsent(biz.getBizName(), new ConcurrentHashMap<>(16));\n        ConcurrentHashMap<String, Biz> bizCache = bizRegistration.get(biz.getBizName());\n        return bizCache.put(biz.getBizVersion(), biz) == null;\n    }\n\n    @Override\n    public Biz unRegisterBiz(String bizName, String bizVersion) {\n        AssertUtils.isTrue(getBizState(bizName, bizVersion) != BizState.RESOLVED,\n            \"Biz whose state is resolved must not be un-registered.\");\n        return unRegisterBizStrictly(bizName, bizVersion);\n    }\n\n    @Override\n    public Biz unRegisterBizStrictly(String bizName, String bizVersion) {\n        AssertUtils.isFalse(StringUtils.isEmpty(bizName), \"Biz name must not be empty.\");\n        AssertUtils.isFalse(StringUtils.isEmpty(bizVersion), \"Biz version must not be empty.\");\n        ConcurrentHashMap<String, Biz> bizCache = bizRegistration.get(bizName);\n        if (bizCache != null) {\n            return bizCache.remove(bizVersion);\n        }\n        return null;\n    }\n\n    @Override\n    public List<Biz> getBiz(String bizName) {\n        AssertUtils.isFalse(StringUtils.isEmpty(bizName), \"Biz name must not be empty.\");\n        ConcurrentHashMap<String, Biz> bizCache = bizRegistration.get(bizName);\n        List<Biz> bizList = new ArrayList<>();\n        if (bizCache != null) {\n            bizList.addAll(bizCache.values());\n        }\n        return bizList;\n    }\n\n    @Override\n    public Biz getBiz(String bizName, String bizVersion) {\n        AssertUtils.isFalse(StringUtils.isEmpty(bizName), \"Biz name must not be empty.\");\n        AssertUtils.isFalse(StringUtils.isEmpty(bizVersion), \"Biz version must not be empty.\");\n        ConcurrentHashMap<String, Biz> bizCache = bizRegistration.get(bizName);\n        if (bizCache != null) {\n            return bizCache.get(bizVersion);\n        }\n        return null;\n    }\n\n    @Override\n    public Biz getBizByIdentity(String bizIdentity) {\n        AssertUtils.isTrue(BizIdentityUtils.isValid(bizIdentity),\n            \"Format of Biz Identity is error.\");\n        String[] str = bizIdentity.split(Constants.STRING_COLON);\n        return getBiz(str[0], str[1]);\n    }\n\n    @Override\n    public Biz getBizByClassLoader(ClassLoader classLoader) {\n        for (Map.Entry<String, ConcurrentHashMap<String, Biz>> bizMapEntry : bizRegistration\n            .entrySet()) {\n            for (Map.Entry<String, Biz> bizEntry : bizMapEntry.getValue().entrySet()) {\n                Biz biz = bizEntry.getValue();\n                if (biz.getBizClassLoader().equals(classLoader)) {\n                    return biz;\n                }\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public Set<String> getAllBizNames() {\n        return bizRegistration.keySet();\n    }\n\n    @Override\n    public Set<String> getAllBizIdentities() {\n        Set<String> bizIdentities = new HashSet<>();\n        for (Biz biz : getBizInOrder()) {\n            bizIdentities.add(biz.getIdentity());\n        }\n        return bizIdentities;\n    }\n\n    @Override\n    public List<Biz> getBizInOrder() {\n        List<Biz> bizList = new ArrayList<>();\n        for (String bizName : bizRegistration.keySet()) {\n            bizList.addAll(bizRegistration.get(bizName).values());\n        }\n        Collections.sort(bizList, new OrderComparator());\n        return bizList;\n    }\n\n    @Override\n    public Biz getActiveBiz(String bizName) {\n        AssertUtils.isFalse(StringUtils.isEmpty(bizName), \"Biz name must not be empty.\");\n        Map<String, Biz> bizCache = bizRegistration.get(bizName);\n        if (bizCache != null) {\n            for (Biz biz : bizCache.values()) {\n                if (biz.getBizState() == BizState.ACTIVATED) {\n                    return biz;\n                }\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public boolean isActiveBiz(String bizName, String bizVersion) {\n        AssertUtils.isFalse(StringUtils.isEmpty(bizName), \"Biz name must not be empty.\");\n        AssertUtils.isFalse(StringUtils.isEmpty(bizVersion), \"Biz version must not be empty.\");\n        Map<String, Biz> bizCache = bizRegistration.get(bizName);\n        if (bizCache != null) {\n            Biz biz = bizCache.get(bizVersion);\n            return biz != null && (biz.getBizState() == BizState.ACTIVATED);\n        }\n        return false;\n    }\n\n    @Override\n    public void activeBiz(String bizName, String bizVersion) {\n        AssertUtils.isFalse(StringUtils.isEmpty(bizName), \"Biz name must not be empty.\");\n        AssertUtils.isFalse(StringUtils.isEmpty(bizVersion), \"Biz version must not be empty.\");\n        Biz biz = getBiz(bizName, bizVersion);\n        Biz activeBiz = getActiveBiz(bizName);\n        if (biz != null && biz.getBizState() == BizState.DEACTIVATED) {\n            if (activeBiz != null) {\n                ((BizModel) activeBiz).setBizState(BizState.DEACTIVATED,\n                    StateChangeReason.SWITCHED,\n                    String.format(\"switch to new version %s\", biz.getIdentity()));\n            }\n            String message = activeBiz == null ? \"\" : String.format(\"switch from old version: %s\",\n                activeBiz.getIdentity());\n            ((BizModel) biz).setBizState(BizState.ACTIVATED, StateChangeReason.SWITCHED, message);\n        }\n    }\n\n    @Override\n    public BizState getBizState(String bizName, String bizVersion) {\n        AssertUtils.isFalse(StringUtils.isEmpty(bizName), \"Biz name must not be empty.\");\n        AssertUtils.isFalse(StringUtils.isEmpty(bizVersion), \"Biz version must not be empty.\");\n        Map<String, Biz> bizCache = bizRegistration.get(bizName);\n        if (bizCache != null) {\n            Biz biz = bizCache.get(bizVersion);\n            return biz != null ? biz.getBizState() : BizState.UNRESOLVED;\n        }\n        return BizState.UNRESOLVED;\n    }\n\n    @Override\n    public BizState getBizState(String bizIdentity) {\n        AssertUtils.isTrue(BizIdentityUtils.isValid(bizIdentity),\n            \"Format of Biz Identity is error.\");\n        String[] str = bizIdentity.split(Constants.STRING_COLON);\n        return getBizState(str[0], str[1]);\n    }\n\n    @Override\n    public boolean removeAndAddBiz(Biz addingBiz, Biz removingBiz) {\n        Set<Map.Entry<String, ConcurrentHashMap<String, Biz>>> bizEntrySet = bizRegistration.entrySet();\n        bizEntrySet.forEach(item -> {\n            String bizName = item.getKey();\n            if (removingBiz.getBizName().equals(bizName)){\n                bizEntrySet.remove(item);\n                return;\n            }\n        });\n        bizRegistration.putIfAbsent(addingBiz.getBizName(), new ConcurrentHashMap<>(16));\n        return bizRegistration.get(addingBiz.getBizName()).put(addingBiz.getBizVersion(), addingBiz) == null;\n    }\n\n    @Override\n    public ConcurrentHashMap<String, ConcurrentHashMap<String, Biz>> getBizRegistration() {\n        return bizRegistration;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/biz/DefaultBizDeployer.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.biz;\n\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.BizState;\nimport com.alipay.sofa.ark.spi.service.ArkInject;\nimport com.alipay.sofa.ark.spi.service.biz.BizDeployer;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\n\n/**\n * Biz Deployer to deploy Biz\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic class DefaultBizDeployer implements BizDeployer {\n\n    @ArkInject\n    private BizManagerService bizManagerService;\n\n    private String[]          arguments;\n\n    @Override\n    public void init(String[] args) {\n        this.arguments = args;\n    }\n\n    @Override\n    public void deploy() {\n        for (Biz biz : bizManagerService.getBizInOrder()) {\n            if (isEmbedStaticBizAndIllegalState(biz)) {\n                continue;\n            }\n            try {\n                ArkLoggerFactory.getDefaultLogger().info(\n                    String.format(\"Begin to start biz: %s\", biz.getBizName()));\n                biz.start(arguments);\n                ArkLoggerFactory.getDefaultLogger().info(\n                    String.format(\"Finish to start biz: %s\", biz.getBizName()));\n            } catch (Throwable e) {\n                ArkLoggerFactory.getDefaultLogger().error(\n                    String.format(\"Start biz: %s meet error\", biz.getBizName()), e);\n                throw new ArkRuntimeException(e);\n            }\n        }\n    }\n\n    @Override\n    public void unDeploy() {\n        for (Biz biz : bizManagerService.getBizInOrder()) {\n            try {\n                ArkLoggerFactory.getDefaultLogger().info(\n                    String.format(\"Begin to stop biz: %s\", biz.getBizName()));\n                biz.stop();\n                ArkLoggerFactory.getDefaultLogger().info(\n                    String.format(\"Finish to stop biz: %s\", biz.getBizName()));\n            } catch (Throwable e) {\n                ArkLoggerFactory.getDefaultLogger().error(\n                    String.format(\"stop biz: %s meet error\", biz.getBizName()), e);\n                throw new ArkRuntimeException(e);\n            }\n        }\n    }\n\n    public boolean isEmbedStaticBizAndIllegalState(Biz biz) {\n        return ArkConfigs.isEmbedStaticBizEnable() && !BizState.RESOLVED.equals(biz.getBizState());\n    }\n\n    @Override\n    public String getDesc() {\n        return String.format(\"{name=\\'%s\\', provider=\\'%s\\'}\", \"DefaultBizDeployer\",\n            \"Ark Container\");\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/classloader/AbstractClasspathClassLoader.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.classloader;\n\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.bootstrap.UseFastConnectionExceptionsEnumeration;\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.container.model.PluginModel;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.exception.ArkLoaderException;\nimport com.alipay.sofa.ark.loader.jar.Handler;\nimport com.alipay.sofa.ark.loader.jar.JarUtils;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.service.classloader.ClassLoaderService;\nimport com.google.common.cache.Cache;\nimport org.apache.commons.io.FileUtils;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.JarURLConnection;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.net.URLConnection;\nimport java.security.AccessController;\nimport java.security.PrivilegedExceptionAction;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Enumeration;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.ExecutionException;\nimport java.util.jar.JarFile;\n\nimport static com.google.common.cache.CacheBuilder.newBuilder;\nimport static java.util.concurrent.TimeUnit.SECONDS;\n\n/**\n *\n * Abstract Classpath ClassLoader, basic logic to load class/resource, sub class need to implement\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic abstract class AbstractClasspathClassLoader extends URLClassLoader {\n\n    protected static final String              CLASS_RESOURCE_SUFFIX = \".class\";\n\n    protected ClassLoaderService               classloaderService    = ArkServiceContainerHolder\n                                                                         .getContainer()\n                                                                         .getService(\n                                                                             ClassLoaderService.class);\n\n    protected Cache<String, LoadClassResult>   classCache;\n\n    protected Cache<String, Optional<Package>> packageCache;\n\n    protected Cache<String, Optional<URL>>     urlResourceCache      = newBuilder()\n                                                                         .expireAfterWrite(10,\n                                                                             SECONDS).build();\n    protected boolean                          exploded              = false;\n\n    static {\n        ClassLoader.registerAsParallelCapable();\n    }\n\n    public AbstractClasspathClassLoader(URL[] urls) {\n        super(urls, null);\n        classCache = newBuilder()\n            .initialCapacity(\n                ArkConfigs.getIntValue(Constants.ARK_CLASSLOADER_CACHE_CLASS_SIZE_INITIAL, 2500))\n            .maximumSize(\n                ArkConfigs.getIntValue(Constants.ARK_CLASSLOADER_CACHE_CLASS_SIZE_MAX, 2500))\n            .concurrencyLevel(\n                ArkConfigs.getIntValue(Constants.ARK_CLASSLOADER_CACHE_CONCURRENCY_LEVEL, 16))\n            .expireAfterWrite(30, SECONDS).recordStats().build();\n\n        packageCache = newBuilder()\n            .initialCapacity(\n                ArkConfigs.getIntValue(Constants.ARK_CLASSLOADER_CACHE_CLASS_SIZE_INITIAL, 2000))\n            .maximumSize(\n                ArkConfigs.getIntValue(Constants.ARK_CLASSLOADER_CACHE_CLASS_SIZE_MAX, 2000))\n            .concurrencyLevel(\n                ArkConfigs.getIntValue(Constants.ARK_CLASSLOADER_CACHE_CONCURRENCY_LEVEL, 16))\n            .expireAfterWrite(30, SECONDS).recordStats().build();\n    }\n\n    public AbstractClasspathClassLoader(URL[] urls, boolean exploded) {\n        this(urls);\n        this.exploded = exploded;\n    }\n\n    @Override\n    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {\n        if (StringUtils.isEmpty(name)) {\n            return null;\n        }\n        Handler.setUseFastConnectionExceptions(true);\n        try {\n            if (!exploded) {\n                definePackageIfNecessary(name);\n            }\n            return loadClassWithCache(name, resolve);\n        } finally {\n            Handler.setUseFastConnectionExceptions(false);\n        }\n    }\n\n    /**\n     * Define a package before a {@code findClass} call is made. This is necessary to\n     * ensure that the appropriate manifest for nested JARs is associated with the\n     * package.\n     * @param className the class name being found\n     */\n    private void definePackageIfNecessary(String className) {\n        int lastDot = className.lastIndexOf('.');\n        if (lastDot >= 0) {\n            String packageName = className.substring(0, lastDot);\n            Optional<Package> pkgInCache = packageCache.getIfPresent(packageName);\n            // null means not cached, package haven't been defined yet, try to define it now\n            if (pkgInCache == null) {\n                try {\n                    definePackage(className, packageName);\n                } catch (IllegalArgumentException ex) {\n                    // Tolerate race condition due to being parallel capable\n                } finally {\n                    // cache define result\n                    Package pkgAfterDefined = super.getPackage(packageName);\n                    packageCache.put(packageName, pkgAfterDefined == null ? Optional.empty()\n                        : Optional.of(pkgAfterDefined));\n                }\n            }\n        }\n    }\n\n    private void definePackage(final String className, final String packageName) {\n        try {\n            AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {\n                @Override\n                public Object run() throws Exception {\n                    StringBuilder pen = new StringBuilder(packageName.length() + 10);\n                    StringBuilder cen = new StringBuilder(className.length() + 10);\n                    String packageEntryName = pen.append(packageName.replace('.', '/')).append(\"/\")\n                        .toString();\n                    String classEntryName = cen.append(className.replace('.', '/'))\n                        .append(\".class\").toString();\n                    for (URL url : getURLs()) {\n                        try {\n                            URLConnection connection = url.openConnection();\n                            if (connection instanceof JarURLConnection) {\n                                JarFile jarFile = ((JarURLConnection) connection).getJarFile();\n                                if (jarFile.getEntry(classEntryName) != null\n                                    && jarFile.getEntry(packageEntryName) != null\n                                    && jarFile.getManifest() != null) {\n                                    definePackage(packageName, jarFile.getManifest(), url);\n                                    return null;\n                                }\n                            }\n                        } catch (IOException ex) {\n                            // Ignore\n                        }\n                    }\n                    return null;\n                }\n            }, AccessController.getContext());\n        } catch (java.security.PrivilegedActionException ex) {\n            // Ignore\n        }\n    }\n\n    @Override\n    protected Package getPackage(String name) {\n        Optional<Package> pkgInCache = packageCache.getIfPresent(name);\n        if (pkgInCache != null && pkgInCache.isPresent()) {\n            return pkgInCache.orElse(null);\n        }\n\n        Package pkg = super.getPackage(name);\n        // don't cache null here because we may define pkg successfully later,\n        // only cache null after define fail\n        if (pkg != null) {\n            packageCache.put(name, Optional.of(pkg));\n        }\n        return pkg;\n    }\n\n    /**\n     * cache load results of classes recently loaded\n     * @param name\n     * @param resolve\n     * @return\n     * @throws ArkLoaderException\n     */\n    protected Class<?> loadClassWithCache(String name, boolean resolve) throws ArkLoaderException {\n        try {\n            LoadClassResult resultInCache = classCache.get(name, () -> {\n                LoadClassResult r = new LoadClassResult();\n                try {\n                    r.setClazz(loadClassInternal(name, resolve));\n                } catch (ArkLoaderException ex) {\n                    r.setEx(ex);\n                }\n                return r;\n            });\n\n            if (resultInCache.getEx() != null) {\n                throw resultInCache.getEx();\n            }\n\n            return resultInCache.getClazz();\n        } catch (ExecutionException e) {\n            throw new ArkLoaderException(\n                    String.format(\"[Ark Loader] unexpected exception when load class: %s\", name),\n                    e.getCause());\n        }\n    }\n\n    /**\n     * Real logic to load class，need to implement by Sub ClassLoader\n     * @param name\n     * @param resolve\n     * @return\n     * @throws ArkLoaderException\n     */\n    abstract protected Class<?> loadClassInternal(String name, boolean resolve)\n                                                                               throws ArkLoaderException;\n\n    @Override\n    public URL getResource(String name) {\n        Handler.setUseFastConnectionExceptions(true);\n        Optional<URL> urlOptional = urlResourceCache.getIfPresent(name);\n        try {\n            if (urlOptional != null) {\n                return urlOptional.orElse(null);\n            }\n            URL ret = preFindResource(name);\n            if (ret != null) {\n                return ret;\n            }\n            ret = getResourceInternal(name);\n            URL url = ret != null ? ret : postFindResource(name);\n            urlResourceCache.put(name, url != null ? Optional.of(url) : Optional.empty());\n            return url;\n        } finally {\n            Handler.setUseFastConnectionExceptions(false);\n        }\n    }\n\n    /**\n     * Real logic to get resource\n     * @param name\n     * @return\n     */\n    protected URL getResourceInternal(String name) {\n        // 1. find jdk resource\n        URL url = getJdkResource(name);\n\n        // 2. find export resource\n        if (url == null) {\n            url = getExportResource(name);\n        }\n\n        // 3. get .class resource\n        if (url == null) {\n            url = getClassResource(name);\n        }\n\n        // 4. get local resource\n        if (url == null) {\n            url = getLocalResource(name);\n        }\n\n        return url;\n    }\n\n    @Override\n    public Enumeration<URL> getResources(String name) throws IOException {\n        Handler.setUseFastConnectionExceptions(true);\n        try {\n            if (isDeclaredMode()) {\n                List<Enumeration<URL>> enumerationList = new ArrayList<>();\n                // 1. get resources from ClassLoaderHook.\n                enumerationList.add(preFindResources(name));\n                // 2. get jdk resources, plugin resources declared by the biz and resources in the biz.\n                enumerationList.add(getResourcesInternal(name));\n                // 3. delegate master biz to get resources declared by the biz.\n                enumerationList.add(postFindResources(name));\n                // unique urls\n                return uniqueUrls(enumerationList, name);\n            } else {\n                Enumeration<URL> ret = preFindResources(name);\n                if (ret != null && ret.hasMoreElements()) {\n                    return ret;\n                }\n                ret = getResourcesInternal(name);\n                if (ret != null && ret.hasMoreElements()) {\n                    return ret;\n                }\n                ret = postFindResources(name);\n                return ret != null ? ret : new CompoundEnumeration<URL>(\n                    (Enumeration<URL>[]) new Enumeration<?>[] {});\n            }\n\n        } finally {\n            Handler.setUseFastConnectionExceptions(false);\n        }\n    }\n\n    private Enumeration<URL> uniqueUrls(List<Enumeration<URL>> enumerationList, String resourceName) {\n        // unique urls\n        Set<String> temp = new HashSet<>();\n        List<URL> uniqueUrls = new ArrayList<>();\n\n        for (Enumeration<URL> e : enumerationList) {\n            while (e != null && e.hasMoreElements()) {\n                URL resourceUrl = e.nextElement();\n                String filePath = resourceUrl.getFile().replace(\"file:\", \"\");\n\n                if (filePath.endsWith(resourceName)) {\n                    filePath = filePath.substring(0, filePath.lastIndexOf(resourceName));\n                }\n                String artifactId = JarUtils.parseArtifactId(filePath);\n                if (artifactId == null) {\n                    uniqueUrls.add(resourceUrl);\n                } else {\n                    if (!temp.contains(artifactId)) {\n                        uniqueUrls.add(resourceUrl);\n                        temp.add(artifactId);\n                    }\n                }\n            }\n        }\n        return Collections.enumeration(uniqueUrls);\n    }\n\n    /**\n     * Real logic to get resources\n     * @param name\n     * @return\n     * @throws IOException\n     */\n    protected Enumeration<URL> getResourcesInternal(String name) throws IOException {\n        List<Enumeration<URL>> enumerationList = new ArrayList<>();\n        // 1. find jdk resources\n        enumerationList.add(getJdkResources(name));\n\n        // 2. find exported resources\n        enumerationList.add(getExportResources(name));\n\n        // 3. find local resources\n        enumerationList.add(getLocalResources(name));\n\n        return new CompoundEnumeration<>(\n            enumerationList.toArray((Enumeration<URL>[]) new Enumeration<?>[0]));\n    }\n\n    /**\n     * Whether to find class that exported by other classloader\n     * @param className class name\n     * @return\n     */\n    abstract boolean shouldFindExportedClass(String className);\n\n    /**\n     * Whether to find resource that exported by other classloader\n     * @param resourceName\n     * @return\n     */\n    abstract boolean shouldFindExportedResource(String resourceName);\n\n    private boolean isDeclaredMode() {\n        return this instanceof BizClassLoader && ((BizClassLoader) this).checkDeclaredMode();\n    }\n\n    /**\n     * Load JDK class\n     * @param name class name\n     * @return\n     */\n    protected Class<?> resolveJDKClass(String name) {\n        try {\n            return classloaderService.getJDKClassLoader().loadClass(name);\n        } catch (ClassNotFoundException e) {\n            // ignore\n        }\n        return null;\n    }\n\n    /**\n     * Load export class\n     * @param name\n     * @return\n     */\n    protected Class<?> resolveExportClass(String name) {\n        if (!PluginModel.EXPORTMODE_OVERRIDE.equals(classloaderService.getExportMode(name))) {\n            return doResolveExportClass(name);\n        } else {\n            ClassLoader classLoader = classloaderService.findExportClassLoader(name);\n            URL url = classLoader.getResource(name.replace('.', '/') + \".class\");\n            if (url != null) {\n                String filePath = url.getFile().replaceFirst(\"file:\", \"\");\n                try {\n                    byte[] bytes;\n                    if (filePath.contains(\".jar\")) {\n                        bytes = getClassBytesFromJar(filePath, name.replace('.', '/') + \".class\");\n                    } else {\n                        bytes = FileUtils.readFileToByteArray(new File(filePath));\n                    }\n                    return defineClass(name, bytes, 0, bytes.length);\n                } catch (Exception e) {\n                    ArkLoggerFactory.getDefaultLogger().warn(\n                        String.format(\"can't convert class to reLoad by bizClassLoader: %s\",\n                            e.getMessage()));\n                    throw new RuntimeException(e);\n                }\n            } else {\n                return null;\n            }\n        }\n    }\n\n    private byte[] getClassBytesFromJar(String jarFilePath, String className) throws IOException {\n        try (com.alipay.sofa.ark.loader.jar.JarFile jarFile = JarUtils\n            .getNestedRootJarFromJarLocation(jarFilePath);\n                InputStream inputStream = jarFile.getInputStream(jarFile.getJarEntry(className))) {\n            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();\n            int bufferSize = 4096;\n            byte[] buffer = new byte[bufferSize];\n            int bytesRead;\n\n            while ((bytesRead = inputStream.read(buffer, 0, bufferSize)) != -1) {\n                byteArrayOutputStream.write(buffer, 0, bytesRead);\n            }\n\n            return byteArrayOutputStream.toByteArray();\n        }\n    }\n\n    private Class<?> doResolveExportClass(String name) {\n        if (shouldFindExportedClass(name)) {\n            ClassLoader importClassLoader = null;\n            if (this instanceof BizClassLoader) {\n                importClassLoader = classloaderService.findExportClassLoaderByBiz(\n                    ((BizClassLoader) this).getBizModel(), name);\n            } else if (this instanceof PluginClassLoader) {\n                importClassLoader = classloaderService.findExportClassLoader(name);\n            }\n            if (importClassLoader != null) {\n                try {\n                    Class<?> clazz = importClassLoader.loadClass(name);\n                    if (clazz == null) {\n                        return null;\n                    }\n                    URL url = clazz.getProtectionDomain().getCodeSource().getLocation();\n                    if (this instanceof BizClassLoader\n                        && ((BizClassLoader) this).getBizModel() != null) {\n                        BizModel bizModel = ((BizClassLoader) this).getBizModel();\n\n                        if (url != null && bizModel.isDeclared(url, \"\")) {\n                            return clazz;\n                        }\n                        String classResourceName = name.replace('.', '/') + \".class\";\n                        Enumeration<URL> urls = importClassLoader.getResources(classResourceName);\n                        while (urls.hasMoreElements()) {\n                            URL resourceUrl = urls.nextElement();\n                            if (resourceUrl != null\n                                && bizModel.isDeclared(resourceUrl, classResourceName)) {\n                                ArkLoggerFactory.getDefaultLogger().warn(\n                                    String.format(\n                                        \"find class %s from %s in multiple dependencies.\", name,\n                                        resourceUrl.getFile()));\n                                return clazz;\n                            }\n                        }\n                    } else {\n                        return clazz;\n                    }\n                } catch (ClassNotFoundException | NoClassDefFoundError | IOException e) {\n                    // just log when debug level\n                    if (ArkLoggerFactory.getDefaultLogger().isDebugEnabled()) {\n                        // log debug message\n                        ArkLoggerFactory.getDefaultLogger().debug(\n                            \"Fail to load export class \" + name, e);\n                    }\n                }\n            }\n        }\n        return null;\n    }\n\n    /**\n     * Load ark spi class\n     * @param name\n     * @return\n     */\n    protected Class<?> resolveArkClass(String name) {\n        if (classloaderService.isArkSpiClass(name) || classloaderService.isArkApiClass(name)\n            || classloaderService.isArkLogClass(name)\n            || classloaderService.isArkExceptionClass(name)) {\n            try {\n                return classloaderService.getArkClassLoader().loadClass(name);\n            } catch (ClassNotFoundException e) {\n                // ignore\n            }\n        }\n        return null;\n    }\n\n    /**\n     * Load classpath class\n     * @param name\n     * @return\n     */\n    protected Class<?> resolveLocalClass(String name) {\n        try {\n            return super.loadClass(name, false);\n        } catch (ClassNotFoundException e) {\n            // ignore\n        }\n        return null;\n    }\n\n    /**\n     * Load Java Agent Class\n     * @param name className\n     * @return\n     */\n    protected Class<?> resolveJavaAgentClass(String name) {\n        try {\n            classloaderService.getAgentClassLoader().loadClass(name);\n            return classloaderService.getSystemClassLoader().loadClass(name);\n        } catch (ClassNotFoundException e) {\n            // ignore\n        }\n        return null;\n    }\n\n    /**\n     * Find export resource\n     * @param resourceName\n     * @return\n     */\n    protected URL getExportResource(String resourceName) {\n        if (shouldFindExportedResource(resourceName)) {\n            List<ClassLoader> exportResourceClassLoadersInOrder = null;\n            if (this instanceof BizClassLoader) {\n                exportResourceClassLoadersInOrder = classloaderService\n                    .findExportResourceClassLoadersInOrderByBiz(\n                        ((BizClassLoader) this).getBizModel(), resourceName);\n            } else if (this instanceof PluginClassLoader) {\n                exportResourceClassLoadersInOrder = classloaderService\n                    .findExportResourceClassLoadersInOrder(resourceName);\n            }\n            if (exportResourceClassLoadersInOrder != null) {\n                for (ClassLoader exportResourceClassLoader : exportResourceClassLoadersInOrder) {\n                    URL url = exportResourceClassLoader.getResource(resourceName);\n                    if (url != null && this instanceof BizClassLoader) {\n                        if (((BizClassLoader) (this)).getBizModel().isDeclared(url, resourceName)) {\n                            return url;\n                        } else {\n                            return null;\n                        }\n                    }\n                    return url;\n                }\n            }\n\n        }\n        return null;\n    }\n\n    /**\n     * Find jdk dir resource\n     * @param resourceName\n     * @return\n     */\n    protected URL getJdkResource(String resourceName) {\n        return classloaderService.getJDKClassLoader().getResource(resourceName);\n    }\n\n    /**\n     * Find .class resource\n     * @param resourceName\n     * @return\n     */\n    protected URL getClassResource(String resourceName) {\n        if (resourceName.endsWith(CLASS_RESOURCE_SUFFIX)) {\n            String className = transformClassName(resourceName);\n            if (resolveArkClass(className) != null) {\n                return classloaderService.getArkClassLoader().getResource(resourceName);\n            }\n\n            if (shouldFindExportedClass(className)) {\n                ClassLoader classLoader = classloaderService.findExportClassLoader(className);\n                return classLoader == null ? null : classLoader.getResource(resourceName);\n            }\n        }\n        return null;\n    }\n\n    /**\n     * Find local resource\n     * @param resourceName\n     * @return\n     */\n    protected URL getLocalResource(String resourceName) {\n        return super.getResource(resourceName);\n    }\n\n    private String transformClassName(String name) {\n        if (name.endsWith(CLASS_RESOURCE_SUFFIX)) {\n            name = name.substring(0, name.length() - CLASS_RESOURCE_SUFFIX.length());\n        }\n        return name.replace(\"/\", \".\");\n    }\n\n    /**\n     * Find export resources\n     * @param resourceName\n     * @return\n     */\n    @SuppressWarnings(\"unchecked\")\n    protected Enumeration<URL> getExportResources(String resourceName) throws IOException {\n        if (shouldFindExportedResource(resourceName)) {\n            List<ClassLoader> exportResourceClassLoadersInOrder = null;\n            if (this instanceof BizClassLoader) {\n                exportResourceClassLoadersInOrder = classloaderService\n                    .findExportResourceClassLoadersInOrderByBiz(\n                        ((BizClassLoader) this).getBizModel(), resourceName);\n            } else if (this instanceof PluginClassLoader) {\n                exportResourceClassLoadersInOrder = classloaderService\n                    .findExportResourceClassLoadersInOrder(resourceName);\n            }\n            if (exportResourceClassLoadersInOrder != null) {\n                List<Enumeration<URL>> enumerationList = new ArrayList<>();\n                for (ClassLoader exportResourceClassLoader : exportResourceClassLoadersInOrder) {\n                    if (exportResourceClassLoader instanceof AbstractClasspathClassLoader) {\n                        enumerationList\n                            .add(((AbstractClasspathClassLoader) exportResourceClassLoader)\n                                .getLocalResources(resourceName));\n                    } else {\n                        enumerationList.add(exportResourceClassLoader.getResources(resourceName));\n                    }\n                }\n\n                Enumeration<URL> urls = new CompoundEnumeration<>(\n                    enumerationList.toArray((Enumeration<URL>[]) new Enumeration<?>[0]));\n                if (this instanceof BizClassLoader) {\n                    BizModel bizModel = ((BizClassLoader) this).getBizModel();\n                    List<URL> matchedResourceUrls = new ArrayList<>();\n                    while (urls.hasMoreElements()) {\n                        URL resourceUrl = urls.nextElement();\n\n                        if (resourceUrl != null && bizModel.isDeclared(resourceUrl, resourceName)) {\n                            matchedResourceUrls.add(resourceUrl);\n                        }\n                    }\n                    return Collections.enumeration(matchedResourceUrls);\n                }\n                return urls;\n            }\n        }\n        return Collections.emptyEnumeration();\n    }\n\n    protected Enumeration<URL> getLocalResources(String resourceName) throws IOException {\n        return new UseFastConnectionExceptionsEnumeration(super.getResources(resourceName));\n    }\n\n    protected Enumeration<URL> getJdkResources(String resourceName) throws IOException {\n        return new UseFastConnectionExceptionsEnumeration(classloaderService.getJDKClassLoader()\n            .getResources(resourceName));\n    }\n\n    public void clearCache() {\n        classCache.cleanUp();\n        packageCache.cleanUp();\n        urlResourceCache.cleanUp();\n    }\n\n    public void invalidAllCache() {\n        classCache.invalidateAll();\n        packageCache.invalidateAll();\n        urlResourceCache.invalidateAll();\n    }\n\n    /**\n     * invoked before {@link #loadClass(String, boolean)}\n     *\n     * @param className\n     * @return\n     * @throws ArkLoaderException\n     */\n    protected abstract Class<?> preLoadClass(String className) throws ArkLoaderException;\n\n    /**\n     * invoked after {@link #loadClass(String, boolean)}\n     *\n     * @param className\n     * @return\n     * @throws ArkLoaderException\n     */\n    protected abstract Class<?> postLoadClass(String className) throws ArkLoaderException;\n\n    /**\n     * invoked before {@link #getResource(String)}\n     *\n     * @param resourceName\n     * @return\n     */\n    protected abstract URL preFindResource(String resourceName);\n\n    /**\n     * invoked after {@link #getResource(String)}\n     *\n     * @param resourceName\n     * @return\n     */\n    protected abstract URL postFindResource(String resourceName);\n\n    /**\n     * invoked before {@link #getResources(String)}\n     *\n     * @param resourceName\n     * @return\n     */\n    protected abstract Enumeration<URL> preFindResources(String resourceName) throws IOException;\n\n    /**\n     * invoked after {@link #getResources(String)}\n     *\n     * @param resourceName\n     * @return\n     */\n    protected abstract Enumeration<URL> postFindResources(String resourceName) throws IOException;\n\n    public static class LoadClassResult {\n        private ArkLoaderException ex;\n        private Class              clazz;\n\n        public ArkLoaderException getEx() {\n            return ex;\n        }\n\n        public void setEx(ArkLoaderException ex) {\n            this.ex = ex;\n        }\n\n        public Class getClazz() {\n            return clazz;\n        }\n\n        public void setClazz(Class clazz) {\n            this.clazz = clazz;\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/classloader/BizClassLoader.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.classloader;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.exception.ArkLoaderException;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.classloader.ClassLoaderHook;\nimport com.alipay.sofa.ark.spi.service.extension.ArkServiceLoader;\n\nimport java.io.IOException;\nimport java.net.URL;\nimport java.security.ProtectionDomain;\nimport java.util.Enumeration;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.BIZ_CLASS_LOADER_HOOK;\nimport static com.alipay.sofa.ark.spi.constant.Constants.BIZ_CLASS_LOADER_HOOK_DIR;\n\n/**\n * Ark Biz ClassLoader\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class BizClassLoader extends AbstractClasspathClassLoader {\n\n    private String               bizIdentity;\n    private BizManagerService    bizManagerService = ArkServiceContainerHolder.getContainer()\n                                                       .getService(BizManagerService.class);\n    private ClassLoaderHook<Biz> bizClassLoaderHook;\n    private AtomicBoolean        isHookLoaded      = new AtomicBoolean(false);\n    private AtomicBoolean        skipLoadHook      = new AtomicBoolean(false);\n    private final Object         lock              = new Object();\n\n    private BizModel             bizModel;\n\n    public void setBizModel(BizModel bizModel) {\n        this.bizModel = bizModel;\n    }\n\n    public BizModel getBizModel() {\n        return this.bizModel;\n    }\n\n    static {\n        ClassLoader.registerAsParallelCapable();\n    }\n\n    public BizClassLoader(String bizIdentity, URL[] urls) {\n        super(urls);\n        this.bizIdentity = bizIdentity;\n    }\n\n    public BizClassLoader(String bizIdentity, URL[] urls, boolean exploded) {\n        this(bizIdentity, urls);\n        this.exploded = exploded;\n    }\n\n    // support use biz classloader define app classloader class in org.springframework.cglib.core.ReflectUtils.defineClass\n    public Class<?> publicDefineClass(String name, byte[] b, ProtectionDomain protectionDomain) {\n        return defineClass(name, b, 0, b.length, protectionDomain);\n    }\n\n    @Override\n    protected Class<?> loadClassInternal(String name, boolean resolve) throws ArkLoaderException {\n        Class<?> clazz = null;\n\n        // 0. sun reflect related class throw exception directly\n        if (classloaderService.isSunReflectClass(name)) {\n            throw new ArkLoaderException(\n                String\n                    .format(\n                        \"[ArkBiz Loader] %s : can not load class: %s, this class can only be loaded by sun.reflect.DelegatingClassLoader\",\n                        bizIdentity, name));\n        }\n\n        // 1. findLoadedClass\n        if (clazz == null) {\n            clazz = findLoadedClass(name);\n        }\n\n        // 2. JDK related class\n        if (clazz == null) {\n            clazz = resolveJDKClass(name);\n        }\n\n        // 3. Ark Spi class\n        if (clazz == null) {\n            clazz = resolveArkClass(name);\n        }\n\n        // 4. pre find class\n        if (clazz == null) {\n            clazz = preLoadClass(name);\n        }\n\n        // 5. Plugin Export class\n        if (clazz == null) {\n            clazz = resolveExportClass(name);\n        }\n\n        // 6. Biz classpath class\n        if (clazz == null) {\n            clazz = resolveLocalClass(name);\n        }\n\n        // 7. Java Agent ClassLoader for agent problem\n        if (clazz == null) {\n            clazz = resolveJavaAgentClass(name);\n        }\n\n        // 8. post find class\n        if (clazz == null) {\n            clazz = postLoadClass(name);\n        }\n\n        if (clazz != null) {\n            if (resolve) {\n                super.resolveClass(clazz);\n            }\n            return clazz;\n        }\n\n        throw new ArkLoaderException(String.format(\"[ArkBiz Loader] %s : can not load class: %s\",\n            bizIdentity, name));\n    }\n\n    @Override\n    boolean shouldFindExportedClass(String className) {\n        return !classloaderService.isDeniedImportClass(bizIdentity, className);\n    }\n\n    @Override\n    boolean shouldFindExportedResource(String resourceName) {\n        return !classloaderService.isDeniedImportResource(bizIdentity, resourceName);\n    }\n\n    public boolean checkDeclaredMode() {\n        BizModel biz = this.getBizModel();\n        if (biz == null) {\n            return false;\n        }\n        return biz.isDeclaredMode();\n    }\n\n    private void loadBizClassLoaderHook() {\n        if (!skipLoadHook.get()) {\n            synchronized (lock) {\n                if (isHookLoaded.compareAndSet(false, true)) {\n                    bizClassLoaderHook = ArkServiceLoader.loadExtensionFromArkBiz(\n                        ClassLoaderHook.class, BIZ_CLASS_LOADER_HOOK, bizIdentity);\n                    Biz masterBiz = ArkClient.getMasterBiz();\n                    if (bizClassLoaderHook == null && masterBiz != null\n                        && !masterBiz.getIdentity().equals(bizIdentity)) {\n                        ClassLoader masterClassLoader = masterBiz.getBizClassLoader();\n                        String defaultBizClassloaderHook = System\n                            .getProperty(BIZ_CLASS_LOADER_HOOK_DIR);\n                        if (!StringUtils.isEmpty(defaultBizClassloaderHook)) {\n                            try {\n                                bizClassLoaderHook = (ClassLoaderHook<Biz>) masterClassLoader\n                                    .loadClass(defaultBizClassloaderHook).newInstance();\n                            } catch (Exception e) {\n                                throw new RuntimeException(String.format(\n                                    \"can not find master classloader hook: %s\",\n                                    defaultBizClassloaderHook), e);\n                            }\n                        }\n                    }\n                    skipLoadHook.set(true);\n                }\n            }\n        }\n    }\n\n    @Override\n    protected Class<?> preLoadClass(String className) throws ArkLoaderException {\n        try {\n            loadBizClassLoaderHook();\n            return bizClassLoaderHook == null ? null : bizClassLoaderHook.preFindClass(className,\n                classloaderService, bizManagerService.getBizByIdentity(bizIdentity));\n        } catch (Throwable throwable) {\n            throw new ArkLoaderException(String.format(\n                \"Pre find class %s occurs an error via biz %s ClassLoaderHook: %s.\", className,\n                bizIdentity, bizClassLoaderHook), throwable);\n        }\n    }\n\n    @Override\n    protected Class<?> postLoadClass(String className) throws ArkLoaderException {\n        try {\n            loadBizClassLoaderHook();\n            return bizClassLoaderHook == null ? null : bizClassLoaderHook.postFindClass(className,\n                classloaderService, bizManagerService.getBizByIdentity(bizIdentity));\n        } catch (Throwable throwable) {\n            throw new ArkLoaderException(String.format(\n                \"Post find class %s occurs an error via biz %s ClassLoaderHook: %s.\", className,\n                bizIdentity, bizClassLoaderHook), throwable);\n        }\n    }\n\n    @Override\n    protected URL preFindResource(String resourceName) {\n        loadBizClassLoaderHook();\n        return bizClassLoaderHook == null ? null : bizClassLoaderHook.preFindResource(resourceName,\n            classloaderService, bizManagerService.getBizByIdentity(bizIdentity));\n    }\n\n    @Override\n    protected URL postFindResource(String resourceName) {\n        loadBizClassLoaderHook();\n        return bizClassLoaderHook == null ? null : bizClassLoaderHook.postFindResource(\n            resourceName, classloaderService, bizManagerService.getBizByIdentity(bizIdentity));\n    }\n\n    @Override\n    protected Enumeration<URL> preFindResources(String resourceName) throws IOException {\n        loadBizClassLoaderHook();\n        return bizClassLoaderHook == null ? null : bizClassLoaderHook.preFindResources(\n            resourceName, classloaderService, bizManagerService.getBizByIdentity(bizIdentity));\n    }\n\n    @Override\n    protected Enumeration<URL> postFindResources(String resourceName) throws IOException {\n        loadBizClassLoaderHook();\n        return bizClassLoaderHook == null ? null : bizClassLoaderHook.postFindResources(\n            resourceName, classloaderService, bizManagerService.getBizByIdentity(bizIdentity));\n    }\n\n    /**\n     * Getter method for property <code>bizIdentity</code>.\n     *\n     * @return property value of bizIdentity\n     */\n    public String getBizIdentity() {\n        return bizIdentity;\n    }\n\n    public void setBizIdentity(String bizIdentity) {\n        this.bizIdentity = bizIdentity;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/classloader/ClassLoaderServiceImpl.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.classloader;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.bootstrap.AgentClassLoader;\nimport com.alipay.sofa.ark.common.log.ArkLogger;\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.common.util.AssertUtils;\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.common.util.ClassUtils;\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.container.model.PluginModel;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.classloader.ClassLoaderService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport com.google.inject.Inject;\nimport com.google.inject.Singleton;\n\nimport java.io.File;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.stream.Collectors;\n\n/**\n * ClassLoader Service Implementation\n *\n * @author ruoshan\n * @since 0.1.0\n */\n@Singleton\npublic class ClassLoaderServiceImpl implements ClassLoaderService {\n\n    private static final String                     ARK_SPI_PACKAGES                          = \"com.alipay.sofa.ark.spi\";\n    private static final String                     ARK_API_PACKAGES                          = \"com.alipay.sofa.ark.api\";\n    private static final String                     ARK_LOG_PACKAGES                          = \"com.alipay.sofa.ark.common.log\";\n    private static final String                     ARK_EXCEPTION_PACKAGES                    = \"com.alipay.sofa.ark.exception\";\n\n    private static final List<String>               SUN_REFLECT_GENERATED_ACCESSOR            = new ArrayList<>();\n\n    /* export class and classloader relationship cache */\n    private ConcurrentHashMap<String, Plugin>       exportClassAndClassLoaderMap              = new ConcurrentHashMap<>();\n    private ConcurrentHashMap<String, Plugin>       exportNodeAndClassLoaderMap               = new ConcurrentHashMap<>();\n    private ConcurrentHashMap<String, Plugin>       exportStemAndClassLoaderMap               = new ConcurrentHashMap<>();\n\n    /* export cache and classloader relationship cache */\n    private ConcurrentHashMap<String, List<Plugin>> exportResourceAndClassLoaderMap           = new ConcurrentHashMap<>();\n    private ConcurrentHashMap<String, List<Plugin>> exportPrefixStemResourceAndClassLoaderMap = new ConcurrentHashMap<>();\n    private ConcurrentHashMap<String, List<Plugin>> exportSuffixStemResourceAndClassLoaderMap = new ConcurrentHashMap<>();\n\n    private ClassLoader                             jdkClassLoader;\n    private ClassLoader                             arkClassLoader;\n    private ClassLoader                             systemClassLoader;\n    private ClassLoader                             agentClassLoader;\n\n    @Inject\n    private PluginManagerService                    pluginManagerService;\n\n    @Inject\n    private BizManagerService                       bizManagerService;\n\n    static {\n        SUN_REFLECT_GENERATED_ACCESSOR.add(\"sun.reflect.GeneratedMethodAccessor\");\n        SUN_REFLECT_GENERATED_ACCESSOR.add(\"sun.reflect.GeneratedConstructorAccessor\");\n        SUN_REFLECT_GENERATED_ACCESSOR.add(\"sun.reflect.GeneratedSerializationConstructorAccessor\");\n    }\n\n    @Override\n    public boolean isSunReflectClass(String className) {\n        for (String sunAccessor : SUN_REFLECT_GENERATED_ACCESSOR) {\n            if (className.startsWith(sunAccessor)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public boolean isArkSpiClass(String className) {\n        return className.startsWith(ARK_SPI_PACKAGES);\n    }\n\n    @Override\n    public boolean isArkApiClass(String className) {\n        return className.startsWith(ARK_API_PACKAGES);\n    }\n\n    @Override\n    public boolean isArkLogClass(String className) {\n        return className.startsWith(ARK_LOG_PACKAGES);\n    }\n\n    @Override\n    public boolean isArkExceptionClass(String className) {\n        return className.startsWith(ARK_EXCEPTION_PACKAGES);\n    }\n\n    @Override\n    public void prepareExportClassAndResourceCache() {\n        for (Plugin plugin : pluginManagerService.getPluginsInOrder()) {\n            for (String exportIndex : plugin.getExportPackageNodes()) {\n                exportNodeAndClassLoaderMap.putIfAbsent(exportIndex, plugin);\n            }\n            for (String exportIndex : plugin.getExportPackageStems()) {\n                exportStemAndClassLoaderMap.putIfAbsent(exportIndex, plugin);\n            }\n            for (String exportIndex : plugin.getExportClasses()) {\n                exportClassAndClassLoaderMap.putIfAbsent(exportIndex, plugin);\n            }\n            for (String resource : plugin.getExportResources()) {\n                exportResourceAndClassLoaderMap.putIfAbsent(resource, new LinkedList<>());\n                exportResourceAndClassLoaderMap.get(resource).add(plugin);\n            }\n            for (String resource : plugin.getExportPrefixResourceStems()) {\n                exportPrefixStemResourceAndClassLoaderMap.putIfAbsent(resource, new LinkedList<>());\n                exportPrefixStemResourceAndClassLoaderMap.get(resource).add(plugin);\n            }\n            for (String resource : plugin.getExportSuffixResourceStems()) {\n                exportSuffixStemResourceAndClassLoaderMap.putIfAbsent(resource, new LinkedList<>());\n                exportSuffixStemResourceAndClassLoaderMap.get(resource).add(plugin);\n            }\n        }\n    }\n\n    @Override\n    public boolean isClassInImport(String pluginName, String className) {\n        Plugin plugin = pluginManagerService.getPluginByName(pluginName);\n        AssertUtils.assertNotNull(plugin, \"plugin: \" + pluginName + \" is null\");\n\n        for (String importName : plugin.getImportClasses()) {\n            if (className.equals(importName)) {\n                return true;\n            }\n        }\n\n        String pkg = ClassUtils.getPackageName(className);\n        for (String pattern : plugin.getImportPackageNodes()) {\n            if (pkg.equals(pattern)) {\n                return true;\n            }\n        }\n\n        for (String pattern : plugin.getImportPackageStems()) {\n            if (pkg.startsWith(pattern)) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    public String getExportMode(String className) {\n        Plugin plugin = findExportPlugin(className);\n        if (plugin == null) {\n            return PluginModel.EXPORTMODE_UNKNOWN;\n        }\n\n        return plugin.getExportMode();\n    }\n\n    @Override\n    public ClassLoader findExportClassLoader(String className) {\n        Plugin plugin = findExportPlugin(className);\n        if (plugin != null) {\n            return plugin.getPluginClassLoader();\n        } else {\n            return null;\n        }\n    }\n\n    @Override\n    public ClassLoader findExportClassLoaderByBiz(Biz biz, String className) {\n        BizModel bizModel = (BizModel) biz;\n        Plugin plugin = bizModel.getExportClassAndClassLoaderMap().get(className);\n        String packageName = ClassUtils.getPackageName(className);\n        if (plugin == null) {\n            plugin = bizModel.getExportNodeAndClassLoaderMap().get(packageName);\n        }\n        while (!Constants.DEFAULT_PACKAGE.equals(packageName) && plugin == null) {\n            plugin = bizModel.getExportStemAndClassLoaderMap().get(packageName);\n            packageName = ClassUtils.getPackageName(packageName);\n        }\n        if (plugin != null) {\n            return plugin.getPluginClassLoader();\n        } else {\n            return null;\n        }\n    }\n\n    @Override\n    public Plugin findExportPlugin(String className) {\n        Plugin plugin = exportClassAndClassLoaderMap.get(className);\n        String packageName = ClassUtils.getPackageName(className);\n        if (plugin == null) {\n            plugin = exportNodeAndClassLoaderMap.get(packageName);\n        }\n        while (!Constants.DEFAULT_PACKAGE.equals(packageName) && plugin == null) {\n            plugin = exportStemAndClassLoaderMap.get(packageName);\n            packageName = ClassUtils.getPackageName(packageName);\n        }\n        return plugin;\n    }\n\n    @Override\n    public boolean isResourceInImport(String pluginName, String resourceName) {\n        Plugin plugin = pluginManagerService.getPluginByName(pluginName);\n        AssertUtils.assertNotNull(plugin, \"plugin: \" + pluginName + \" is null\");\n\n        for (String importResource : plugin.getImportResources()) {\n            if (importResource.equals(resourceName)) {\n                return true;\n            }\n        }\n\n        for (String importResource : plugin.getImportPrefixResourceStems()) {\n            if (resourceName.startsWith(importResource)) {\n                return true;\n            }\n        }\n\n        for (String importResource : plugin.getImportSuffixResourceStems()) {\n            if (resourceName.endsWith(importResource)) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    @Override\n    public List<ClassLoader> findExportResourceClassLoadersInOrder(String resourceName) {\n        List<Plugin> plugins = findExportResourcePluginsInOrder(resourceName);\n\n        if (plugins != null) {\n            return plugins.stream().map(Plugin::getPluginClassLoader).collect(Collectors.toList());\n        } else {\n            return null;\n        }\n    }\n\n    @Override\n    public List<ClassLoader> findExportResourceClassLoadersInOrderByBiz(Biz biz, String resourceName) {\n        BizModel bizModel = (BizModel) biz;\n        List<Plugin> plugins = findExportResourcePluginsInOrderByBiz(bizModel, resourceName);\n\n        if (plugins != null) {\n            return plugins.stream().map(Plugin::getPluginClassLoader).collect(Collectors.toList());\n        } else {\n            return null;\n        }\n    }\n\n    private List<Plugin> findExportResourcePluginsInOrderByBiz(BizModel bizModel,\n                                                               String resourceName) {\n        if (bizModel.getExportResourceAndClassLoaderMap().containsKey(resourceName)) {\n            return bizModel.getExportResourceAndClassLoaderMap().get(resourceName);\n        }\n\n        for (String stemResource : bizModel.getExportPrefixStemResourceAndClassLoaderMap().keySet()) {\n            if (resourceName.startsWith(stemResource)) {\n                return bizModel.getExportPrefixStemResourceAndClassLoaderMap().get(stemResource);\n            }\n        }\n\n        for (String stemResource : bizModel.getExportSuffixStemResourceAndClassLoaderMap().keySet()) {\n            if (resourceName.endsWith(stemResource)) {\n                return bizModel.getExportSuffixStemResourceAndClassLoaderMap().get(stemResource);\n            }\n        }\n        return null;\n    }\n\n    private List<Plugin> findExportResourcePluginsInOrder(String resourceName) {\n        if (exportResourceAndClassLoaderMap.containsKey(resourceName)) {\n            return exportResourceAndClassLoaderMap.get(resourceName);\n        }\n\n        for (String stemResource : exportPrefixStemResourceAndClassLoaderMap.keySet()) {\n            if (resourceName.startsWith(stemResource)) {\n                return exportPrefixStemResourceAndClassLoaderMap.get(stemResource);\n            }\n        }\n\n        for (String stemResource : exportSuffixStemResourceAndClassLoaderMap.keySet()) {\n            if (resourceName.endsWith(stemResource)) {\n                return exportSuffixStemResourceAndClassLoaderMap.get(stemResource);\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public ClassLoader getJDKClassLoader() {\n        return jdkClassLoader;\n    }\n\n    @Override\n    public ClassLoader getArkClassLoader() {\n        return arkClassLoader;\n    }\n\n    @Override\n    public ClassLoader getSystemClassLoader() {\n        return systemClassLoader;\n    }\n\n    @Override\n    public ClassLoader getAgentClassLoader() {\n        return agentClassLoader;\n    }\n\n    @Override\n    public ClassLoader getBizClassLoader(String bizIdentity) {\n        Biz biz = bizManagerService.getBizByIdentity(bizIdentity);\n        return biz == null ? null : biz.getBizClassLoader();\n    }\n\n    @Override\n    public ClassLoader getMasterBizClassLoader() {\n        Biz biz = ArkClient.getMasterBiz();\n        return biz == null ? null : biz.getBizClassLoader();\n    }\n\n    @Override\n    public ClassLoader getPluginClassLoader(String pluginName) {\n        Plugin plugin = pluginManagerService.getPluginByName(pluginName);\n        return plugin == null ? null : plugin.getPluginClassLoader();\n    }\n\n    @Override\n    public void init() throws ArkRuntimeException {\n        arkClassLoader = this.getClass().getClassLoader();\n        systemClassLoader = ClassLoader.getSystemClassLoader();\n        agentClassLoader = createAgentClassLoader();\n\n        ClassLoader extClassLoader = systemClassLoader;\n        while (extClassLoader.getParent() != null) {\n            extClassLoader = extClassLoader.getParent();\n        }\n        List<URL> jdkUrls = new ArrayList<>();\n        try {\n            String javaHome = System.getProperty(\"java.home\").replace(File.separator + \"jre\", \"\");\n            URL[] urls = ClassLoaderUtils.getURLs(systemClassLoader);\n            for (URL url : urls) {\n                if (url.getPath().startsWith(javaHome)) {\n                    if (ArkLoggerFactory.getDefaultLogger().isDebugEnabled()) {\n                        ArkLoggerFactory.getDefaultLogger().debug(\n                            String.format(\"Find JDK Url: %s\", url));\n                    }\n                    jdkUrls.add(url);\n                }\n            }\n        } catch (Throwable e) {\n            ArkLoggerFactory.getDefaultLogger().warn(\"Meet exception when parse JDK urls\", e);\n        }\n\n        jdkClassLoader = new JDKDelegateClassLoader(jdkUrls.toArray(new URL[0]), extClassLoader);\n    }\n\n    @Override\n    public void dispose() throws ArkRuntimeException {\n\n    }\n\n    private ClassLoader createAgentClassLoader() throws ArkRuntimeException {\n        return new AgentClassLoader(ClassLoaderUtils.getAgentClassPath(), null);\n    }\n\n    @Override\n    public boolean isDeniedImportClass(String bizIdentity, String className) {\n        Biz biz = bizManagerService.getBizByIdentity(bizIdentity);\n        if (biz == null) {\n            return false;\n        }\n\n        for (String pattern : biz.getDenyImportClasses()) {\n            if (pattern.equals(className)) {\n                return true;\n            }\n        }\n\n        String pkg = ClassUtils.getPackageName(className);\n        for (String pattern : biz.getDenyImportPackageNodes()) {\n            if (pkg.equals(pattern)) {\n                return true;\n            }\n        }\n\n        for (String pattern : biz.getDenyImportPackageStems()) {\n            if (pkg.startsWith(pattern)) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    @Override\n    public boolean isDeniedImportResource(String bizIdentity, String resourceName) {\n        Biz biz = bizManagerService.getBizByIdentity(bizIdentity);\n        if (biz == null) {\n            return false;\n        }\n\n        for (String resource : biz.getDenyImportResources()) {\n            if (resource.equals(resourceName)) {\n                return true;\n            }\n        }\n\n        for (String resource : biz.getDenyPrefixImportResourceStems()) {\n            if (resourceName.startsWith(resource)) {\n                return true;\n            }\n        }\n\n        for (String resource : biz.getDenySuffixImportResourceStems()) {\n            if (resourceName.endsWith(resource)) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    @Override\n    public int getPriority() {\n        return DEFAULT_PRECEDENCE;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/classloader/CompoundEnumeration.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.classloader;\n\nimport java.util.Enumeration;\nimport java.util.NoSuchElementException;\n\n/*\n * A utility class that will enumerate over an array of enumerations.\n */\npublic final class CompoundEnumeration<E> implements Enumeration<E> {\n    private final Enumeration<E>[] enums;\n    private int                    index;\n\n    public CompoundEnumeration(Enumeration<E>[] enums) {\n        this.enums = enums;\n    }\n\n    private boolean next() {\n        while (index < enums.length) {\n            if (enums[index] != null && enums[index].hasMoreElements()) {\n                return true;\n            }\n            index++;\n        }\n        return false;\n    }\n\n    public boolean hasMoreElements() {\n        return next();\n    }\n\n    public E nextElement() {\n        if (!next()) {\n            throw new NoSuchElementException();\n        }\n        return enums[index].nextElement();\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/classloader/JDKDelegateClassLoader.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.classloader;\n\nimport java.net.URL;\nimport java.net.URLClassLoader;\n\n/**\n * JDK Delegate ClassLoader, parent is excClassLoader, urls are jdk related path on SystemClassLoader\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class JDKDelegateClassLoader extends URLClassLoader {\n    static {\n        ClassLoader.registerAsParallelCapable();\n    }\n\n    public JDKDelegateClassLoader(URL[] urls, ClassLoader parent) {\n        super(urls, parent);\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/classloader/PluginClassLoader.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.classloader;\n\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.exception.ArkLoaderException;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.service.classloader.ClassLoaderHook;\nimport com.alipay.sofa.ark.spi.service.extension.ArkServiceLoader;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\n\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.Enumeration;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport static com.alipay.sofa.ark.spi.constant.Constants.PLUGIN_CLASS_LOADER_HOOK;\n\n/**\n * Ark Plugin ClassLoader\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class PluginClassLoader extends AbstractClasspathClassLoader {\n\n    private String                  pluginName;\n    private ClassLoaderHook<Plugin> pluginClassLoaderHook;\n    private AtomicBoolean           isHookLoaded         = new AtomicBoolean(false);\n    private AtomicBoolean           skipLoadHook         = new AtomicBoolean(false);\n    private PluginManagerService    pluginManagerService = ArkServiceContainerHolder\n                                                             .getContainer()\n                                                             .getService(PluginManagerService.class);\n    private final Object            lock                 = new Object();\n\n    static {\n        ClassLoader.registerAsParallelCapable();\n    }\n\n    public PluginClassLoader(String pluginName, URL[] urls) {\n        super(urls);\n        this.pluginName = pluginName;\n    }\n\n    public String getPluginName() {\n        return pluginName;\n    }\n\n    @Override\n    protected Class<?> loadClassInternal(String name, boolean resolve) throws ArkLoaderException {\n        Class<?> clazz = null;\n\n        // 0. sun reflect related class throw exception directly\n        if (classloaderService.isSunReflectClass(name)) {\n            throw new ArkLoaderException(\n                String\n                    .format(\n                        \"[ArkPlugin Loader] %s : can not load class: %s, this class can only be loaded by sun.reflect.DelegatingClassLoader\",\n                        pluginName, name));\n        }\n\n        // 1. findLoadedClass\n        if (clazz == null) {\n            clazz = findLoadedClass(name);\n        }\n\n        // 2. JDK related class\n        if (clazz == null) {\n            clazz = resolveJDKClass(name);\n        }\n\n        // 3. Ark Spi class\n        if (clazz == null) {\n            clazz = resolveArkClass(name);\n        }\n\n        // 4. pre find class\n        if (clazz == null) {\n            clazz = preLoadClass(name);\n        }\n\n        // 5. Import class export by other plugins\n        if (clazz == null) {\n            clazz = resolveExportClass(name);\n        }\n\n        // 6. Plugin classpath class\n        if (clazz == null) {\n            clazz = resolveLocalClass(name);\n        }\n\n        // 7. Java Agent ClassLoader for agent problem\n        if (clazz == null) {\n            clazz = resolveJavaAgentClass(name);\n        }\n\n        // 8. Post find class\n        if (clazz == null) {\n            clazz = postLoadClass(name);\n        }\n\n        if (clazz != null) {\n            if (resolve) {\n                super.resolveClass(clazz);\n            }\n            return clazz;\n        }\n\n        throw new ArkLoaderException(String.format(\n            \"[ArkPlugin Loader] %s : can not load class: %s\", pluginName, name));\n    }\n\n    @Override\n    boolean shouldFindExportedClass(String className) {\n        return classloaderService.isClassInImport(pluginName, className);\n    }\n\n    @Override\n    boolean shouldFindExportedResource(String resourceName) {\n        return classloaderService.isResourceInImport(pluginName, resourceName);\n    }\n\n    private void loadPluginClassLoaderHook() {\n        if (!skipLoadHook.get()) {\n            synchronized (lock) {\n                if (isHookLoaded.compareAndSet(false, true)) {\n                    pluginClassLoaderHook = ArkServiceLoader.loadExtensionFromArkPlugin(\n                        ClassLoaderHook.class, PLUGIN_CLASS_LOADER_HOOK, pluginName);\n                    skipLoadHook.set(true);\n                }\n            }\n        }\n    }\n\n    @Override\n    protected Class<?> preLoadClass(String className) throws ArkLoaderException {\n        try {\n            loadPluginClassLoaderHook();\n            return pluginClassLoaderHook == null ? null : pluginClassLoaderHook.preFindClass(\n                className, classloaderService, pluginManagerService.getPluginByName(pluginName));\n        } catch (Throwable throwable) {\n            throw new ArkLoaderException(String.format(\n                \"Pre find class %s occurs an error via plugin ClassLoaderHook: %s.\", className,\n                pluginClassLoaderHook), throwable);\n        }\n    }\n\n    @Override\n    protected Class<?> postLoadClass(String className) throws ArkLoaderException {\n        try {\n            loadPluginClassLoaderHook();\n            return pluginClassLoaderHook == null ? null : pluginClassLoaderHook.postFindClass(\n                className, classloaderService, pluginManagerService.getPluginByName(pluginName));\n        } catch (Throwable throwable) {\n            throw new ArkLoaderException(String.format(\n                \"Post find class %s occurs an error via plugin ClassLoaderHook: %s.\", className,\n                pluginClassLoaderHook), throwable);\n        }\n    }\n\n    @Override\n    protected URL preFindResource(String resourceName) {\n        loadPluginClassLoaderHook();\n        return pluginClassLoaderHook == null ? null : pluginClassLoaderHook.preFindResource(\n            resourceName, classloaderService, pluginManagerService.getPluginByName(pluginName));\n    }\n\n    @Override\n    protected URL postFindResource(String resourceName) {\n        loadPluginClassLoaderHook();\n        return pluginClassLoaderHook == null ? null : pluginClassLoaderHook.postFindResource(\n            resourceName, classloaderService, pluginManagerService.getPluginByName(pluginName));\n    }\n\n    @Override\n    protected Enumeration<URL> preFindResources(String resourceName) throws IOException {\n        loadPluginClassLoaderHook();\n        return pluginClassLoaderHook == null ? null : pluginClassLoaderHook.preFindResources(\n            resourceName, classloaderService, pluginManagerService.getPluginByName(pluginName));\n    }\n\n    @Override\n    protected Enumeration<URL> postFindResources(String resourceName) throws IOException {\n        loadPluginClassLoaderHook();\n        return pluginClassLoaderHook == null ? null : pluginClassLoaderHook.postFindResources(\n            resourceName, classloaderService, pluginManagerService.getPluginByName(pluginName));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/event/EventAdminServiceImpl.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.event;\n\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.common.util.OrderComparator;\nimport com.alipay.sofa.ark.spi.event.ArkEvent;\nimport com.alipay.sofa.ark.spi.registry.ServiceReference;\nimport com.alipay.sofa.ark.spi.service.PriorityOrdered;\nimport com.alipay.sofa.ark.spi.service.event.EventAdminService;\nimport com.alipay.sofa.ark.spi.service.event.EventHandler;\nimport com.alipay.sofa.ark.spi.service.registry.RegistryService;\nimport com.google.inject.Inject;\nimport com.google.inject.Singleton;\n\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.concurrent.CopyOnWriteArraySet;\n\n/**\n * @author qilong.zql\n * @since 0.4.0\n */\n@Singleton\npublic class EventAdminServiceImpl implements EventAdminService, EventHandler {\n\n    private final static ConcurrentMap<ClassLoader, CopyOnWriteArraySet<EventHandler>> SUBSCRIBER_MAP = new ConcurrentHashMap<>();\n\n    @Inject\n    private RegistryService                                                            registryService;\n\n    public EventAdminServiceImpl() {\n        register(this);\n    }\n\n    @Override\n    public void sendEvent(ArkEvent event) {\n        List<EventHandler> eventHandlers = new ArrayList<>();\n        for (CopyOnWriteArraySet<EventHandler> values : SUBSCRIBER_MAP.values()) {\n            eventHandlers.addAll(values);\n        }\n        for (ServiceReference<EventHandler> eventHandler : registryService.referenceServices(\n            EventHandler.class, null)) {\n            eventHandlers.add(eventHandler.getService());\n        }\n        Collections.sort(eventHandlers, new OrderComparator());\n        for (EventHandler eventHandler : eventHandlers) {\n            if (isSupportEventType(eventHandler, event)) {\n                eventHandler.handleEvent(event);\n            }\n        }\n    }\n\n    @Override\n    public void register(EventHandler eventHandler) {\n        CopyOnWriteArraySet<EventHandler> set = SUBSCRIBER_MAP.get(eventHandler.getClass()\n            .getClassLoader());\n        if (set == null) {\n            set = new CopyOnWriteArraySet<>();\n            CopyOnWriteArraySet<EventHandler> old = SUBSCRIBER_MAP.putIfAbsent(eventHandler\n                .getClass().getClassLoader(), set);\n            if (old != null) {\n                set = old;\n            }\n        }\n        set.add(eventHandler);\n        ArkLoggerFactory.getDefaultLogger().debug(\n            String.format(\"Register event handler: %s.\", eventHandler));\n    }\n\n    @Override\n    public void unRegister(EventHandler eventHandler) {\n        CopyOnWriteArraySet<EventHandler> set = SUBSCRIBER_MAP.get(eventHandler.getClass()\n            .getClassLoader());\n        if (set != null) {\n            set.remove(eventHandler);\n            ArkLoggerFactory.getDefaultLogger().debug(\n                String.format(\"Unregister event handler: %s.\", eventHandler));\n        }\n    }\n\n    @Override\n    public void unRegister(ClassLoader classLoader) {\n        SUBSCRIBER_MAP.remove(classLoader);\n        ArkLoggerFactory.getDefaultLogger().debug(\n            String.format(\"Unregister event handler of classLoader: %s.\", classLoader));\n\n    }\n\n    @Override\n    public void handleEvent(ArkEvent event) {\n    }\n\n    @Override\n    public int getPriority() {\n        return PriorityOrdered.LOWEST_PRECEDENCE;\n    }\n\n    private boolean isSupportEventType(EventHandler eventHandler, ArkEvent event) {\n        boolean isSupport = false;\n        try {\n            Class<? extends EventHandler> aClass = eventHandler.getClass();\n            // get current class's interface type\n            Type[] types = aClass.getGenericInterfaces();\n            if (types != null) {\n                // traverse types\n                for (Type type : types) {\n                    if (!checkEventHandlerType(type)) {\n                        continue;\n                    }\n                    if (type instanceof ParameterizedType) {\n                        // a generic type is specified, the current type and its subclasses will be processed\n                        Type[] actualTypeArguments = ((ParameterizedType) type)\n                            .getActualTypeArguments();\n                        if (actualTypeArguments.length == 1) {\n                            if (Class.forName(actualTypeArguments[0].getTypeName())\n                                .isAssignableFrom(event.getClass())) {\n                                isSupport = true;\n                                break;\n                            }\n                        }\n                    } else {\n                        // no generic type is specified, ArkEvent and its subclasses will handle\n                        if (ArkEvent.class.isAssignableFrom(event.getClass())) {\n                            isSupport = true;\n                            break;\n                        }\n                    }\n                }\n            }\n        } catch (Throwable t) {\n            // ignore\n        }\n        return isSupport;\n    }\n\n    private boolean checkEventHandlerType(Type type) {\n        if (type.getTypeName().equals(EventHandler.class.getTypeName())) {\n            return true;\n        }\n        if (type instanceof ParameterizedType) {\n            ParameterizedType parameterizedType = (ParameterizedType) type;\n            return parameterizedType.getRawType().getTypeName()\n                .equals(EventHandler.class.getTypeName());\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/extension/ExtensionLoaderServiceImpl.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.extension;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.common.util.AssertUtils;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.extension.Extensible;\nimport com.alipay.sofa.ark.spi.service.extension.Extension;\nimport com.alipay.sofa.ark.spi.service.extension.ExtensionClass;\nimport com.alipay.sofa.ark.spi.service.extension.ExtensionLoaderService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\n\nimport javax.inject.Singleton;\nimport java.io.BufferedReader;\nimport java.io.InputStreamReader;\nimport java.net.URL;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.EXTENSION_FILE_DIR;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@Singleton\npublic class ExtensionLoaderServiceImpl implements ExtensionLoaderService {\n    private PluginManagerService pluginManagerService = ArkServiceContainerHolder.getContainer()\n                                                          .getService(PluginManagerService.class);\n\n    private BizManagerService    bizManagerService    = ArkServiceContainerHolder.getContainer()\n                                                          .getService(BizManagerService.class);\n\n    @Override\n    public <T> T getExtensionContributorFromArkPlugin(Class<T> interfaceType, String extensionName,\n                                                      String pluginName) {\n        AssertUtils.assertNotNull(interfaceType, \"interfaceType can't be null.\");\n        AssertUtils.assertNotNull(extensionName, \"extensionName can't be null.\");\n        AssertUtils.assertNotNull(pluginName, \"pluginName can't be null.\");\n        Plugin plugin = pluginManagerService.getPluginByName(pluginName);\n        AssertUtils.assertNotNull(plugin, \"plugin: \" + pluginName + \" is null\");\n        return getExtensionContributor(interfaceType, extensionName, plugin,\n            plugin.getPluginClassLoader());\n    }\n\n    @Override\n    public <T> T getExtensionContributorFromArkBiz(Class<T> interfaceType, String extensionName,\n                                                   String bizIdentity) {\n        AssertUtils.assertNotNull(interfaceType, \"interfaceType can't be null.\");\n        AssertUtils.assertNotNull(extensionName, \"extensionName can't be null.\");\n        AssertUtils.assertNotNull(bizIdentity, \"bizIdentity can't be null.\");\n        Biz biz = bizManagerService.getBizByIdentity(bizIdentity);\n        AssertUtils.assertNotNull(biz, \"biz: \" + bizIdentity + \" is null\");\n        return getExtensionContributor(interfaceType, extensionName, biz, biz.getBizClassLoader());\n    }\n\n    @Override\n    public <T> List<T> getExtensionContributorsFromArkBiz(Class<T> interfaceType, String bizIdentity) {\n        AssertUtils.assertNotNull(interfaceType, \"interfaceType can't be null.\");\n        AssertUtils.assertNotNull(bizIdentity, \"bizIdentity can't be null.\");\n        Biz biz = bizManagerService.getBizByIdentity(bizIdentity);\n        AssertUtils.assertNotNull(biz, \"biz: \" + bizIdentity + \" is null\");\n        return getExtensionContributors(interfaceType, biz, biz.getBizClassLoader());\n    }\n\n    public <T, L> T getExtensionContributor(Class<T> interfaceType, String extensionName,\n                                            L location, ClassLoader resourceLoader) {\n        ExtensionClass<T, L> extensionClass = null;\n        try {\n            Set<ExtensionClass<T, L>> extensionClassSet = loadExtension(interfaceType,\n                extensionName, location, resourceLoader);\n            for (ExtensionClass extensionClazz : extensionClassSet) {\n                if (extensionClass == null\n                    || extensionClass.getPriority() > extensionClazz.getPriority()) {\n                    extensionClass = extensionClazz;\n                }\n            }\n        } catch (Throwable throwable) {\n            ArkLoggerFactory.getDefaultLogger()\n                .error(\"Loading extension of interfaceType: {} occurs error {}.\", interfaceType,\n                    throwable);\n            throw new ArkRuntimeException(throwable);\n        }\n        return extensionClass == null ? null : extensionClass.getObject();\n    }\n\n    public <T, L> List<T> getExtensionContributors(Class<T> interfaceType,\n                                                   L location, ClassLoader resourceLoader) {\n        try {\n            Set<ExtensionClass<T, L>> extensionClassSet = loadExtensions(interfaceType, location, resourceLoader);\n            Map<String,ExtensionClass> nameToExtensionClass =  new HashMap<>();\n            for (ExtensionClass extensionClazz : extensionClassSet) {\n                if(extensionClazz!=null){\n                    String extensionName = extensionClazz.getExtension().value();\n                    nameToExtensionClass.putIfAbsent(extensionName,extensionClazz);\n                    if(extensionClazz.getPriority() > nameToExtensionClass.get(extensionName).getPriority()){\n                        nameToExtensionClass.put(extensionName,extensionClazz);\n                    }\n                }\n            }\n            return  (List<T>)nameToExtensionClass.values().stream().map(it -> it.getObject()).collect(Collectors.toList());\n        } catch (Throwable throwable) {\n            ArkLoggerFactory.getDefaultLogger()\n                    .error(\"Loading extension of interfaceType: {} occurs error {}.\", interfaceType,\n                            throwable);\n            throw new ArkRuntimeException(throwable);\n        }\n\n    }\n\n    private <I, L> Set<ExtensionClass<I, L>> loadExtension(Class<I> interfaceType,\n                                                           String extensionName, L location,\n                                                           ClassLoader resourceLoader)\n                                                                                      throws Throwable {\n        Set<ExtensionClass<I, L>> extensionClassSet = loadExtensions(interfaceType,location,resourceLoader);\n        return extensionClassSet.stream().filter(it -> extensionName.equals(it.getExtension().value())).collect(Collectors.toSet());\n    }\n\n    private <I, L> Set<ExtensionClass<I, L>> loadExtensions(Class<I> interfaceType, L location,\n                                                            ClassLoader resourceLoader)\n                                                                                       throws Throwable {\n        BufferedReader reader = null;\n        try {\n            Set<ExtensionClass<I, L>> extensionClassSet = new HashSet<>();\n            Extensible extensible = interfaceType.getAnnotation(Extensible.class);\n            if (extensible == null) {\n                throw new ArkRuntimeException(String.format(\n                    \"Extensible class %s is not annotated by %s.\", interfaceType, Extensible.class));\n            }\n            String fileName = interfaceType.getCanonicalName();\n            if (!StringUtils.isEmpty(extensible.file())) {\n                fileName = extensible.file();\n            }\n            Enumeration<URL> enumeration = resourceLoader.getResources(EXTENSION_FILE_DIR\n                                                                       + fileName);\n            while (enumeration.hasMoreElements()) {\n                URL url = enumeration.nextElement();\n                if (ArkLoggerFactory.getDefaultLogger().isDebugEnabled()) {\n                    ArkLoggerFactory.getDefaultLogger().debug(\n                        \"Loading extension of extensible: {} from location: {} and file: {}\",\n                        interfaceType, location, url);\n                }\n                reader = new BufferedReader(new InputStreamReader(url.openStream(), \"UTF-8\"));\n                String line;\n                while ((line = reader.readLine()) != null) {\n                    ExtensionClass<I, L> extensionClass = new ExtensionClass<>();\n                    extensionClass.setDefinedLocation(location);\n                    extensionClass.setExtensible(extensible);\n                    extensionClass.setInterfaceClass(interfaceType);\n                    Class<?> implementClass = null;\n                    String clazzName = line.trim();\n                    try {\n                        implementClass = resourceLoader.loadClass(clazzName);\n                    } catch (Exception e) {\n                        if (ArkConfigs.isEmbedEnable()\n                            && resourceLoader != ArkClient.getMasterBiz().getBizClassLoader()) {\n                            implementClass = ArkClient.getMasterBiz().getBizClassLoader()\n                                .loadClass(clazzName);\n                        } else {\n                            throw e;\n                        }\n                    }\n                    if (!interfaceType.isAssignableFrom(implementClass)) {\n                        throw new ArkRuntimeException(String.format(\n                            \"Extension implementation class %s is not type of %s.\",\n                            implementClass.getCanonicalName(), interfaceType.getCanonicalName()));\n                    }\n                    Extension extension = implementClass.getAnnotation(Extension.class);\n                    if (extension == null) {\n                        throw new ArkRuntimeException(String.format(\n                            \"Extension implementation class %s is not annotated by %s.\",\n                            implementClass, Extension.class));\n                    }\n\n                    extensionClass.setExtension(extension);\n                    extensionClass.setImplementClass((Class<I>) implementClass);\n                    extensionClassSet.add(extensionClass);\n                }\n            }\n            return extensionClassSet;\n        } catch (Throwable throwable) {\n            ArkLoggerFactory.getDefaultLogger().error(\n                \"Loading extension files from {} occurs an error {}.\", location, throwable);\n            throw throwable;\n        } finally {\n            if (reader != null) {\n                reader.close();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/injection/InjectionServiceImpl.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.injection;\n\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.common.util.ReflectionUtils;\nimport com.alipay.sofa.ark.common.util.ReflectionUtils.FieldCallback;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.registry.ServiceReference;\nimport com.alipay.sofa.ark.spi.service.ArkInject;\nimport com.alipay.sofa.ark.spi.service.injection.InjectionService;\nimport com.alipay.sofa.ark.spi.service.registry.RegistryService;\nimport com.google.inject.Inject;\nimport com.google.inject.Singleton;\n\nimport java.lang.reflect.Field;\n\n/**\n * {@link InjectionService}\n *\n * @author qilong.zql\n * @since 0.4.0\n */\n@Singleton\npublic class InjectionServiceImpl implements InjectionService {\n\n    @Inject\n    private RegistryService registryService;\n\n    @Override\n    public void inject(final ServiceReference reference) {\n        inject(reference.getService(), reference.toString());\n    }\n\n    @Override\n    public void inject(final Object object) {\n        inject(object, object.getClass().getName());\n    }\n\n    private void inject(final Object instance, final String type) {\n        ReflectionUtils.doWithFields(instance.getClass(), new FieldCallback() {\n            @Override\n            public void doWith(Field field) throws ArkRuntimeException {\n                ArkInject arkInject = field.getAnnotation(ArkInject.class);\n                if (arkInject == null) {\n                    return;\n                }\n\n                Class<?> serviceType = arkInject.interfaceType() == void.class ? field.getType()\n                    : arkInject.interfaceType();\n                Object value = getService(serviceType, arkInject.uniqueId());\n\n                if (value == null) {\n                    ArkLoggerFactory.getDefaultLogger().warn(\n                        String.format(\"Inject {field= %s} of {service= %s} fail!\", field.getName(),\n                            type));\n                    return;\n                }\n                ReflectionUtils.makeAccessible(field);\n                try {\n                    field.set(instance, value);\n                    ArkLoggerFactory.getDefaultLogger().info(\n                        String.format(\"Inject {field= %s} of {service= %s} success!\",\n                            field.getName(), type));\n                } catch (Throwable throwable) {\n                    throw new ArkRuntimeException(throwable);\n                }\n            }\n        });\n    }\n\n    private Object getService(Class serviceType, String uniqueId) {\n        ServiceReference serviceReference = registryService.referenceService(serviceType, uniqueId);\n        return serviceReference == null ? null : serviceReference.getService();\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/plugin/PluginCommandProvider.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.plugin;\n\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.service.ArkInject;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport com.alipay.sofa.ark.spi.service.session.CommandProvider;\n\nimport java.net.URL;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.regex.Pattern;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class PluginCommandProvider implements CommandProvider {\n\n    @ArkInject\n    private PluginManagerService pluginManagerService;\n\n    @Override\n    public String getHelp() {\n        return HELP_MESSAGE;\n    }\n\n    @Override\n    public String handleCommand(String command) {\n        return new PluginCommand(command).process();\n    }\n\n    @Override\n    public boolean validate(String command) {\n        return new PluginCommand(command).isValidate();\n    }\n\n    private static final String HELP_MESSAGE = \"Plugin Command Tips:\\n\"\n                                               + \"  USAGE: plugin [option...] [pluginName...]\\n\"\n                                               + \"  SAMPLE: plugin -m plugin-A plugin-B\\n\"\n                                               + \"  -h  Shows the help message.\\n\"\n                                               + \"  -a  Shows all plugin name.\\n\"\n                                               + \"  -m  Shows the meta info of specified pluginName.\\n\"\n                                               + \"  -s  Shows the service info of specified pluginName.\\n\"\n                                               + \"  -d  Shows the detail info of specified pluginName.\\n\";\n\n    class PluginCommand {\n        private boolean        isValidate;\n        private Set<Character> options    = new HashSet<>();\n        private Set<String>    parameters = new HashSet<>();\n\n        PluginCommand(String command) {\n            if (StringUtils.isEmpty(command)) {\n                isValidate = false;\n                return;\n            }\n\n            String[] syntax = command.trim().split(Constants.SPACE_SPLIT);\n            if (!\"plugin\".equals(syntax[0])) {\n                isValidate = false;\n                return;\n            }\n\n            int pluginNameIndex = syntax.length;\n            // fetch all options and allow repetition\n            for (int i = 1; i < syntax.length; ++i) {\n                if (!syntax[i].startsWith(\"-\")) {\n                    pluginNameIndex = i;\n                    break;\n                }\n                if (syntax[i].startsWith(\"-\") && syntax[i].length() == 1) {\n                    isValidate = false;\n                    return;\n                }\n                for (int j = 1; j < syntax[i].length(); ++j) {\n                    options.add(syntax[i].charAt(j));\n                }\n            }\n\n            // only four option can be allowed.\n            for (Character option : options) {\n                switch (option) {\n                    case 'h':\n                    case 'a':\n                    case 'm':\n                    case 's':\n                    case 'd':\n                        continue;\n                    default:\n                        isValidate = false;\n                        return;\n                }\n            }\n\n            // '-h' or '-a' option can not be combined with other option, such as '-m'\n            if ((options.contains('h') || options.contains('a')) && options.size() > 1) {\n                isValidate = false;\n                return;\n            }\n\n            // take the rest option as pluginName parameters\n            while (pluginNameIndex < syntax.length) {\n                parameters.add(syntax[pluginNameIndex++]);\n            }\n\n            // '-h' or '-a' option need not pluginName parameter\n            if ((options.contains('h') || options.contains('a')) && parameters.size() > 0) {\n                isValidate = false;\n                return;\n            }\n\n            // no parameter is needed when no options\n            if (options.isEmpty()) {\n                isValidate = false;\n                return;\n            }\n\n            // if option is not 'h' or 'a', parameter should not be empty\n            if (!(options.contains('h') || options.contains('a')) && !options.isEmpty()\n                && parameters.isEmpty()) {\n                isValidate = false;\n                return;\n            }\n\n            isValidate = true;\n        }\n\n        boolean isValidate() {\n            return isValidate;\n        }\n\n        String process() {\n            if (!isValidate) {\n                return \"Error command format. Pls type 'plugin -h' to get help message\\n\";\n            }\n            StringBuilder sb = new StringBuilder(512);\n            // print plugin command help message\n            if (options.contains('h')) {\n                sb.append(getHelp());\n            } else if (options.contains('a')) {\n                return pluginList();\n            } else {\n                Set<String> candidates = pluginManagerService.getAllPluginNames();\n                boolean matched = false;\n                for (String pattern : parameters) {\n                    for (String candidate : candidates) {\n                        if (Pattern.matches(pattern, candidate)) {\n                            matched = true;\n                            sb.append(pluginInfo(candidate));\n                        }\n                    }\n                }\n                if (!matched) {\n                    sb.append(\"no matched plugin candidates.\").append(\"\\n\");\n                }\n            }\n            return sb.toString();\n        }\n\n        String pluginList() {\n            Set<String> pluginNames = pluginManagerService.getAllPluginNames();\n            StringBuilder sb = new StringBuilder(128);\n            if (pluginNames.isEmpty()) {\n                sb.append(\"no plugins.\").append(\"\\n\");\n            } else {\n                for (String pluginName : pluginNames) {\n                    sb.append(pluginName).append(\"\\n\");\n                }\n            }\n            sb.append(\"plugin count = \").append(pluginNames.size()).append(\"\\n\");\n            return sb.toString();\n        }\n\n        String pluginInfo(String pluginName) {\n            Plugin plugin = pluginManagerService.getPluginByName(pluginName);\n            StringBuilder sb = new StringBuilder(256);\n            // print plugin meta info\n            if (options.contains('m')) {\n                sb.append(\"PluginName:       \").append(pluginName).append(\"\\n\");\n                sb.append(\"Version:          \").append(plugin.getVersion()).append(\"\\n\");\n                sb.append(\"Priority:         \").append(plugin.getPriority()).append(\"\\n\");\n                sb.append(\"Activator:        \").append(plugin.getPluginActivator()).append(\"\\n\");\n                sb.append(\"Export Packages:  \")\n                    .append(StringUtils.setToStr(plugin.getExportPackages(), \",\", \"\\\\\"))\n                    .append(\"\\n\");\n                sb.append(\"Import Packages:  \")\n                    .append(StringUtils.setToStr(plugin.getImportPackages(), \",\", \"\\\\\"))\n                    .append(\"\\n\");\n                sb.append(\"Export Classes:   \")\n                    .append(StringUtils.setToStr(plugin.getExportClasses(), \",\", \"\\\\\"))\n                    .append(\"\\n\");\n                sb.append(\"Import Classes:   \")\n                    .append(StringUtils.setToStr(plugin.getImportClasses(), \",\", \"\\\\\"))\n                    .append(\"\\n\");\n                sb.append(\"Export Resources: \")\n                    .append(StringUtils.setToStr(plugin.getExportResources(), \",\", \"\\\\\"))\n                    .append(\"\\n\");\n                sb.append(\"Import Resources: \")\n                    .append(StringUtils.setToStr(plugin.getImportResources(), \",\", \"\\\\\"))\n                    .append(\"\\n\");\n            }\n            // print plugin service info\n            if (options.contains('s')) {\n                // TODO\n            }\n            // print plugin detail info\n            if (options.contains('d')) {\n                sb.append(\"GroupId:     \").append(plugin.getGroupId()).append(\"\\n\");\n                sb.append(\"ArtifactId:  \").append(plugin.getArtifactId()).append(\"\\n\");\n                sb.append(\"Version:     \").append(plugin.getVersion()).append(\"\\n\");\n                sb.append(\"URL:         \").append(plugin.getPluginURL()).append(\"\\n\");\n                sb.append(\"ClassLoader: \").append(plugin.getPluginClassLoader()).append(\"\\n\");\n                sb.append(\"ClassPath:   \").append(join(plugin.getClassPath(), \",\")).append(\"\\n\");\n            }\n            sb.append(\"\\n\");\n            return sb.toString();\n        }\n\n        String join(URL[] urls, String separator) {\n            Set<String> set = new HashSet<>();\n            if (urls != null) {\n                for (URL url : urls) {\n                    set.add(url.getPath());\n                }\n            }\n            return StringUtils.setToStr(set, separator, \"\\\\\");\n        }\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/plugin/PluginDeployServiceImpl.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.plugin;\n\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginDeployService;\nimport com.google.inject.Inject;\nimport com.google.inject.Singleton;\n\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * Service Implementation to deploy ark plugin\n *\n * @author ruoshan\n * @since 0.1.0\n */\n@Singleton\npublic class PluginDeployServiceImpl implements PluginDeployService {\n\n    @Inject\n    PluginManagerService pluginManagerService;\n\n    @Override\n    public void deploy() throws ArkRuntimeException {\n        for (Plugin plugin : pluginManagerService.getPluginsInOrder()) {\n            try {\n                deployPlugin(plugin);\n            } catch (ArkRuntimeException e) {\n                ArkLoggerFactory.getDefaultLogger().error(\n                    String.format(\"Deploy plugin: %s meet error\", plugin.getPluginName()), e);\n                throw e;\n            }\n        }\n    }\n\n    private void deployPlugin(Plugin plugin) throws ArkRuntimeException {\n        try {\n            ArkLoggerFactory.getDefaultLogger().info(\n                String.format(\"Start to deploy plugin: %s\", plugin.getPluginName()));\n            plugin.start();\n            ArkLoggerFactory.getDefaultLogger().info(\n                String.format(\"Finish to deploy plugin: %s\", plugin.getPluginName()));\n        } catch (ArkRuntimeException e) {\n            ArkLoggerFactory.getDefaultLogger().error(\n                String.format(\"Start plugin: %s meet error\", plugin.getPluginName()), e);\n            throw e;\n        }\n    }\n\n    @Override\n    public void unDeploy() throws ArkRuntimeException {\n        List<Plugin> pluginsInOrder = pluginManagerService.getPluginsInOrder();\n        Collections.reverse(pluginsInOrder);\n        for (Plugin plugin : pluginsInOrder) {\n            try {\n                unDeployPlugin(plugin);\n            } catch (ArkRuntimeException e) {\n                ArkLoggerFactory.getDefaultLogger().error(\n                    String.format(\"UnDeploy plugin: %s meet error\", plugin.getPluginName()), e);\n                throw e;\n            }\n        }\n    }\n\n    private void unDeployPlugin(Plugin plugin) throws ArkRuntimeException {\n        try {\n            ArkLoggerFactory.getDefaultLogger().info(\n                String.format(\"Start to unDeploy plugin: %s\", plugin.getPluginName())\n                        + plugin.getPluginName());\n            plugin.stop();\n            ArkLoggerFactory.getDefaultLogger().info(\n                String.format(\"Stop to unDeploy plugin: %s\", plugin.getPluginName())\n                        + plugin.getPluginName());\n        } catch (ArkRuntimeException e) {\n            ArkLoggerFactory.getDefaultLogger().error(\n                String.format(\"Stop plugin: %s meet error\", plugin.getPluginName()), e);\n            throw e;\n        }\n    }\n\n    @Override\n    public void init() throws ArkRuntimeException {\n\n    }\n\n    @Override\n    public void dispose() throws ArkRuntimeException {\n        unDeploy();\n    }\n\n    @Override\n    public int getPriority() {\n        return DEFAULT_PRECEDENCE;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/plugin/PluginFactoryServiceImpl.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.plugin;\n\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.common.util.AssertUtils;\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.container.model.PluginContextImpl;\nimport com.alipay.sofa.ark.container.model.PluginModel;\nimport com.alipay.sofa.ark.container.service.classloader.PluginClassLoader;\nimport com.alipay.sofa.ark.loader.JarPluginArchive;\nimport com.alipay.sofa.ark.loader.archive.JarFileArchive;\nimport com.alipay.sofa.ark.loader.jar.JarFile;\nimport com.alipay.sofa.ark.spi.archive.Archive;\nimport com.alipay.sofa.ark.spi.archive.PluginArchive;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.model.PluginConfig;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginFactoryService;\nimport com.google.inject.Singleton;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.*;\nimport java.util.jar.Attributes;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.*;\n\n/**\n * {@link PluginFactoryService}\n *\n * @author qilong.zql\n * @since 0.4.0\n */\n@Singleton\npublic class PluginFactoryServiceImpl implements PluginFactoryService {\n\n    @Override\n    public Plugin createPlugin(PluginArchive pluginArchive) throws IOException,\n                                                           IllegalArgumentException {\n        AssertUtils.isTrue(isArkPlugin(pluginArchive), \"Archive must be a ark plugin!\");\n        PluginModel plugin = new PluginModel();\n        Attributes manifestMainAttributes = pluginArchive.getManifest().getMainAttributes();\n        plugin\n            .setPluginName(manifestMainAttributes.getValue(PLUGIN_NAME_ATTRIBUTE))\n            .setGroupId(manifestMainAttributes.getValue(GROUP_ID_ATTRIBUTE))\n            .setArtifactId(manifestMainAttributes.getValue(ARTIFACT_ID_ATTRIBUTE))\n            .setVersion(manifestMainAttributes.getValue(PLUGIN_VERSION_ATTRIBUTE))\n            .setPriority(manifestMainAttributes.getValue(PRIORITY_ATTRIBUTE))\n            .setPluginActivator(manifestMainAttributes.getValue(ACTIVATOR_ATTRIBUTE))\n            .setClassPath(pluginArchive.getUrls())\n            .setPluginUrl(pluginArchive.getUrl())\n            .setExportClasses(manifestMainAttributes.getValue(EXPORT_CLASSES_ATTRIBUTE))\n            .setExportPackages(manifestMainAttributes.getValue(EXPORT_PACKAGES_ATTRIBUTE))\n            .setImportClasses(manifestMainAttributes.getValue(IMPORT_CLASSES_ATTRIBUTE))\n            .setImportPackages(manifestMainAttributes.getValue(IMPORT_PACKAGES_ATTRIBUTE))\n            .setImportResources(manifestMainAttributes.getValue(IMPORT_RESOURCES_ATTRIBUTE))\n            .setExportResources(manifestMainAttributes.getValue(EXPORT_RESOURCES_ATTRIBUTE))\n            .setPluginClassLoader(\n                new PluginClassLoader(plugin.getPluginName(), plugin.getClassPath()))\n            .setPluginContext(new PluginContextImpl(plugin));\n        return plugin;\n    }\n\n    @Override\n    public Plugin createPlugin(PluginArchive pluginArchive, URL[] extensions,\n                               Set<String> exportPackages) throws IOException,\n                                                          IllegalArgumentException {\n        AssertUtils.isTrue(isArkPlugin(pluginArchive), \"Archive must be a ark plugin!\");\n        if (extensions == null || extensions.length == 0) {\n            return createPlugin(pluginArchive);\n        }\n\n        PluginModel plugin = new PluginModel();\n        Attributes manifestMainAttributes = pluginArchive.getManifest().getMainAttributes();\n        plugin\n            .setPluginName(manifestMainAttributes.getValue(PLUGIN_NAME_ATTRIBUTE))\n            .setGroupId(manifestMainAttributes.getValue(GROUP_ID_ATTRIBUTE))\n            .setArtifactId(manifestMainAttributes.getValue(ARTIFACT_ID_ATTRIBUTE))\n            .setVersion(manifestMainAttributes.getValue(PLUGIN_VERSION_ATTRIBUTE))\n            .setPriority(manifestMainAttributes.getValue(PRIORITY_ATTRIBUTE))\n            .setPluginActivator(manifestMainAttributes.getValue(ACTIVATOR_ATTRIBUTE))\n            .setClassPath(getFinalPluginUrls(pluginArchive, extensions, plugin.getPluginName()))\n            .setPluginUrl(pluginArchive.getUrl())\n            .setExportMode(manifestMainAttributes.getValue(EXPORT_MODE))\n            .setExportClasses(manifestMainAttributes.getValue(EXPORT_CLASSES_ATTRIBUTE))\n            .setExportPackages(manifestMainAttributes.getValue(EXPORT_PACKAGES_ATTRIBUTE),\n                exportPackages)\n            .setImportClasses(manifestMainAttributes.getValue(IMPORT_CLASSES_ATTRIBUTE))\n            .setImportPackages(manifestMainAttributes.getValue(IMPORT_PACKAGES_ATTRIBUTE))\n            .setImportResources(manifestMainAttributes.getValue(IMPORT_RESOURCES_ATTRIBUTE))\n            .setExportResources(manifestMainAttributes.getValue(EXPORT_RESOURCES_ATTRIBUTE))\n            .setPluginClassLoader(\n                new PluginClassLoader(plugin.getPluginName(), plugin.getClassPath()))\n            .setPluginContext(new PluginContextImpl(plugin));\n        return plugin;\n    }\n\n    @Override\n    public Plugin createPlugin(File file) throws IOException {\n        JarFile pluginFile = new JarFile(file);\n        JarFileArchive jarFileArchive = new JarFileArchive(pluginFile);\n        JarPluginArchive jarPluginArchive = new JarPluginArchive(jarFileArchive);\n        return createPlugin(jarPluginArchive);\n    }\n\n    @Override\n    public Plugin createPlugin(File file, URL[] extensions) throws IOException {\n        JarFile pluginFile = new JarFile(file);\n        JarFileArchive jarFileArchive = new JarFileArchive(pluginFile);\n        JarPluginArchive jarPluginArchive = new JarPluginArchive(jarFileArchive);\n        return createPlugin(jarPluginArchive, extensions, new HashSet<>());\n    }\n\n    @Override\n    public Plugin createPlugin(File file, PluginConfig pluginConfig) throws IOException {\n        JarFile pluginFile = new JarFile(file);\n        JarFileArchive jarFileArchive = new JarFileArchive(pluginFile);\n        JarPluginArchive pluginArchive = new JarPluginArchive(jarFileArchive);\n\n        AssertUtils.isTrue(isArkPlugin(pluginArchive), \"Archive must be a ark plugin!\");\n        AssertUtils.isTrue(pluginConfig != null, \"PluginConfig must not be null!\");\n\n        PluginModel plugin = new PluginModel();\n        Attributes manifestMainAttributes = pluginArchive.getManifest().getMainAttributes();\n        plugin\n            .setPluginName(\n                !StringUtils.isEmpty(pluginConfig.getSpecifiedName()) ? pluginConfig\n                    .getSpecifiedName() : manifestMainAttributes.getValue(PLUGIN_NAME_ATTRIBUTE))\n            .setGroupId(manifestMainAttributes.getValue(GROUP_ID_ATTRIBUTE))\n            .setArtifactId(manifestMainAttributes.getValue(ARTIFACT_ID_ATTRIBUTE))\n            .setVersion(\n                !StringUtils.isEmpty(pluginConfig.getSpecifiedVersion()) ? pluginConfig\n                    .getSpecifiedVersion() : manifestMainAttributes\n                    .getValue(PLUGIN_VERSION_ATTRIBUTE))\n            .setPriority(manifestMainAttributes.getValue(PRIORITY_ATTRIBUTE))\n            .setPluginActivator(manifestMainAttributes.getValue(ACTIVATOR_ATTRIBUTE))\n            .setClassPath(\n                getFinalPluginUrls(pluginArchive, pluginConfig.getExtensionUrls(),\n                    plugin.getPluginName()))\n            .setPluginUrl(pluginArchive.getUrl())\n            .setExportMode(manifestMainAttributes.getValue(EXPORT_MODE))\n            .setExportClasses(manifestMainAttributes.getValue(EXPORT_CLASSES_ATTRIBUTE))\n            .setExportPackages(manifestMainAttributes.getValue(EXPORT_PACKAGES_ATTRIBUTE))\n            .setImportClasses(manifestMainAttributes.getValue(IMPORT_CLASSES_ATTRIBUTE))\n            .setImportPackages(manifestMainAttributes.getValue(IMPORT_PACKAGES_ATTRIBUTE))\n            .setImportResources(manifestMainAttributes.getValue(IMPORT_RESOURCES_ATTRIBUTE))\n            .setExportResources(manifestMainAttributes.getValue(EXPORT_RESOURCES_ATTRIBUTE))\n            .setPluginClassLoader(\n                new PluginClassLoader(plugin.getPluginName(), plugin.getClassPath()))\n            .setPluginContext(new PluginContextImpl(plugin));\n        return plugin;\n    }\n\n    @Override\n    public Plugin createEmbedPlugin(PluginArchive pluginArchive, ClassLoader masterClassLoader)\n                                                                                               throws IOException {\n        AssertUtils.isTrue(isArkPlugin(pluginArchive), \"Archive must be a ark plugin!\");\n        PluginModel plugin = new PluginModel();\n        Attributes manifestMainAttributes = pluginArchive.getManifest().getMainAttributes();\n        boolean enableExportClass = \"true\".equals(System.getProperty(PLUGIN_EXPORT_CLASS_ENABLE));\n        boolean enableClassIsolation = \"true\".equals(System\n            .getProperty(PLUGIN_CLASS_ISOLATION_ENABLE));\n        boolean overrideExportMode = PluginModel.EXPORTMODE_OVERRIDE.equals(manifestMainAttributes\n            .getValue(EXPORT_MODE));\n        plugin\n            .setPluginName(manifestMainAttributes.getValue(PLUGIN_NAME_ATTRIBUTE))\n            .setGroupId(manifestMainAttributes.getValue(GROUP_ID_ATTRIBUTE))\n            .setArtifactId(manifestMainAttributes.getValue(ARTIFACT_ID_ATTRIBUTE))\n            .setVersion(manifestMainAttributes.getValue(PLUGIN_VERSION_ATTRIBUTE))\n            .setPriority(manifestMainAttributes.getValue(PRIORITY_ATTRIBUTE))\n            .setPluginActivator(manifestMainAttributes.getValue(ACTIVATOR_ATTRIBUTE))\n            .setClassPath(\n                (enableClassIsolation || overrideExportMode) ? pluginArchive.getUrls()\n                    : ClassLoaderUtils.getURLs(masterClassLoader))\n            .setPluginUrl(pluginArchive.getUrl())\n            .setExportMode(manifestMainAttributes.getValue(EXPORT_MODE))\n            .setExportClasses(\n                enableExportClass ? manifestMainAttributes.getValue(EXPORT_CLASSES_ATTRIBUTE)\n                    : null)\n            .setExportPackages(\n                enableExportClass ? manifestMainAttributes.getValue(EXPORT_PACKAGES_ATTRIBUTE)\n                    : null)\n            .setImportClasses(manifestMainAttributes.getValue(IMPORT_CLASSES_ATTRIBUTE))\n            .setImportPackages(manifestMainAttributes.getValue(IMPORT_PACKAGES_ATTRIBUTE))\n            .setImportResources(manifestMainAttributes.getValue(IMPORT_RESOURCES_ATTRIBUTE))\n            .setExportResources(manifestMainAttributes.getValue(EXPORT_RESOURCES_ATTRIBUTE))\n            .setPluginClassLoader(\n                (enableClassIsolation || overrideExportMode) ? new PluginClassLoader(plugin\n                    .getPluginName(), plugin.getClassPath()) : masterClassLoader)\n            .setPluginContext(new PluginContextImpl(plugin));\n        return plugin;\n    }\n\n    private URL[] getFinalPluginUrls(PluginArchive pluginArchive, URL[] extensions,\n                                     String pluginName) throws IOException {\n        URL[] urls = pluginArchive.getUrls();\n        List<URL> urlList = new ArrayList<>(Arrays.asList(urls));\n        urlList.remove(null);\n\n        // get config by PLUGIN-EXPORT key, exclude jar by config\n        String excludeArtifact = ArkConfigs.getStringValue(String.format(PLUGIN_EXTENSION_FORMAT,\n            pluginName));\n        if (!StringUtils.isEmpty(excludeArtifact)) {\n            List<URL> preRemoveList = new ArrayList<>();\n            for (URL url : urlList) {\n                String[] dependencies = excludeArtifact.split(STRING_SEMICOLON);\n                for (String dependency : dependencies) {\n                    String artifactId = dependency.split(STRING_COLON)[0];\n                    String version = dependency.split(STRING_COLON)[1];\n                    if (url.getPath().endsWith(artifactId + \"-\" + version + \".jar!/\")) {\n                        preRemoveList.add(url);\n                        break;\n                    }\n                }\n            }\n            urlList.removeAll(preRemoveList);\n        }\n\n        // add extension urls to plugin classloader classpath\n        if (extensions != null && extensions.length > 0) {\n            pluginArchive.setExtensionUrls(extensions);\n            urlList.addAll(Arrays.asList(extensions));\n        }\n        return urlList.toArray(new URL[0]);\n    }\n\n    private boolean isArkPlugin(PluginArchive pluginArchive) {\n        return pluginArchive.isEntryExist(new Archive.EntryFilter() {\n            @Override\n            public boolean matches(Archive.Entry entry) {\n                return !entry.isDirectory()\n                       && entry.getName().equals(Constants.ARK_PLUGIN_MARK_ENTRY);\n            }\n        });\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/plugin/PluginManagerServiceImpl.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.plugin;\n\nimport com.alipay.sofa.ark.common.util.OrderComparator;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.google.inject.Singleton;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * Service Implementation to manager ark plugin\n *\n * @author ruoshan\n * @since 0.1.0\n */\n@Singleton\npublic class PluginManagerServiceImpl implements PluginManagerService {\n\n    private ConcurrentHashMap<String, Plugin> plugins = new ConcurrentHashMap<>();\n\n    @Override\n    public void registerPlugin(Plugin plugin) {\n        if (plugins.putIfAbsent(plugin.getPluginName(), plugin) != null) {\n            throw new ArkRuntimeException(String.format(\"duplicate plugin: %s exists.\",\n                plugin.getPluginName()));\n        }\n    }\n\n    @Override\n    public Plugin getPluginByName(String pluginName) {\n        return plugins.get(pluginName);\n    }\n\n    @Override\n    public Set<String> getAllPluginNames() {\n        return plugins.keySet();\n    }\n\n    @Override\n    public List<Plugin> getPluginsInOrder() {\n        List<Plugin> pluginList = new ArrayList<>(plugins.values());\n        Collections.sort(pluginList, new OrderComparator());\n        return pluginList;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/registry/RegistryServiceImpl.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.registry;\n\nimport com.alipay.sofa.ark.common.log.ArkLogger;\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.common.util.AssertUtils;\nimport com.alipay.sofa.ark.common.util.OrderComparator;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.container.registry.DefaultServiceFilter;\nimport com.alipay.sofa.ark.container.registry.ServiceMetadataImpl;\nimport com.alipay.sofa.ark.container.registry.ServiceReferenceImpl;\nimport com.alipay.sofa.ark.spi.registry.*;\nimport com.alipay.sofa.ark.spi.service.injection.InjectionService;\nimport com.alipay.sofa.ark.spi.service.registry.RegistryService;\nimport com.google.inject.Inject;\nimport com.google.inject.Singleton;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.CopyOnWriteArraySet;\n\n/**\n * Registry Service Implement\n *\n * @author ruoshan\n * @since 0.1.0\n */\n@Singleton\npublic class RegistryServiceImpl implements RegistryService {\n\n    private CopyOnWriteArraySet<ServiceReference<?>> services        = new CopyOnWriteArraySet<>();\n\n    private OrderComparator                          orderComparator = new OrderComparator();\n\n    @Inject\n    private InjectionService                         injectionService;\n\n    @Override\n    public <T> ServiceReference<T> publishService(Class<T> ifClass, T implObject,\n                                                  ServiceProvider serviceProvider) {\n        return publishService(ifClass, implObject, StringUtils.EMPTY_STRING, serviceProvider);\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T> ServiceReference<T> publishService(Class<T> ifClass, T implObject, String uniqueId,\n                                                  ServiceProvider serviceProvider) {\n        AssertUtils.assertNotNull(ifClass, \"Service interface should not be null.\");\n        AssertUtils.assertNotNull(implObject, \"Service implementation should not be null.\");\n        AssertUtils.assertNotNull(uniqueId, \"Service uniqueId should not be null.\");\n        AssertUtils.assertNotNull(serviceProvider, \"ServiceProvider should not be null.\");\n\n        ServiceMetadata serviceMetadata = new ServiceMetadataImpl(ifClass, uniqueId,\n            serviceProvider);\n        for (ServiceReference<?> serviceReference : services) {\n            if (serviceMetadata.equals(serviceReference.getServiceMetadata())) {\n                ArkLoggerFactory.getDefaultLogger().warn(\n                    String.format(\"Service: %s publish by: %s already exist\",\n                        serviceMetadata.getServiceName(), serviceProvider));\n                return (ServiceReference<T>) serviceReference;\n            }\n        }\n\n        ServiceReference<T> serviceReference = new ServiceReferenceImpl<>(serviceMetadata,\n            implObject);\n        injectionService.inject(serviceReference);\n\n        ArkLoggerFactory.getDefaultLogger().info(\n            String.format(\"Service: %s publish by: %s succeed\", serviceMetadata.getServiceName(),\n                serviceProvider));\n\n        services.add(serviceReference);\n\n        return serviceReference;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public <T> ServiceReference<T> referenceService(Class<T> ifClass) {\n        return referenceService(ifClass, StringUtils.EMPTY_STRING);\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T> ServiceReference<T> referenceService(Class<T> ifClass, String uniqueId) {\n        List<ServiceReference<T>> references = referenceServices(ifClass, uniqueId);\n        return references.isEmpty() ? null : references.get(0);\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T> List<ServiceReference<T>> referenceServices(Class<T> ifClass) {\n        return referenceServices(ifClass, StringUtils.EMPTY_STRING);\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T> List<ServiceReference<T>> referenceServices(Class<T> ifClass, String uniqueId) {\n        DefaultServiceFilter<T> defaultServiceFilter = new DefaultServiceFilter<>();\n        // only conditional on interface and uniqueId\n        defaultServiceFilter.setServiceInterface(ifClass).setUniqueId(uniqueId);\n        List<ServiceReference<T>> references = referenceServices(defaultServiceFilter);\n        return references;\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T> List<ServiceReference<T>> referenceServices(ServiceFilter<T> serviceFilter) {\n        List<ServiceReference<T>> serviceReferences = new ArrayList<>();\n\n        for (ServiceReference<?> reference : services) {\n            if (serviceFilter.match(reference)) {\n                serviceReferences.add((ServiceReference<T>) reference);\n            }\n        }\n        Collections.sort(serviceReferences, orderComparator);\n\n        return serviceReferences;\n    }\n\n    @Override\n    public int unPublishServices(ServiceFilter serviceFilter) {\n        int count = 0;\n\n        for (ServiceReference<?> reference : services) {\n            if (serviceFilter.match(reference)) {\n                services.remove(reference);\n                count += 1;\n            }\n        }\n\n        return count;\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/retrieval/ClassInfoMethod.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.retrieval;\n\nimport java.lang.reflect.Modifier;\nimport java.security.CodeSource;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Handle class information display\n *\n * @author yanzhu\n * @since 2.2.4\n */\npublic class ClassInfoMethod {\n\n    /**\n     * empty string\n     */\n    private static final String EMPTY_STRING = \"\";\n\n    /**\n     * get code source of class\n     */\n    private static String getCodeSource(final CodeSource cs) {\n        if (null == cs || null == cs.getLocation() || null == cs.getLocation().getFile()) {\n            return EMPTY_STRING;\n        }\n\n        return cs.getLocation().getFile();\n    }\n\n    /**\n     * get the complete class name\n     *\n     * @param clazz\n     * @return\n     */\n    private static String getClassName(Class<?> clazz) {\n        if (clazz.isArray()) {\n            StringBuilder sb = new StringBuilder(clazz.getName());\n            sb.delete(0, 2);\n            if (sb.length() > 0 && sb.charAt(sb.length() - 1) == ';') {\n                sb.deleteCharAt(sb.length() - 1);\n            }\n            sb.append(\"[]\");\n            return sb.toString();\n        } else {\n            return clazz.getName();\n        }\n    }\n\n    /**\n     * get modifier of class\n     *\n     * @param mod modifier\n     * @return 翻译值\n     */\n    private static String getModifier(int mod, char splitter) {\n        StringBuilder sb = new StringBuilder();\n        if (Modifier.isAbstract(mod)) {\n            sb.append(\"abstract\").append(splitter);\n        }\n        if (Modifier.isFinal(mod)) {\n            sb.append(\"final\").append(splitter);\n        }\n        if (Modifier.isInterface(mod)) {\n            sb.append(\"interface\").append(splitter);\n        }\n        if (Modifier.isNative(mod)) {\n            sb.append(\"native\").append(splitter);\n        }\n        if (Modifier.isPrivate(mod)) {\n            sb.append(\"private\").append(splitter);\n        }\n        if (Modifier.isProtected(mod)) {\n            sb.append(\"protected\").append(splitter);\n        }\n        if (Modifier.isPublic(mod)) {\n            sb.append(\"public\").append(splitter);\n        }\n        if (Modifier.isStatic(mod)) {\n            sb.append(\"static\").append(splitter);\n        }\n        if (Modifier.isStrict(mod)) {\n            sb.append(\"strict\").append(splitter);\n        }\n        if (Modifier.isSynchronized(mod)) {\n            sb.append(\"synchronized\").append(splitter);\n        }\n        if (Modifier.isTransient(mod)) {\n            sb.append(\"transient\").append(splitter);\n        }\n        if (Modifier.isVolatile(mod)) {\n            sb.append(\"volatile\").append(splitter);\n        }\n        if (sb.length() > 0) {\n            sb.deleteCharAt(sb.length() - 1);\n        }\n        return sb.toString();\n    }\n\n    /**\n     * get all super classes of current class\n     *\n     * @param clazz\n     * @return\n     */\n    private static String[] getSuperClass(Class clazz) {\n        List<String> list = new ArrayList<String>();\n        Class<?> superClass = clazz.getSuperclass();\n        if (null != superClass) {\n            list.add(ClassInfoMethod.getClassName(superClass));\n            while (true) {\n                superClass = superClass.getSuperclass();\n                if (null == superClass) {\n                    break;\n                }\n                list.add(ClassInfoMethod.getClassName(superClass));\n            }\n        }\n        return list.toArray(new String[0]);\n    }\n\n    /**\n     * get all classloaders of class\n     *\n     * @param clazz\n     * @return\n     */\n    private static String[] getClassloader(Class clazz) {\n        List<String> list = new ArrayList<String>();\n        ClassLoader loader = clazz.getClassLoader();\n        if (null != loader) {\n            list.add(loader.toString());\n            while (true) {\n                loader = loader.getParent();\n                if (null == loader) {\n                    break;\n                }\n                list.add(loader.toString());\n            }\n        }\n        return list.toArray(new String[0]);\n    }\n\n    /**\n     * create complete class information\n     *\n     * @param clazz\n     * @param bizName\n     * @return\n     */\n    public static String createClassInfo(Class<?> clazz, String bizName) {\n        ClassInfoVO classInfo = new ClassInfoVO();\n        classInfo.setClassInfo(ClassInfoMethod.getClassName(clazz));\n        classInfo.setCodeSource(ClassInfoMethod.getCodeSource(clazz.getProtectionDomain()\n            .getCodeSource()));\n        classInfo.setInterface(clazz.isInterface());\n        classInfo.setAnnotation(clazz.isAnnotation());\n        classInfo.setEnum(clazz.isEnum());\n        classInfo.setContainerName(bizName);\n        classInfo.setSimpleName(clazz.getSimpleName());\n        classInfo.setModifier(ClassInfoMethod.getModifier(clazz.getModifiers(), ','));\n        classInfo.setSuperClass(ClassInfoMethod.getSuperClass(clazz));\n        classInfo.setClassloader(ClassInfoMethod.getClassloader(clazz));\n        return ViewRender.renderClassInfo(classInfo);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/retrieval/ClassInfoVO.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.retrieval;\n\nimport java.util.Arrays;\n\n/**\n * Class Detail Info VO Of Telnet 'ck' Command\n *\n * @author yanzhu\n * @since 2.2.4\n */\n\npublic class ClassInfoVO {\n\n    private String   classInfo;\n    private String   codeSource;\n    private Boolean  isInterface;\n    private Boolean  isAnnotation;\n    private Boolean  isEnum;\n    private String   containerName;\n    private String   simpleName;\n    private String   modifier;\n    private String[] superClass;\n    private String[] classloader;\n\n    public ClassInfoVO() {\n    }\n\n    public String getClassInfo() {\n        return classInfo;\n    }\n\n    public void setClassInfo(String classInfo) {\n        this.classInfo = classInfo;\n    }\n\n    public String getCodeSource() {\n        return codeSource;\n    }\n\n    public void setCodeSource(String codeSource) {\n        this.codeSource = codeSource;\n    }\n\n    public boolean isInterface() {\n        return isInterface;\n    }\n\n    public void setInterface(boolean anInterface) {\n        isInterface = anInterface;\n    }\n\n    public boolean isAnnotation() {\n        return isAnnotation;\n    }\n\n    public void setAnnotation(boolean annotation) {\n        isAnnotation = annotation;\n    }\n\n    public boolean isEnum() {\n        return isEnum;\n    }\n\n    public void setEnum(boolean anEnum) {\n        isEnum = anEnum;\n    }\n\n    public String getContainerName() {\n        return containerName;\n    }\n\n    public void setContainerName(String containerName) {\n        this.containerName = containerName;\n    }\n\n    public String getSimpleName() {\n        return simpleName;\n    }\n\n    public void setSimpleName(String simpleName) {\n        this.simpleName = simpleName;\n    }\n\n    public String getModifier() {\n        return modifier;\n    }\n\n    public void setModifier(String modifier) {\n        this.modifier = modifier;\n    }\n\n    public String[] getSuperClass() {\n        return superClass;\n    }\n\n    public void setSuperClass(String[] superClass) {\n        this.superClass = superClass;\n    }\n\n    public String[] getClassloader() {\n        return classloader;\n    }\n\n    public void setClassloader(String[] classloader) {\n        this.classloader = classloader;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClassDetailVO{\" + \"classInfo='\" + classInfo + '\\'' + \", codeSource='\" + codeSource\n               + '\\'' + \", isInterface=\" + isInterface + \", isAnnotation=\" + isAnnotation\n               + \", isEnum=\" + isEnum + \", containerName='\" + containerName + '\\''\n               + \", simpleName='\" + simpleName + '\\'' + \", modifier='\" + modifier + '\\''\n               + \", superClass=\" + Arrays.toString(superClass) + \", classloader=\"\n               + Arrays.toString(classloader) + '}';\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/retrieval/InfoQueryCommandProvider.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.retrieval;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.common.log.ArkLogger;\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.common.util.EnvironmentUtils;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.container.service.classloader.BizClassLoader;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.service.session.CommandProvider;\nimport net.bytebuddy.agent.ByteBuddyAgent;\n\nimport java.lang.instrument.Instrumentation;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * @author yanzhu\n * @since 2.2.4\n */\npublic class InfoQueryCommandProvider implements CommandProvider {\n\n    @Override\n    public String getHelp() {\n        return HELP_MESSAGE;\n    }\n\n    @Override\n    public String handleCommand(String command) {\n        return new ContainerQueryInfoCommand(command).process();\n    }\n\n    @Override\n    public boolean validate(String command) {\n        return new ContainerQueryInfoCommand(command).isValidate();\n    }\n\n    private static final String HELP_MESSAGE = \"Query Class Info Or Other Info Command Tips:\\n\"\n                                               + \"  USAGE: plugin [option...] [className/beanName...]\\n\"\n                                               + \"  SAMPLE: ck -c com.sofa.ark.HelloWorld\\n\"\n                                               + \"  -h  Shows the help message.\\n\"\n                                               + \"  -c  Shows the class info.\\n\";\n\n    class ContainerQueryInfoCommand {\n        private boolean               isValidate;\n        private Set<Character>        options         = new HashSet<>();\n        private Set<String>           parameters      = new HashSet<>();\n        private final Instrumentation instrumentation = ByteBuddyAgent.install();\n\n        ContainerQueryInfoCommand(String command) {\n            if (StringUtils.isEmpty(command)) {\n                isValidate = false;\n                return;\n            }\n\n            String[] syntax = command.trim().split(Constants.SPACE_SPLIT);\n            if (!\"ck\".equals(syntax[0])) {\n                isValidate = false;\n                return;\n            }\n\n            int argumentIndex = syntax.length;\n            // fetch all options and allow repetition\n            for (int i = 1; i < syntax.length; ++i) {\n                if (!syntax[i].startsWith(\"-\")) {\n                    argumentIndex = i;\n                    break;\n                }\n                if (syntax[i].startsWith(\"-\") && syntax[i].length() == 1) {\n                    isValidate = false;\n                    return;\n                }\n                for (int j = 1; j < syntax[i].length(); ++j) {\n                    options.add(syntax[i].charAt(j));\n                }\n            }\n\n            // only the following option can be allowed.\n            for (Character option : options) {\n                switch (option) {\n                    case 'h':\n                    case 'c':\n                        continue;\n                    default:\n                        isValidate = false;\n                        return;\n                }\n            }\n\n            // check whether options is empty\n            if (options.isEmpty()) {\n                isValidate = false;\n                return;\n            }\n\n            // '-h' or '-c' option can not be combined with other option\n            if (options.contains('h') || options.contains('c')) {\n                if (options.size() > 1) {\n                    isValidate = false;\n                    return;\n                }\n            }\n\n            // take the rest option as parameters\n            while (argumentIndex < syntax.length) {\n                parameters.add(syntax[argumentIndex++]);\n            }\n\n            // '-h' option need not any parameter\n            if (options.contains('h') && parameters.size() > 0) {\n                isValidate = false;\n                return;\n            }\n\n            // if option is not 'h' or 'a', parameter should not be empty\n            if (!options.contains('h') && parameters.isEmpty()) {\n                isValidate = false;\n                return;\n            }\n\n            // if option is 'c', parameter count should be only one.\n            if (options.contains('c')) {\n                if (parameters.size() > 1) {\n                    isValidate = false;\n                    return;\n                }\n            }\n\n            isValidate = true;\n        }\n\n        boolean isValidate() {\n            return isValidate;\n        }\n\n        String process() {\n            if (!isValidate) {\n                return \"Error command format. Pls type 'ck -h' to get help message\\n\";\n            }\n\n            if (options.contains('h')) {\n                return HELP_MESSAGE;\n            } else if (options.contains('c')) {\n                return queryClass();\n            } else {\n                return HELP_MESSAGE;\n            }\n        }\n\n        String queryClass() {\n            if (EnvironmentUtils.isOpenSecurity()) {\n                return \"Cannot execute 'ck -c' command in security mode.\\n\";\n            }\n            String param = parameters.toArray(new String[] {})[0];\n            Set<Class<?>> matches = new HashSet<Class<?>>();\n            for (Class<?> c : instrumentation.getAllLoadedClasses()) {\n                if (c == null) {\n                    continue;\n                }\n                if (param.equals(c.getName())) {\n                    matches.add(c);\n                }\n            }\n            if (matches.isEmpty()) {\n                return \"Can not find class : \" + param;\n            }\n            return createClassInfo(matches).toString();\n        }\n\n        StringBuilder createClassInfo(Set<Class<?>> classSet) {\n            StringBuilder sb = new StringBuilder();\n            for (Class<?> clazz : classSet) {\n                ClassLoader classLoader = clazz.getClassLoader();\n                if (null != ArkClient.getMasterBiz()\n                    && classLoader == ArkClient.getMasterBiz().getBizClassLoader()) {\n                    String classInfo = ClassInfoMethod.createClassInfo(clazz, ArkClient\n                        .getMasterBiz().getIdentity());\n                    sb.append(classInfo).append(\"\\n\");\n                } else if (null != ArkClient.getMasterBiz()\n                           && classLoader instanceof BizClassLoader) {\n                    String classInfo = ClassInfoMethod.createClassInfo(clazz,\n                        ((BizClassLoader) classLoader).getBizIdentity());\n                    sb.append(classInfo).append(\"\\n\");\n                } else {\n                    String classInfo = ClassInfoMethod.createClassInfo(clazz,\n                        classLoader.toString());\n                    sb.append(classInfo).append(\"\\n\");\n                }\n            }\n            return sb;\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/service/retrieval/ViewRender.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.retrieval;\n\nimport com.taobao.text.Decoration;\nimport com.taobao.text.ui.Element;\nimport com.taobao.text.ui.TableElement;\nimport com.taobao.text.ui.TreeElement;\nimport com.taobao.text.util.RenderUtil;\n\n/**\n * Render Telnet Panel\n *\n * @author yanzhu\n * @since 2.2.4\n */\npublic class ViewRender {\n\n    /**\n     * render class information\n     *\n     * @param clazz\n     * @return\n     */\n    public static String renderClassInfo(ClassInfoVO clazz) {\n        TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1);\n        table\n            .row(Element.label(\"class-info\").style(Decoration.bold.bold()),\n                Element.label(clazz.getClassInfo()))\n            .row(Element.label(\"code-source\").style(Decoration.bold.bold()),\n                Element.label(clazz.getCodeSource()))\n            .row(Element.label(\"isInterface\").style(Decoration.bold.bold()),\n                Element.label(\"\" + clazz.isInterface()))\n            .row(Element.label(\"isAnnotation\").style(Decoration.bold.bold()),\n                Element.label(\"\" + clazz.isAnnotation()))\n            .row(Element.label(\"isEnum\").style(Decoration.bold.bold()),\n                Element.label(\"\" + clazz.isEnum()))\n            .row(Element.label(\"container-name\").style(Decoration.bold.bold()),\n                Element.label(\"\" + clazz.getContainerName()))\n            .row(Element.label(\"simple-name\").style(Decoration.bold.bold()),\n                Element.label(clazz.getSimpleName()))\n            .row(Element.label(\"modifier\").style(Decoration.bold.bold()),\n                Element.label(clazz.getModifier()))\n            .row(Element.label(\"super-class\").style(Decoration.bold.bold()), drawSuperClass(clazz))\n            .row(Element.label(\"class-loader\").style(Decoration.bold.bold()),\n                drawClassLoader(clazz.getClassloader()));\n        return RenderUtil.render(table);\n    }\n\n    private static Element drawSuperClass(ClassInfoVO clazz) {\n        return drawTree(clazz.getSuperClass());\n    }\n\n    private static Element drawClassLoader(String[] classloaders) {\n        return drawTree(classloaders);\n    }\n\n    private static Element drawTree(String[] nodes) {\n        TreeElement root = new TreeElement();\n        TreeElement parent = root;\n        for (String node : nodes) {\n            TreeElement child = new TreeElement(Element.label(node));\n            parent.addChild(child);\n            parent = child;\n        }\n        return root;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/session/NettyTelnetServer.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.session;\n\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.common.util.EnvironmentUtils;\nimport com.alipay.sofa.ark.container.session.handler.ArkCommandHandler;\nimport io.netty.bootstrap.ServerBootstrap;\nimport io.netty.channel.Channel;\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.channel.ChannelInitializer;\nimport io.netty.channel.ChannelOption;\nimport io.netty.channel.ChannelPipeline;\nimport io.netty.channel.EventLoopGroup;\nimport io.netty.channel.SimpleChannelInboundHandler;\nimport io.netty.channel.nio.NioEventLoopGroup;\nimport io.netty.channel.socket.SocketChannel;\nimport io.netty.channel.socket.nio.NioServerSocketChannel;\nimport io.netty.handler.codec.DelimiterBasedFrameDecoder;\nimport io.netty.handler.codec.Delimiters;\nimport io.netty.handler.codec.string.StringDecoder;\nimport io.netty.handler.codec.string.StringEncoder;\nimport io.netty.handler.logging.LogLevel;\nimport io.netty.handler.logging.LoggingHandler;\n\nimport java.util.concurrent.Executor;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.CHANNEL_QUIT;\nimport static com.alipay.sofa.ark.spi.constant.Constants.LOCAL_HOST;\n\n/**\n * @author qilong.zql\n * @since 1.0.0\n */\npublic class NettyTelnetServer {\n\n    private int             port;\n    private ServerBootstrap serverBootstrap;\n    private EventLoopGroup  bossGroup;\n    private EventLoopGroup  workerGroup;\n    private Channel         channel;\n\n    public NettyTelnetServer(int port, Executor executor) {\n        this.port = port;\n        bossGroup = new NioEventLoopGroup(1, executor);\n        workerGroup = new NioEventLoopGroup(1, executor);\n    }\n\n    public void open() throws InterruptedException {\n        serverBootstrap = new ServerBootstrap();\n        serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024);\n        serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)\n            .handler(new LoggingHandler(LogLevel.INFO)).childHandler(new NettyTelnetInitializer());\n        channel = serverBootstrap.bind(port).sync().channel();\n    }\n\n    public void close() {\n        channel.close();\n        bossGroup.shutdownGracefully();\n        workerGroup.shutdownGracefully();\n    }\n\n    static class NettyTelnetInitializer extends ChannelInitializer<SocketChannel> {\n        private static StringDecoder DECODER = new StringDecoder();\n        private static StringEncoder ENCODER = new StringEncoder();\n\n        @Override\n        protected void initChannel(SocketChannel channel) throws Exception {\n            if (EnvironmentUtils.isOpenSecurity()) {\n                if (!channel.remoteAddress().getHostName().equals(LOCAL_HOST)) {\n                    return;\n                }\n            }\n            ChannelPipeline pipeline = channel.pipeline();\n            pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));\n            pipeline.addLast(ENCODER);\n            pipeline.addLast(DECODER);\n            pipeline.addLast(new NettyTelnetHandler());\n        }\n    }\n\n    static class NettyTelnetHandler extends SimpleChannelInboundHandler<String> {\n\n        private static ArkCommandHandler arkCommandHandler = new ArkCommandHandler();\n\n        @Override\n        public void channelActive(ChannelHandlerContext ctx) throws Exception {\n            super.channelActive(ctx);\n            ctx.write(arkCommandHandler.promptMessage());\n            ctx.flush();\n        }\n\n        @Override\n        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {\n            ArkLoggerFactory.getDefaultLogger()\n                .error(\"Error occurs in netty telnet server.\", cause);\n            super.exceptionCaught(ctx, cause);\n        }\n\n        @Override\n        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {\n            if (CHANNEL_QUIT.contains(msg)) {\n                ctx.channel().close();\n                return;\n            }\n            ctx.write(arkCommandHandler.responseMessage(msg));\n            ctx.flush();\n        }\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/session/StandardTelnetServerImpl.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.session;\n\nimport com.alipay.sofa.ark.common.log.ArkLogger;\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.common.thread.CommonThreadPool;\nimport com.alipay.sofa.ark.common.thread.ThreadPoolManager;\nimport com.alipay.sofa.ark.common.util.AssertUtils;\nimport com.alipay.sofa.ark.common.util.EnvironmentUtils;\nimport com.alipay.sofa.ark.common.util.PortSelectUtils;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.service.session.TelnetServerService;\nimport com.google.inject.Singleton;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.DEFAULT_SELECT_PORT_SIZE;\nimport static com.alipay.sofa.ark.spi.constant.Constants.DEFAULT_TELNET_PORT;\nimport static com.alipay.sofa.ark.spi.constant.Constants.TELNET_PORT_ATTRIBUTE;\nimport static com.alipay.sofa.ark.spi.constant.Constants.TELNET_SERVER_ENABLE;\n\n/**\n * {@link TelnetServerService}\n *\n * @author qilong.zql\n * @since 0.4.0\n */\n@Singleton\npublic class StandardTelnetServerImpl implements TelnetServerService {\n\n    private static final int  WORKER_THREAD_POOL_SIZE = 2;\n\n    private int               port                    = -1;\n\n    private AtomicBoolean     shutdown                = new AtomicBoolean(false);\n\n    private boolean           enableTelnetServer      = EnvironmentUtils.getProperty(\n                                                          TELNET_SERVER_ENABLE, \"true\")\n                                                          .equalsIgnoreCase(\"true\");\n\n    private NettyTelnetServer nettyTelnetServer;\n\n    public StandardTelnetServerImpl() {\n        if (enableTelnetServer) {\n            String telnetPort = EnvironmentUtils.getProperty(TELNET_PORT_ATTRIBUTE);\n            try {\n                if (!StringUtils.isEmpty(telnetPort)) {\n                    port = Integer.parseInt(telnetPort);\n                } else {\n                    port = PortSelectUtils.selectAvailablePort(DEFAULT_TELNET_PORT,\n                        DEFAULT_SELECT_PORT_SIZE);\n                }\n            } catch (NumberFormatException e) {\n                ArkLoggerFactory.getDefaultLogger().error(\n                    String.format(\"Invalid port in %s\", telnetPort), e);\n                throw new ArkRuntimeException(e);\n            }\n        }\n    }\n\n    @Override\n    public void run() {\n        AssertUtils.isTrue(port > 0, \"Telnet port should be positive integer.\");\n        try {\n            ArkLoggerFactory.getDefaultLogger().info(\"Listening on port: \" + port);\n            CommonThreadPool workerPool = new CommonThreadPool()\n                .setCorePoolSize(WORKER_THREAD_POOL_SIZE).setDaemon(true)\n                .setThreadPoolName(Constants.TELNET_SERVER_WORKER_THREAD_POOL_NAME);\n            ThreadPoolManager.registerThreadPool(Constants.TELNET_SERVER_WORKER_THREAD_POOL_NAME,\n                workerPool);\n            nettyTelnetServer = new NettyTelnetServer(port, workerPool.getExecutor());\n            nettyTelnetServer.open();\n        } catch (InterruptedException e) {\n            ArkLoggerFactory.getDefaultLogger().error(\"Unable to open netty telnet server.\", e);\n            throw new ArkRuntimeException(e);\n        }\n    }\n\n    @Override\n    public void shutdown() {\n        if (shutdown.compareAndSet(false, true)) {\n            try {\n                if (nettyTelnetServer != null) {\n                    nettyTelnetServer.close();\n                    nettyTelnetServer = null;\n                }\n            } catch (Throwable t) {\n                ArkLoggerFactory.getDefaultLogger().error(\n                    \"An error occurs when shutdown telnet server.\", t);\n                throw new ArkRuntimeException(t);\n            }\n        }\n    }\n\n    @Override\n    public void init() throws ArkRuntimeException {\n        if (enableTelnetServer) {\n            run();\n        } else {\n            ArkLoggerFactory.getDefaultLogger().warn(\"Telnet server is disabled.\");\n        }\n    }\n\n    @Override\n    public void dispose() throws ArkRuntimeException {\n        if (enableTelnetServer) {\n            shutdown();\n        }\n    }\n\n    @Override\n    public int getPriority() {\n        return HIGHEST_PRECEDENCE;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/session/handler/AbstractTerminalTypeMapping.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.session.handler;\n\nimport java.io.File;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Map special key value according to different terminal type\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic abstract class AbstractTerminalTypeMapping {\n\n    public static String getDefaultTerminalType() {\n        return File.separatorChar == '/' ? \"XTERM\" : \"ANSI\";\n    }\n\n    protected Map<String, KEYS> escKeys;\n\n    protected byte              backSpace;\n\n    protected byte              del;\n\n    public AbstractTerminalTypeMapping(byte backSpace, byte del) {\n        this.backSpace = backSpace;\n        this.del = del;\n        escKeys = new HashMap<>();\n        escKeys.put(\"[C\", KEYS.RIGHT);\n        escKeys.put(\"[D\", KEYS.LEFT);\n        escKeys.put(\"[3~\", KEYS.DEL);\n    }\n\n    public byte getBackspace() {\n        return backSpace;\n    }\n\n    public byte getDel() {\n        return del;\n    }\n\n    public KEYS getMatchKeys(String str) {\n        if (escKeys.get(str) != null) {\n            return escKeys.get(str);\n        }\n        if (isPossibleEscKeys(str)) {\n            return KEYS.UNFINISHED;\n        }\n        return KEYS.UNKNOWN;\n    }\n\n    protected boolean isPossibleEscKeys(String str) {\n        for (String key : escKeys.keySet()) {\n            if (key.startsWith(str)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    public enum KEYS {\n        RIGHT, LEFT, DEL, UNFINISHED, UNKNOWN\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/session/handler/ArkCommandHandler.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.session.handler;\n\nimport com.alipay.sofa.ark.common.thread.CommonThreadPool;\nimport com.alipay.sofa.ark.common.thread.ThreadPoolManager;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.registry.ServiceReference;\nimport com.alipay.sofa.ark.spi.service.registry.RegistryService;\nimport com.alipay.sofa.ark.spi.service.session.CommandProvider;\n\nimport java.util.List;\n\n/**\n * Collect implementation of {@link com.alipay.sofa.ark.spi.service.session.CommandProvider}\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic class ArkCommandHandler {\n\n    private RegistryService registryService;\n\n    static {\n        init();\n    }\n\n    private static void init() {\n        CommonThreadPool commandPool = new CommonThreadPool().setAllowCoreThreadTimeOut(true)\n            .setThreadPoolName(Constants.TELNET_COMMAND_THREAD_POOL_NAME).setDaemon(true);\n        ThreadPoolManager\n            .registerThreadPool(Constants.TELNET_COMMAND_THREAD_POOL_NAME, commandPool);\n    }\n\n    public ArkCommandHandler() {\n        registryService = ArkServiceContainerHolder.getContainer()\n            .getService(RegistryService.class);\n    }\n\n    public String handleCommand(String cmdLine) {\n        if (StringUtils.isEmpty(cmdLine)) {\n            return StringUtils.EMPTY_STRING;\n        }\n        List<ServiceReference<CommandProvider>> commandProviders = registryService\n            .referenceServices(CommandProvider.class, null);\n        for (ServiceReference<CommandProvider> commandService : commandProviders) {\n            CommandProvider commandProvider = commandService.getService();\n            if (commandProvider.validate(cmdLine)) {\n                return commandProvider.handleCommand(cmdLine);\n            }\n        }\n        return helpMessage(commandProviders);\n    }\n\n    public String helpMessage(List<ServiceReference<CommandProvider>> commandProviders) {\n        StringBuilder sb = new StringBuilder();\n        for (ServiceReference<CommandProvider> commandService : commandProviders) {\n            CommandProvider commandProvider = commandService.getService();\n            sb.append(commandProvider.getHelp());\n        }\n        return sb.toString();\n    }\n\n    public String promptMessage() {\n        return Constants.TELNET_SESSION_PROMPT;\n    }\n\n    public String responseMessage(String cmd) {\n        String commandResult = handleCommand(cmd);\n        commandResult = commandResult.replace(\"\\n\", Constants.TELNET_STRING_END);\n        if (StringUtils.isEmpty(commandResult)) {\n            commandResult = Constants.TELNET_STRING_END;\n        } else if (!commandResult.endsWith(Constants.TELNET_STRING_END)) {\n            commandResult = commandResult + Constants.TELNET_STRING_END\n                            + Constants.TELNET_STRING_END;\n        } else if (!commandResult.endsWith(Constants.TELNET_STRING_END\n            .concat(Constants.TELNET_STRING_END))) {\n            commandResult = commandResult + Constants.TELNET_STRING_END;\n        }\n        commandResult = commandResult + promptMessage();\n        return commandResult;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/test/NoneDelegateTestClassLoader.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.test;\n\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.container.service.classloader.BizClassLoader;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.BizState;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\n\nimport java.net.URL;\n\n/**\n * @author caojie.cj\n * @since 0.1.0\n */\npublic class NoneDelegateTestClassLoader extends BizClassLoader {\n    public NoneDelegateTestClassLoader(String bizIdentity, URL[] urls) {\n        super(bizIdentity, urls);\n        // since version 1.1.0, we support load extension from ark biz, we should register biz now.\n        BizManagerService bizManagerService = ArkServiceContainerHolder.getContainer().getService(\n            BizManagerService.class);\n        Biz testBiz = createTestBiz(bizIdentity);\n        bizManagerService.registerBiz(testBiz);\n        ((BizModel) testBiz).setBizState(BizState.ACTIVATED);\n    }\n\n    private Biz createTestBiz(String bizIdentity) {\n        String[] bizNameAndVersion = bizIdentity.split(\":\");\n        if (bizNameAndVersion.length != 2) {\n            throw new ArkRuntimeException(\"error bizIdentity format.\");\n        }\n        BizManagerService bizManagerService = ArkServiceContainerHolder.getContainer().getService(\n            BizManagerService.class);\n        Biz testBiz = new BizModel().setBizName(bizNameAndVersion[0])\n            .setBizVersion(bizNameAndVersion[1]).setClassLoader(this).setDenyImportPackages(\"\")\n            .setDenyImportClasses(\"\").setDenyImportResources(\"\").setBizState(BizState.RESOLVED);\n        bizManagerService.registerBiz(testBiz);\n        return testBiz;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/test/TestClassLoader.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.test;\n\nimport com.alipay.sofa.ark.common.util.ClassUtils;\nimport com.alipay.sofa.ark.common.util.EnvironmentUtils;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.container.service.classloader.BizClassLoader;\nimport com.alipay.sofa.ark.exception.ArkLoaderException;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.BizState;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\n\nimport java.net.URL;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class TestClassLoader extends BizClassLoader {\n\n    private final ClassLoader delegateClassLoader;\n\n    private static String[]   packageForTest = {\n            // Junit\n            \"org.junit\", \"junit\", \"org.hamcrest\",\n            // TestNG\n            \"org.testng\", \"com.beust.jcommander\", \"bsh\",\n            // mockito\n            \"org.mockito\",\n            // Ark\n            \"com.alipay.sofa.ark.support.common\",\n            // tomcat\n            \"org.apache.catalina\", \"org.apache.coyote\", \"org.apache.juli\", \"org.apache.naming\",\n            \"org.apache.tomcat\", \"org.apache.el\", \"javax\" };\n\n    private List<String>      delegateClassToAppClassLoader;\n\n    private List<String>      delegateClassToTestClassLoader;\n\n    public TestClassLoader(String bizIdentity, URL[] urls, ClassLoader delegate) {\n        super(bizIdentity, urls, true);\n        delegateClassLoader = delegate;\n        // since version 1.1.0, we support load extension from ark biz, we should register biz now.\n        BizManagerService bizManagerService = ArkServiceContainerHolder.getContainer().getService(\n            BizManagerService.class);\n        Biz testBiz = createTestBiz(bizIdentity);\n        bizManagerService.registerBiz(testBiz);\n        ((BizModel) testBiz).setBizState(BizState.ACTIVATED);\n        super.setBizModel((BizModel) testBiz);\n    }\n\n    @Override\n    protected Class<?> loadClassInternal(String name, boolean resolve) throws ArkLoaderException {\n        if (isDelegateToAppClassLoader(ClassUtils.getPackageName(name))) {\n            try {\n                return delegateClassLoader.loadClass(name);\n            } catch (ClassNotFoundException e) {\n                throw new ArkLoaderException(String.format(\n                    \"[TestClass Loader] %s : can not load class: %s\", getBizIdentity(), name));\n            }\n        } else {\n            return super.loadClassInternal(name, resolve);\n        }\n    }\n\n    private boolean isDelegateToAppClassLoader(String name) {\n        if (delegateClassToAppClassLoader == null) {\n            String classes = EnvironmentUtils.getProperty(\n                Constants.FORCE_DELEGATE_TO_APP_CLASSLOADER, Constants.EMPTY_STR);\n            delegateClassToAppClassLoader = Arrays.asList(classes.split(Constants.COMMA_SPLIT));\n        }\n        if (delegateClassToTestClassLoader == null) {\n            String classes = EnvironmentUtils.getProperty(\n                Constants.FORCE_DELEGATE_TO_TEST_CLASSLOADER, Constants.EMPTY_STR);\n            delegateClassToTestClassLoader = Arrays.asList(classes.split(Constants.COMMA_SPLIT));\n        }\n\n        for (String pkg : delegateClassToAppClassLoader) {\n            if (!StringUtils.isEmpty(pkg) && name.startsWith(pkg)) {\n                return true;\n            }\n        }\n\n        for (String pkg : delegateClassToTestClassLoader) {\n            if (!StringUtils.isEmpty(pkg) && name.startsWith(pkg)) {\n                return false;\n            }\n        }\n\n        for (String pkg : packageForTest) {\n            if (name.startsWith(pkg)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    private Biz createTestBiz(String bizIdentity) {\n        String[] bizNameAndVersion = bizIdentity.split(\":\");\n        if (bizNameAndVersion.length != 2) {\n            throw new ArkRuntimeException(\"error bizIdentity format.\");\n        }\n        BizManagerService bizManagerService = ArkServiceContainerHolder.getContainer().getService(\n            BizManagerService.class);\n        Biz testBiz = new BizModel().setBizName(bizNameAndVersion[0])\n            .setBizVersion(bizNameAndVersion[1]).setClassLoader(this).setDenyImportPackages(\"\")\n            .setDenyImportClasses(\"\").setDenyImportResources(\"\").setBizState(BizState.RESOLVED);\n        bizManagerService.registerBiz(testBiz);\n        return testBiz;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/java/com/alipay/sofa/ark/container/test/TestHelper.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.test;\n\nimport com.alipay.sofa.ark.container.ArkContainer;\nimport com.alipay.sofa.ark.spi.pipeline.PipelineContext;\nimport com.alipay.sofa.ark.spi.service.classloader.ClassLoaderService;\n\nimport java.net.URL;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class TestHelper {\n\n    private final static String MOCK_BIZ_IDENTITY = \"Mock Biz:Mock Version\";\n    private ArkContainer        arkContainer;\n\n    public TestHelper(Object object) {\n        this.arkContainer = (ArkContainer) object;\n    }\n\n    public ClassLoader createTestClassLoader() {\n        PipelineContext context = arkContainer.getPipelineContext();\n        URL[] classpath = context.getLaunchCommand().getClasspath();\n        ClassLoaderService classloaderService = arkContainer.getArkServiceContainer().getService(\n            ClassLoaderService.class);\n        return new TestClassLoader(MOCK_BIZ_IDENTITY, classpath,\n            classloaderService.getSystemClassLoader());\n    }\n\n    public ClassLoader createNoneDelegateTestClassLoader() {\n        PipelineContext context = arkContainer.getPipelineContext();\n        URL[] classpath = context.getLaunchCommand().getClasspath();\n        ClassLoaderService classloaderService = arkContainer.getArkServiceContainer().getService(\n            ClassLoaderService.class);\n        return new NoneDelegateTestClassLoader(MOCK_BIZ_IDENTITY, classpath);\n    }\n\n    public boolean isStarted() {\n        return arkContainer.isStarted();\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/main/resources/META-INF/services/com.alipay.sofa.ark.common.guice.AbstractArkGuiceModule",
    "content": "com.alipay.sofa.ark.container.guice.ContainerModule"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/ArkContainerTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.common.util.FileUtils;\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.container.session.handler.ArkCommandHandler;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.loader.ExecutableArkBizJar;\nimport com.alipay.sofa.ark.loader.archive.JarFileArchive;\nimport com.alipay.sofa.ark.spi.model.BizState;\nimport com.alipay.sofa.ark.spi.service.biz.BizFactoryService;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.extension.ArkServiceLoader;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.mockito.MockedStatic;\nimport org.mockito.Mockito;\n\nimport java.net.URL;\n\nimport static com.alipay.sofa.ark.container.ArkContainer.main;\nimport static com.alipay.sofa.ark.spi.constant.Constants.TELNET_SESSION_PROMPT;\nimport static com.alipay.sofa.ark.spi.constant.Constants.TELNET_STRING_END;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * @author ruoshan\n * @since 0.1.0\n */\npublic class ArkContainerTest extends BaseTest {\n\n    private URL jarURL = ArkContainerTest.class.getClassLoader().getResource(\"test.jar\");\n\n    @Override\n    public void before() {\n        // no op\n    }\n\n    @Override\n    public void after() {\n        // no op\n    }\n\n    @Test\n    public void testStart() throws ArkRuntimeException {\n        String[] args = new String[] { \"-Ajar=\" + jarURL.toExternalForm() };\n        ArkContainer arkContainer = (ArkContainer) main(args);\n        assertTrue(arkContainer.isStarted());\n        arkContainer.stop();\n    }\n\n    @Test\n    public void testStop() throws Exception {\n        ArkContainer arkContainer = new ArkContainer(new ExecutableArkBizJar(new JarFileArchive(\n            FileUtils.file((jarURL.getFile())))));\n        arkContainer.start();\n        arkContainer.stop();\n        Assert.assertFalse(arkContainer.isRunning());\n    }\n\n    @Test\n    public void testArkServiceLoader() throws ArkRuntimeException {\n        String[] args = new String[] { \"-Ajar=\" + jarURL.toExternalForm() };\n        ArkContainer arkContainer = (ArkContainer) main(args);\n        Assert.assertNotNull(ArkServiceLoader.getExtensionLoaderService());\n        arkContainer.stop();\n    }\n\n    @Test\n    public void testResponseMessage() {\n        String[] args = new String[] { \"-Ajar=\" + jarURL.toExternalForm() };\n        main(args);\n        ArkCommandHandler arkCommandHandler = new ArkCommandHandler();\n        assertEquals(TELNET_STRING_END + TELNET_SESSION_PROMPT,\n            arkCommandHandler.responseMessage(null));\n        assertTrue(arkCommandHandler.responseMessage(\"a\").endsWith(\n            TELNET_STRING_END + TELNET_STRING_END + TELNET_SESSION_PROMPT));\n        assertTrue(arkCommandHandler.responseMessage(\"-a\").endsWith(\n            TELNET_STRING_END + TELNET_STRING_END + TELNET_SESSION_PROMPT));\n        assertTrue(arkCommandHandler.responseMessage(\"biz -a\").endsWith(\n            TELNET_STRING_END + TELNET_STRING_END + TELNET_SESSION_PROMPT));\n    }\n\n    @Test\n    public void testDeployBizAfterMasterBizReady() throws Exception {\n        String[] args = new String[] { \"-Ajar=\" + jarURL.toExternalForm() };\n        ArkContainer arkContainer = (ArkContainer) main(args);\n        BizManagerService bizManagerService = arkContainer.getArkServiceContainer().getService(BizManagerService.class);\n        BizFactoryService bizFactoryService = arkContainer.getArkServiceContainer().getService(BizFactoryService.class);\n        BizModel masterBiz = createTestBizModel(\"master\", \"1.0.0\", BizState.RESOLVED,\n                ArkContainerTest.class.getClassLoader());\n        ArkConfigs.setEmbedStaticBizEnable(true);\n\n        try (MockedStatic<ArkClient> mockedStatic = Mockito.mockStatic(ArkClient.class)) {\n\n            mockedStatic.when(ArkClient::getMasterBiz).thenReturn(masterBiz);\n            mockedStatic.when(ArkClient::getBizFactoryService).thenReturn(bizFactoryService);\n            mockedStatic.when(ArkClient::getBizManagerService).thenReturn(bizManagerService);\n\n            bizManagerService.registerBiz(masterBiz);\n            masterBiz.setBizState(BizState.ACTIVATED);\n\n            assertEquals(arkContainer, arkContainer.deployBizAfterMasterBizReady());\n            assertEquals(2,bizManagerService.getBizInOrder().size());\n        }finally {\n            bizManagerService.getBizInOrder().stream().forEach(biz -> ((BizModel)biz).setBizState(BizState.BROKEN));\n            bizManagerService.unRegisterBiz(\"biz-demo\",\"1.0.0\");\n            arkContainer.stop();\n            ArkConfigs.setEmbedStaticBizEnable(false);\n        }\n    }\n\n    @Test(expected = ArkRuntimeException.class)\n    public void testStartNotWithCommandLine() {\n        String[] args = new String[] { \"-BmethodName=main\", \"-BclassName=a\",\n                \"-Aclasspath=\" + jarURL.toString() };\n        main(args);\n    }\n\n    @Test\n    public void testOtherMethods() {\n\n        String[] args = new String[] { \"-Ajar=\" + jarURL.toExternalForm() };\n        ArkContainer arkContainer = (ArkContainer) main(args);\n        assertEquals(0, arkContainer.getProfileConfFiles(\"prod\").size());\n\n        assertTrue(arkContainer.isRunning());\n        assertNotNull(arkContainer.getArkServiceContainer());\n        assertNotNull(arkContainer.getPipelineContext());\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/BaseTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container;\n\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.container.model.PluginModel;\nimport com.alipay.sofa.ark.container.pipeline.RegisterServiceStage;\nimport com.alipay.sofa.ark.container.registry.PluginServiceProvider;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainer;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.container.service.classloader.BizClassLoader;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.BizState;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.extension.ExtensionLoaderService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.BeforeClass;\nimport org.mockito.MockedStatic;\n\nimport java.lang.management.ManagementFactory;\nimport java.lang.management.RuntimeMXBean;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static com.alipay.sofa.ark.common.util.FileUtils.file;\nimport static com.alipay.sofa.ark.spi.service.extension.ArkServiceLoader.setExtensionLoaderService;\nimport static org.mockito.Mockito.*;\n\n/**\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class BaseTest {\n\n    private URL                     jarURL              = ArkContainerTest.class.getClassLoader()\n                                                            .getResource(\"test.jar\");\n    protected ArkServiceContainer   arkServiceContainer = new ArkServiceContainer(new String[] {});\n    protected ArkContainer          arkContainer;\n    MockedStatic<ManagementFactory> managementFactoryMockedStatic;\n    static {\n        // fix cobertura bug\n        new PluginServiceProvider(new PluginModel());\n    }\n\n    public static BizModel createTestBizModel(String bizName, String bizVersion, BizState bizState,\n                                              URL[] urls) {\n        BizModel bizModel = new BizModel().setBizState(bizState);\n        bizModel.setBizName(bizName).setBizVersion(bizVersion);\n        BizClassLoader bizClassLoader = new BizClassLoader(bizModel.getIdentity(), urls);\n        bizClassLoader.setBizModel(bizModel);\n        bizModel.setClassPath(urls).setClassLoader(bizClassLoader);\n        return bizModel;\n    }\n\n    public static BizModel createTestBizModel(String bizName, String bizVersion, BizState bizState,\n                                              ClassLoader classLoader) {\n        BizModel bizModel = new BizModel().setBizState(bizState);\n        bizModel.setBizName(bizName).setBizVersion(bizVersion);\n        bizModel.setClassLoader(classLoader);\n        return bizModel;\n    }\n\n    @Before\n    public void before() {\n\n        List<String> mockArguments = new ArrayList<>();\n        String filePath = this.getClass().getClassLoader().getResource(\"SampleClass.class\").getPath();\n        String workingPath = file(filePath).getParent();\n        mockArguments.add(String.format(\"javaaget:%s\", workingPath));\n        mockArguments.add(String.format(\"-javaagent:%s\", workingPath));\n        mockArguments.add(String.format(\"-javaagent:%s=xx\", workingPath));\n\n        RuntimeMXBean runtimeMXBean = mock(RuntimeMXBean.class);\n        when(runtimeMXBean.getInputArguments()).thenReturn(mockArguments);\n        when(runtimeMXBean.getName()).thenReturn(\"\");\n\n        managementFactoryMockedStatic = mockStatic(ManagementFactory.class);\n        managementFactoryMockedStatic.when(ManagementFactory::getRuntimeMXBean).thenReturn(runtimeMXBean);\n\n        arkServiceContainer.start();\n        arkServiceContainer.getService(RegisterServiceStage.class).process(null);\n        setExtensionLoaderService(arkServiceContainer.getService(ExtensionLoaderService.class));\n    }\n\n    @After\n    public void after() {\n\n        arkServiceContainer.stop();\n        if (arkContainer != null) {\n            arkContainer.stop();\n        }\n        managementFactoryMockedStatic.close();\n    }\n\n    @BeforeClass\n    public static void beforeClass() {\n    }\n\n    protected void registerMockPlugin() {\n        if (arkContainer == null) {\n            String[] args = new String[] { \"-Ajar=\" + jarURL.toExternalForm() };\n            arkContainer = (ArkContainer) ArkContainer.main(args);\n        }\n        PluginManagerService pluginManagerService = ArkServiceContainerHolder.getContainer()\n            .getService(PluginManagerService.class);\n        Plugin plugin = new PluginModel().setPluginName(\"mock\")\n            .setPluginClassLoader(this.getClass().getClassLoader()).setImportClasses(\"\")\n            .setImportPackages(\"\").setImportResources(\"\");\n        pluginManagerService.registerPlugin(plugin);\n    }\n\n    protected void registerMockBiz() {\n        if (arkContainer == null) {\n            String[] args = new String[] { \"-Ajar=\" + jarURL.toExternalForm() };\n            arkContainer = (ArkContainer) ArkContainer.main(args);\n        }\n        BizManagerService bizManagerService = ArkServiceContainerHolder.getContainer().getService(\n            BizManagerService.class);\n        Biz biz = new BizModel().setBizName(\"mock\").setBizVersion(\"1.0\")\n            .setClassLoader(this.getClass().getClassLoader()).setDenyImportPackages(\"\")\n            .setDenyImportClasses(\"\").setDenyImportResources(\"\").setBizState(BizState.RESOLVED);\n        bizManagerService.registerBiz(biz);\n        ((BizModel) biz).setBizState(BizState.ACTIVATED);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/ClassLoaderTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container;\n\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.common.util.EnvironmentUtils;\nimport com.alipay.sofa.ark.container.test.TestClassLoader;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.net.URLClassLoader;\n\n/**\n * @author qilong.zql\n * @since 3.1.0\n */\npublic class ClassLoaderTest extends BaseTest {\n\n    @Test\n    public void testDefaultDelegate() throws Throwable {\n        // compatible with JDK9+\n        ClassLoader classLoader = ClassLoader.getSystemClassLoader();\n        TestClassLoader testClassLoader = new TestClassLoader(\"mock:1.0\",\n            ClassLoaderUtils.getURLs(classLoader), classLoader);\n\n        Assert.assertEquals(classLoader, testClassLoader.loadClass(Test.class.getCanonicalName())\n            .getClassLoader());\n        Assert.assertEquals(testClassLoader,\n            testClassLoader.loadClass(ClassLoaderTest.class.getCanonicalName()).getClassLoader());\n    }\n\n    @Test\n    public void testDelegateConfigure() throws Throwable {\n        EnvironmentUtils.setProperty(Constants.FORCE_DELEGATE_TO_TEST_CLASSLOADER, Test.class\n            .getPackage().getName());\n        EnvironmentUtils.setProperty(Constants.FORCE_DELEGATE_TO_APP_CLASSLOADER,\n            ClassLoaderTest.class.getPackage().getName());\n\n        // compatible with JDK9+\n        ClassLoader classLoader = ClassLoader.getSystemClassLoader();\n        TestClassLoader testClassLoader = new TestClassLoader(\"mock:1.0\",\n            ClassLoaderUtils.getURLs(classLoader), classLoader);\n\n        Assert.assertEquals(testClassLoader,\n            testClassLoader.loadClass(Test.class.getCanonicalName()).getClassLoader());\n        Assert.assertEquals(classLoader,\n            testClassLoader.loadClass(ClassLoaderTest.class.getCanonicalName()).getClassLoader());\n\n        EnvironmentUtils.clearProperty(Constants.FORCE_DELEGATE_TO_APP_CLASSLOADER);\n        EnvironmentUtils.clearProperty(Constants.FORCE_DELEGATE_TO_TEST_CLASSLOADER);\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/model/BizModelTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.model;\n\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainer;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.container.service.event.EventAdminServiceImpl;\nimport com.alipay.sofa.ark.spi.event.biz.BeforeBizStopEvent;\nimport com.alipay.sofa.ark.spi.model.BizInfo.BizStateRecord;\nimport com.alipay.sofa.ark.spi.model.BizState;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.event.EventAdminService;\nimport org.junit.Test;\nimport org.mockito.MockedStatic;\nimport org.mockito.Mockito;\n\nimport java.io.File;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.AUTO_UNINSTALL_WHEN_FAILED_ENABLE;\nimport static com.alipay.sofa.ark.spi.constant.Constants.REMOVE_BIZ_INSTANCE_AFTER_STOP_FAILED;\nimport static org.apache.commons.io.FileUtils.touch;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertNull;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\npublic class BizModelTest {\n\n    @Test\n    public void testDoCheckDeclared() throws MalformedURLException {\n\n        BizModel bizModel = new BizModel();\n        assertEquals(new HashSet(), bizModel.setAttribute(\"a\", \"b\").setAttributes(new HashMap<>())\n            .getInjectExportPackages());\n        assertEquals(new HashSet(), bizModel.getInjectPluginDependencies());\n        bizModel.setCustomBizName(\"abc\");\n        assertNotNull(bizModel.getAttributes());\n        assertNull(bizModel.getBizTempWorkDir());\n        bizModel.toString();\n\n        bizModel.setPluginClassPath(new URL[] { new URL(\"file://b/a.jar!/\") });\n        assertTrue(bizModel.doCheckDeclared(\"file://b/a.jar!/b.jar\"));\n        assertTrue(bizModel.doCheckDeclared(this.getClass().getClassLoader()\n            .getResource(\"test.jar\").getPath()));\n    }\n\n    @Test\n    public void testBizStateChanged() {\n        BizModel bizModel = new BizModel();\n        bizModel.setBizName(\"biz1\");\n        bizModel.setBizVersion(\"0.0.1-SNAPSHOT\");\n        List<BizStateRecord> changeLogs = bizModel.getBizStateRecords();\n\n        assertEquals(0, changeLogs.size());\n\n        // create Biz\n        bizModel.setBizState(BizState.RESOLVED);\n        bizModel.setClassLoader(this.getClass().getClassLoader());\n        assertEquals(1, changeLogs.size());\n        assertTrue(bizModel.toString().contains(\"-> resolved\"));\n\n        // activate Biz\n        bizModel.setBizState(BizState.ACTIVATED);\n        assertEquals(2, changeLogs.size());\n        assertTrue(bizModel.toString().contains(\"-> resolved\"));\n        assertTrue(bizModel.toString().contains(\"-> activated\"));\n\n        // deactivate Biz\n        bizModel.setBizState(BizState.DEACTIVATED);\n        assertEquals(3, changeLogs.size());\n        assertTrue(bizModel.toString().contains(\"-> resolved\"));\n        assertTrue(bizModel.toString().contains(\"-> activated\"));\n        assertTrue(bizModel.toString().contains(\"-> deactivated\"));\n\n        bizModel.setBizState(BizState.UNRESOLVED);\n        assertEquals(4, changeLogs.size());\n        assertTrue(bizModel.toString().contains(\"-> resolved\"));\n        assertTrue(bizModel.toString().contains(\"-> activated\"));\n        assertTrue(bizModel.toString().contains(\"-> deactivated\"));\n        assertTrue(bizModel.toString().contains(\"-> unresolved\"));\n    }\n\n    @Test\n    public void testRecycleBizTempWorkDir() throws Throwable {\n        assertFalse(BizModel.recycleBizTempWorkDir(null));\n\n        File fileJar = new File(\"/tmp/\" + System.currentTimeMillis() + \".jar\");\n        touch(fileJar);\n\n        assertTrue(BizModel.recycleBizTempWorkDir(fileJar));\n        assertFalse(fileJar.exists());\n\n        File fileDir = new File(\"/tmp/\" + System.currentTimeMillis() + \"-test\");\n        fileDir.mkdir();\n        File fileSubFile = new File(fileDir.getAbsolutePath() + \"/subfile.jar\");\n        touch(fileSubFile);\n\n        assertTrue(BizModel.recycleBizTempWorkDir(fileDir));\n        assertFalse(fileDir.exists());\n        assertFalse(fileSubFile.exists());\n    }\n\n    @Test\n    public void testStopFailedWithClean() {\n        BizModel bizModel = new BizModel();\n        bizModel.setBizName(\"biz1\");\n        bizModel.setBizVersion(\"0.0.1-SNAPSHOT\");\n        bizModel.setBizState(BizState.ACTIVATED);\n\n        try (MockedStatic<ArkServiceContainerHolder> mockedStatic = Mockito.mockStatic(ArkServiceContainerHolder.class)) {\n            EventAdminService eventAdminService = mock(EventAdminServiceImpl.class);\n            BizManagerService bizManagerService = mock(BizManagerService.class);\n\n            ArkServiceContainer arkServiceContainer = mock(ArkServiceContainer.class);\n            doThrow(new RuntimeException()).when(eventAdminService).sendEvent(any(BeforeBizStopEvent.class));\n            when(arkServiceContainer.getService(EventAdminService.class)).thenReturn(eventAdminService);\n            when(arkServiceContainer.getService(BizManagerService.class)).thenReturn(bizManagerService);\n            mockedStatic.when(ArkServiceContainerHolder::getContainer).thenReturn(arkServiceContainer);\n            try {\n                bizModel.stop();\n            } catch (RuntimeException e) {\n            }\n            assertEquals(BizState.STOPPED, bizModel.getBizState());\n        }\n\n        bizModel.setBizState(BizState.ACTIVATED);\n        try (MockedStatic<ArkServiceContainerHolder> mockedStatic = Mockito.mockStatic(ArkServiceContainerHolder.class)) {\n            ArkConfigs.putStringValue(REMOVE_BIZ_INSTANCE_AFTER_STOP_FAILED, \"false\");\n            EventAdminService eventAdminService = mock(EventAdminServiceImpl.class);\n            BizManagerService bizManagerService = mock(BizManagerService.class);\n\n            ArkServiceContainer arkServiceContainer = mock(ArkServiceContainer.class);\n            doThrow(new RuntimeException()).when(eventAdminService).sendEvent(any(BeforeBizStopEvent.class));\n            when(arkServiceContainer.getService(EventAdminService.class)).thenReturn(eventAdminService);\n            when(arkServiceContainer.getService(BizManagerService.class)).thenReturn(bizManagerService);\n            mockedStatic.when(ArkServiceContainerHolder::getContainer).thenReturn(arkServiceContainer);\n            try {\n                bizModel.stop();\n            } catch (RuntimeException e) {\n            }\n            assertEquals(BizState.BROKEN, bizModel.getBizState());\n        } finally {\n            ArkConfigs.putStringValue(REMOVE_BIZ_INSTANCE_AFTER_STOP_FAILED, \"true\");\n        }\n    }\n\n    @Test\n    public void testStopSucceedWithClean() {\n        BizModel bizModel = new BizModel();\n        bizModel.setBizName(\"biz1\");\n        bizModel.setBizVersion(\"0.0.1-SNAPSHOT\");\n        bizModel.setBizState(BizState.ACTIVATED);\n\n        try (MockedStatic<ArkServiceContainerHolder> mockedStatic = Mockito.mockStatic(ArkServiceContainerHolder.class)) {\n            EventAdminService eventAdminService = mock(EventAdminService.class);\n            BizManagerService bizManagerService = mock(BizManagerService.class);\n\n            ArkServiceContainer arkServiceContainer = mock(ArkServiceContainer.class);\n            when(arkServiceContainer.getService(EventAdminService.class)).thenReturn(eventAdminService);\n            when(arkServiceContainer.getService(BizManagerService.class)).thenReturn(bizManagerService);\n            mockedStatic.when(ArkServiceContainerHolder::getContainer).thenReturn(arkServiceContainer);\n            bizModel.stop();\n\n            assertEquals(BizState.STOPPED, bizModel.getBizState());\n        }\n\n        bizModel.setBizState(BizState.ACTIVATED);\n        try (MockedStatic<ArkServiceContainerHolder> mockedStatic = Mockito.mockStatic(ArkServiceContainerHolder.class)) {\n            ArkConfigs.putStringValue(REMOVE_BIZ_INSTANCE_AFTER_STOP_FAILED, \"false\");\n            EventAdminService eventAdminService = mock(EventAdminService.class);\n            BizManagerService bizManagerService = mock(BizManagerService.class);\n\n            ArkServiceContainer arkServiceContainer = mock(ArkServiceContainer.class);\n            when(arkServiceContainer.getService(EventAdminService.class)).thenReturn(eventAdminService);\n            when(arkServiceContainer.getService(BizManagerService.class)).thenReturn(bizManagerService);\n            mockedStatic.when(ArkServiceContainerHolder::getContainer).thenReturn(arkServiceContainer);\n            bizModel.stop();\n\n            assertEquals(BizState.STOPPED, bizModel.getBizState());\n        } finally {\n            ArkConfigs.putStringValue(AUTO_UNINSTALL_WHEN_FAILED_ENABLE, \"true\");\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/pipeline/HandleArchiveStageTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.pipeline;\n\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.loader.DirectoryBizArchive;\nimport com.alipay.sofa.ark.loader.JarBizArchive;\nimport com.alipay.sofa.ark.spi.archive.BizArchive;\nimport com.alipay.sofa.ark.spi.archive.ExecutableArchive;\nimport com.alipay.sofa.ark.spi.archive.PluginArchive;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.pipeline.PipelineContext;\nimport com.alipay.sofa.ark.spi.service.biz.BizFactoryService;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginFactoryService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.lang.reflect.Field;\nimport java.util.HashSet;\nimport java.util.jar.Attributes;\nimport java.util.jar.Manifest;\n\nimport static com.alipay.sofa.ark.api.ArkConfigs.getStringValue;\nimport static com.alipay.sofa.ark.api.ArkConfigs.setSystemProperty;\nimport static com.alipay.sofa.ark.spi.constant.Constants.CONFIG_SERVER_ADDRESS;\nimport static com.alipay.sofa.ark.spi.constant.Constants.MASTER_BIZ;\nimport static java.util.Arrays.asList;\nimport static org.mockito.Mockito.*;\n\npublic class HandleArchiveStageTest {\n\n    private String               originalConfigServerAddress;\n\n    private String               originalMasterBiz;\n\n    private BizFactoryService    bizFactoryService    = mock(BizFactoryService.class);\n\n    private BizManagerService    bizManagerService    = mock(BizManagerService.class);\n\n    private PluginFactoryService pluginFactoryService = mock(PluginFactoryService.class);\n\n    private PluginManagerService pluginManagerService = mock(PluginManagerService.class);\n\n    private PipelineContext      pipelineContext      = mock(PipelineContext.class);\n\n    private ExecutableArchive    executableArchive    = mock(ExecutableArchive.class);\n\n    private HandleArchiveStage   handleArchiveStage   = new HandleArchiveStage();\n\n    @Before\n    public void before() throws Exception {\n\n        originalConfigServerAddress = getStringValue(CONFIG_SERVER_ADDRESS);\n        originalMasterBiz = getStringValue(MASTER_BIZ);\n        when(pipelineContext.getExecutableArchive()).thenReturn(executableArchive);\n\n        Field field = HandleArchiveStage.class.getDeclaredField(\"bizFactoryService\");\n        field.setAccessible(true);\n        field.set(handleArchiveStage, bizFactoryService);\n\n        field = HandleArchiveStage.class.getDeclaredField(\"bizManagerService\");\n        field.setAccessible(true);\n        field.set(handleArchiveStage, bizManagerService);\n\n        field = HandleArchiveStage.class.getDeclaredField(\"pluginFactoryService\");\n        field.setAccessible(true);\n        field.set(handleArchiveStage, pluginFactoryService);\n\n        field = HandleArchiveStage.class.getDeclaredField(\"pluginManagerService\");\n        field.setAccessible(true);\n        field.set(handleArchiveStage, pluginManagerService);\n    }\n\n    @After\n    public void after() {\n        setSystemProperty(CONFIG_SERVER_ADDRESS,\n            originalConfigServerAddress != null ? originalConfigServerAddress : \"\");\n        setSystemProperty(MASTER_BIZ, originalMasterBiz != null ? originalMasterBiz : \"'\");\n        clearInvocations(bizFactoryService, bizManagerService, pluginFactoryService,\n            pluginManagerService);\n    }\n\n    @Test\n    public void testProcess() throws Exception {\n\n        Manifest manifest = mock(Manifest.class);\n        BizArchive bizArchive1 = mock(DirectoryBizArchive.class);\n        when(bizArchive1.getManifest()).thenReturn(manifest);\n        BizArchive bizArchive2 = mock(JarBizArchive.class);\n        when(bizArchive2.getManifest()).thenReturn(manifest);\n        when(manifest.getMainAttributes()).thenReturn(mock(Attributes.class));\n        BizArchive bizArchive3 = mock(BizArchive.class);\n        when(bizArchive3.getManifest()).thenReturn(manifest);\n        BizArchive bizArchive4 = mock(BizArchive.class);\n        when(bizArchive4.getManifest()).thenReturn(manifest);\n        when(executableArchive.getBizArchives()).thenReturn(\n            asList(bizArchive1, bizArchive2, bizArchive3, bizArchive4));\n\n        BizModel bizModel1 = new BizModel();\n        bizModel1.setBizName(\"a\");\n        when(bizFactoryService.createBiz(bizArchive1)).thenReturn(bizModel1);\n        when(bizFactoryService.createBiz(bizArchive2)).thenReturn(bizModel1);\n        when(bizFactoryService.createBiz(bizArchive3)).thenReturn(bizModel1);\n        BizModel bizModel2 = new BizModel();\n        bizModel2.setBizName(\"b\");\n        when(bizFactoryService.createBiz(bizArchive4)).thenReturn(bizModel2);\n\n        PluginArchive pluginArchive = mock(PluginArchive.class);\n        when(executableArchive.getPluginArchives()).thenReturn(asList(pluginArchive));\n        when(bizManagerService.getBizInOrder()).thenReturn(asList(bizModel1));\n\n        Plugin plugin = mock(Plugin.class);\n        when(pluginFactoryService.createPlugin(pluginArchive, null, new HashSet<>())).thenReturn(\n            plugin);\n\n        setSystemProperty(CONFIG_SERVER_ADDRESS, \"localhost\");\n        setSystemProperty(MASTER_BIZ, \"a\");\n        handleArchiveStage.process(pipelineContext);\n\n        verify(bizFactoryService, times(4)).createBiz((BizArchive) any());\n        verify(bizManagerService, times(3)).registerBiz(any());\n        verify(bizManagerService, times(1)).getBizInOrder();\n        verify(pluginFactoryService, times(1)).createPlugin(pluginArchive, null, new HashSet<>());\n        verify(pluginManagerService, times(1)).registerPlugin(plugin);\n\n        when(executableArchive.getBizArchives()).thenReturn(asList(bizArchive3));\n        setSystemProperty(CONFIG_SERVER_ADDRESS, \"\");\n        setSystemProperty(MASTER_BIZ, \"\");\n        handleArchiveStage.process(pipelineContext);\n\n        verify(bizFactoryService, times(5)).createBiz((BizArchive) any());\n        verify(bizManagerService, times(4)).registerBiz(any());\n        verify(bizManagerService, times(2)).getBizInOrder();\n    }\n\n    @Test\n    public void testProcessStaticBizFromClasspath() throws Exception {\n\n        BizArchive bizArchive = mock(BizArchive.class);\n        when(executableArchive.getBizArchives()).thenReturn(asList(bizArchive));\n\n        handleArchiveStage.processStaticBizFromClasspath(pipelineContext);\n        verify(bizFactoryService, times(1)).createBiz((bizArchive));\n        verify(bizManagerService, times(1)).registerBiz(null);\n    }\n\n    @Test\n    public void testProcessEmbed() throws Exception {\n\n        PluginArchive pluginArchive = mock(PluginArchive.class);\n        when(executableArchive.getPluginArchives()).thenReturn(asList(pluginArchive));\n\n        Plugin plugin = mock(Plugin.class);\n        when(\n            pluginFactoryService.createEmbedPlugin(pluginArchive, pipelineContext.getClass()\n                .getClassLoader())).thenReturn(plugin);\n\n        BizModel bizModel = new BizModel();\n        bizModel.setBizName(\"a\");\n        when(bizFactoryService.createEmbedMasterBiz(pipelineContext.getClass().getClassLoader()))\n            .thenReturn(bizModel);\n\n        handleArchiveStage.processEmbed(pipelineContext);\n        verify(pluginFactoryService, times(1)).createEmbedPlugin(pluginArchive,\n            pipelineContext.getClass().getClassLoader());\n        verify(pluginManagerService, times(1)).registerPlugin(plugin);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/ArkServiceContainerTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service;\n\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.container.pipeline.StandardPipeline;\nimport com.alipay.sofa.ark.spi.pipeline.Pipeline;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author ruoshan\n * @since 0.1.0\n */\npublic class ArkServiceContainerTest extends BaseTest {\n    @Test\n    public void testStart() {\n        arkServiceContainer.start();\n        Assert.assertTrue(arkServiceContainer.isStarted());\n        Assert.assertTrue(arkServiceContainer.isRunning());\n    }\n\n    @Test\n    public void testStop() {\n        arkServiceContainer.start();\n        arkServiceContainer.stop();\n        Assert.assertFalse(arkServiceContainer.isRunning());\n    }\n\n    @Test\n    public void testGetService() {\n        arkServiceContainer.start();\n        Pipeline pipeline = arkServiceContainer.getService(Pipeline.class);\n        Assert.assertNotNull(pipeline);\n        Assert.assertTrue(pipeline instanceof StandardPipeline);\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/api/ArkClientTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.api;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.api.ClientResponse;\nimport com.alipay.sofa.ark.common.util.FileUtils;\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.container.service.biz.BizManagerServiceImpl;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.loader.JarBizArchive;\nimport com.alipay.sofa.ark.loader.archive.JarFileArchive;\nimport com.alipay.sofa.ark.loader.jar.JarFile;\nimport com.alipay.sofa.ark.spi.archive.BizArchive;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.event.ArkEvent;\nimport com.alipay.sofa.ark.spi.model.*;\nimport com.alipay.sofa.ark.spi.replay.Replay;\nimport com.alipay.sofa.ark.spi.service.biz.BizFactoryService;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.event.EventAdminService;\nimport com.alipay.sofa.ark.spi.service.event.EventHandler;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static com.alipay.sofa.ark.api.ArkClient.*;\nimport static com.alipay.sofa.ark.api.ResponseCode.*;\nimport static com.alipay.sofa.ark.common.util.FileUtils.copyInputStreamToFile;\nimport static com.alipay.sofa.ark.spi.constant.Constants.ACTIVATE_NEW_MODULE;\nimport static com.alipay.sofa.ark.spi.constant.Constants.AUTO_UNINSTALL_WHEN_FAILED_ENABLE;\nimport static com.alipay.sofa.ark.spi.constant.Constants.CONFIG_BIZ_URL;\nimport static com.alipay.sofa.ark.spi.constant.Constants.EMBED_ENABLE;\nimport static com.alipay.sofa.ark.spi.constant.Constants.ACTIVATE_MULTI_BIZ_VERSION_ENABLE;\nimport static com.alipay.sofa.ark.spi.model.BizOperation.OperationType.CHECK;\nimport static com.alipay.sofa.ark.spi.model.BizOperation.OperationType.INSTALL;\nimport static com.alipay.sofa.ark.spi.model.BizOperation.OperationType.SWITCH;\nimport static com.alipay.sofa.ark.spi.model.BizOperation.OperationType.UNINSTALL;\nimport static com.alipay.sofa.ark.spi.model.BizState.ACTIVATED;\nimport static com.alipay.sofa.ark.spi.model.BizState.DEACTIVATED;\nimport static com.alipay.sofa.ark.spi.model.BizState.RESOLVED;\nimport static java.lang.System.setProperty;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertSame;\nimport static org.junit.Assert.assertTrue;\nimport static org.junit.Assert.fail;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class ArkClientTest extends BaseTest {\n\n    // bizName=biz-demo, bizVersion=1.0.0\n    private URL bizUrl1;\n    // bizName=biz-demo, bizVersion=2.0.0\n    private URL bizUrl2;\n    // bizName=biz-demo, bizVersion=3.0.0\n    private URL bizUrl3;\n    // bizName=biz-demo, bizVersion=4.0.0\n    private URL bizUrl4;\n    // bizName=biz-demo, bizVersion=5.0.0\n    private URL bizUrl5;\n    // samplePlugin\n    private URL samplePlugin;\n\n    @Before\n    public void before() {\n        super.before();\n        // bizName=biz-demo, bizVersion=1.0.0\n        bizUrl1 = this.getClass().getClassLoader().getResource(\"sample-ark-1.0.0-ark-biz.jar\");\n        // bizName=biz-demo, bizVersion=2.0.0\n        bizUrl2 = this.getClass().getClassLoader().getResource(\"sample-ark-2.0.0-ark-biz.jar\");\n        // bizName=biz-demo, bizVersion=3.0.0\n        bizUrl3 = this.getClass().getClassLoader().getResource(\"sample-ark-3.0.0-ark-biz.jar\");\n        // bizName=biz-demo, bizVersion=4.0.0\n        bizUrl4 = this.getClass().getClassLoader().getResource(\"sample-ark-4.0.0-ark-biz.jar\");\n        // bizName=biz-demo, bizVersion=5.0.0\n        bizUrl5 = this.getClass().getClassLoader().getResource(\"sample-ark-5.0.0-ark-biz.jar\");\n        // samplePlugin\n        samplePlugin = this.getClass().getClassLoader().getResource(\"sample-plugin.jar\");\n    }\n\n    @Test\n    public void testCreateBizSaveFile() {\n        File bizFile = createBizSaveFile(\"test-biz-demo\", \"1.0.0\", \"suffix\");\n        assertTrue(bizFile.getName().contains(\"test-biz-demo-1.0.0-suffix\"));\n    }\n\n    @Test\n    public void testInstallBiz() throws Throwable {\n\n        ClientResponse response = checkBiz();\n        assertEquals(SUCCESS, response.getCode());\n        assertEquals(0, response.getBizInfos().size());\n\n        // test install\n        File bizFile = createBizSaveFile(\"biz-demo\", \"1.0.0\");\n        copyInputStreamToFile(bizUrl1.openStream(), bizFile);\n        response = installBiz(bizFile);\n\n        assertEquals(SUCCESS, response.getCode());\n        BizInfo bizInfo = response.getBizInfos().iterator().next();\n        assertEquals(ACTIVATED, bizInfo.getBizState());\n\n        // test install biz with same bizName and bizVersion\n        // test install\n        File bizFile1 = createBizSaveFile(\"biz-demo\", \"1.0.0\");\n        copyInputStreamToFile(bizUrl1.openStream(), bizFile1);\n        response = installBiz(bizFile1);\n        assertEquals(REPEAT_BIZ, response.getCode());\n\n        // test install biz with same bizName and different bizVersion\n        //        response = ArkClient.installBiz(new File(bizUrl2.getFile()));\n        File bizFile2 = createBizSaveFile(\"biz-demo\", \"2.0.0\");\n        copyInputStreamToFile(bizUrl2.openStream(), bizFile2);\n        response = installBiz(bizFile2);\n        assertEquals(SUCCESS, response.getCode());\n        bizInfo = response.getBizInfos().iterator().next();\n        assertEquals(DEACTIVATED, bizInfo.getBizState());\n\n        // test install biz with same bizName and different bizVersion and active latest\n        setProperty(ACTIVATE_NEW_MODULE, \"true\");\n        setProperty(EMBED_ENABLE, \"true\");\n        File bizFile3 = createBizSaveFile(\"biz-demo\", \"3.0.0\");\n        copyInputStreamToFile(bizUrl3.openStream(), bizFile3);\n        response = installBiz(bizFile3);\n        setProperty(ACTIVATE_NEW_MODULE, \"\");\n        setProperty(EMBED_ENABLE, \"\");\n        assertEquals(SUCCESS, response.getCode());\n        bizInfo = response.getBizInfos().iterator().next();\n        assertEquals(ACTIVATED, bizInfo.getBizState());\n\n        // test install biz with same bizName and different bizVersion and keep old module state\n        setProperty(ACTIVATE_MULTI_BIZ_VERSION_ENABLE, \"true\");\n        File bizFile4 = createBizSaveFile(\"biz-demo\", \"4.0.0\");\n        copyInputStreamToFile(bizUrl4.openStream(), bizFile4);\n        response = installBiz(bizFile4);\n        assertEquals(SUCCESS, response.getCode());\n        BizManagerService bizManagerService = arkServiceContainer\n            .getService(BizManagerService.class);\n        assertSame(bizManagerService.getBiz(\"biz-demo\", \"3.0.0\").getBizState(), ACTIVATED);\n        setProperty(ACTIVATE_MULTI_BIZ_VERSION_ENABLE, \"\");\n    }\n\n    @Test\n    public void testBizArguments() throws Throwable {\n\n        EventAdminService eventAdminService = arkServiceContainer\n            .getService(EventAdminService.class);\n        List<String> topicList = new ArrayList<>();\n\n        EventHandler eventHandler = new EventHandler<ArkEvent>() {\n            @Override\n            public void handleEvent(ArkEvent event) {\n                topicList.add(event.getTopic());\n            }\n\n            @Override\n            public int getPriority() {\n                return 0;\n            }\n        };\n        eventAdminService.register(eventHandler);\n\n        File bizFile3 = createBizSaveFile(\"biz-demo\", \"3.0.0\");\n        copyInputStreamToFile(bizUrl3.openStream(), bizFile3);\n        installBiz(bizFile3);\n        //        ArkClient.installBiz(new File(bizUrl3.getFile()));\n        uninstallBiz(\"biz-demo\", \"3.0.0\");\n\n        File bizFile33 = createBizSaveFile(\"biz-demo\", \"3.0.0\");\n        copyInputStreamToFile(bizUrl3.openStream(), bizFile33);\n        installBiz(bizFile33, new String[] { \"demo\" });\n        uninstallBiz(\"biz-demo\", \"3.0.0\");\n        assertEquals(\"BEFORE-INVOKE-BIZ-START\", topicList.get(0));\n        assertEquals(\"No arguments\", topicList.get(1));\n        assertEquals(\"AFTER-INVOKE-BIZ-START\", topicList.get(2));\n        // new event\n        assertEquals(\"BEFORE-RECYCLE-BIZ\", topicList.get(4));\n        assertEquals(\"demo\", topicList.get(7));\n    }\n\n    @Test\n    public void testCheckBiz() throws Throwable {\n\n        testInstallBiz();\n        // test check all biz\n        ClientResponse response = checkBiz();\n        assertEquals(SUCCESS, response.getCode());\n        assertEquals(4, response.getBizInfos().size());\n\n        // test check specified bizName\n        response = checkBiz(\"biz-demo\");\n        assertEquals(SUCCESS, response.getCode());\n        assertEquals(4, response.getBizInfos().size());\n\n        // test check specified bizName and version\n        response = checkBiz(\"biz-demo\", \"2.0.0\");\n        assertEquals(SUCCESS, response.getCode());\n        assertEquals(1, response.getBizInfos().size());\n        response = checkBiz(\"biz-demo\", \"3.0.0\");\n        assertEquals(SUCCESS, response.getCode());\n        assertEquals(1, response.getBizInfos().size());\n\n        response = checkBiz(\"biz-demo\", \"4.0.0\");\n        assertEquals(SUCCESS, response.getCode());\n        assertEquals(1, response.getBizInfos().size());\n\n        response = checkBiz(\"biz-demo\", \"5.0.0\");\n        assertEquals(SUCCESS, response.getCode());\n        assertEquals(0, response.getBizInfos().size());\n    }\n\n    @Test\n    public void testUninstallBiz() throws Throwable {\n\n        testCheckBiz();\n        // test uninstall biz\n        ClientResponse response = uninstallBiz(\"biz-demo\", \"1.0.0\");\n        assertEquals(SUCCESS, response.getCode());\n\n        // test check all biz\n        response = checkBiz();\n        assertEquals(SUCCESS, response.getCode());\n        assertEquals(3, response.getBizInfos().size());\n    }\n\n    @Test\n    public void testUninstallBizWhenIncludeLib() throws Throwable {\n\n        testCheckBiz();\n        // test uninstall biz\n        ClientResponse response = uninstallBiz(\"biz-demo\", \"3.0.0\");\n        assertEquals(SUCCESS, response.getCode());\n\n        // test check all biz\n        response = checkBiz();\n        assertEquals(SUCCESS, response.getCode());\n        assertEquals(3, response.getBizInfos().size());\n    }\n\n    @Test\n    public void testInstallBizWithThrowable() throws Throwable {\n\n        File bizFile = createBizSaveFile(\"biz-demo\", \"1.0.0\");\n        copyInputStreamToFile(bizUrl1.openStream(), bizFile);\n        BizFactoryService bizFactoryService = getBizFactoryService();\n        BizFactoryService bizFactoryServiceMock = mock(BizFactoryService.class);\n        setBizFactoryService(bizFactoryServiceMock);\n        Biz biz = mock(Biz.class);\n        doThrow(new IllegalArgumentException()).when(biz).start(any());\n        when(bizFactoryServiceMock.createBiz((File) any(), (BizConfig) any())).thenReturn(biz);\n\n        try {\n            installBiz(bizFile, new BizConfig());\n            assertTrue(false);\n        } catch (Throwable e) {\n            setBizFactoryService(bizFactoryService);\n            assertEquals(IllegalArgumentException.class, e.getClass());\n        }\n\n        assertNotNull(getBizManagerService());\n        assertNotNull(getBizFactoryService());\n        assertNotNull(getPluginManagerService());\n        assertNotNull(getArguments());\n    }\n\n    @Test\n    public void testInstallOperation() throws Throwable {\n\n        BizOperation bizOperation = new BizOperation();\n        bizOperation.setOperationType(INSTALL);\n        bizOperation.getParameters().put(CONFIG_BIZ_URL, bizUrl1.toString());\n        bizOperation.setBizName(\"biz-demo\");\n        bizOperation.setBizVersion(\"1.0.0\");\n\n        ClientResponse response = installOperation(bizOperation, new String[] {});\n        assertEquals(SUCCESS, response.getCode());\n    }\n\n    @Test\n    public void testInstallOperationWithDynamicMainClass() throws Throwable {\n\n        // the biz module will start with dynamic mainClass specified in env parameters, which is org.example.Main2\n        BizOperation bizOperation = new BizOperation();\n        bizOperation.setOperationType(INSTALL);\n        bizOperation.getParameters().put(CONFIG_BIZ_URL, bizUrl5.toString());\n        bizOperation.setBizName(\"biz-demo\");\n        bizOperation.setBizVersion(\"5.0.0\");\n\n        Map<String, String> envs = Collections.singletonMap(Constants.BIZ_MAIN_CLASS,\n            \"org.example.Main2\");\n\n        ClientResponse response2 = installOperation(bizOperation, new String[] {}, envs);\n        assertEquals(SUCCESS, response2.getCode());\n        assertEquals(\"org.example.Main2\", (new ArrayList<>(response2.getBizInfos())).get(0)\n            .getMainClass());\n\n        // but in fact, the biz module was packaged with mainClass as org.example.Main1\n        URL url = new URL(bizOperation.getParameters().get(Constants.CONFIG_BIZ_URL));\n        File file = ArkClient.createBizSaveFile(bizOperation.getBizName(),\n            bizOperation.getBizVersion());\n        try (InputStream inputStream = url.openStream()) {\n            FileUtils.copyInputStreamToFile(inputStream, file);\n        }\n        JarFile bizFile = new JarFile(file);\n        JarFileArchive jarFileArchive = new JarFileArchive(bizFile);\n        BizArchive bizArchive = new JarBizArchive(jarFileArchive);\n        assertEquals(\"org.example.Main1\",\n            bizArchive.getManifest().getMainAttributes().getValue(Constants.MAIN_CLASS_ATTRIBUTE));\n        assertEquals(\"org.example.Main1\",\n            bizArchive.getManifest().getMainAttributes().getValue(Constants.START_CLASS_ATTRIBUTE));\n    }\n\n    @Test\n    public void testInstallBizFailed() throws Throwable {\n        File bizFile = createBizSaveFile(\"biz-install-failed-demo\", \"1.0.0\");\n        copyInputStreamToFile(bizUrl1.openStream(), bizFile);\n        BizFactoryService bizFactoryService = getBizFactoryService();\n        BizFactoryService bizFactoryServiceMock = mock(BizFactoryService.class);\n        BizManagerService bizManagerService = getBizManagerService();\n        BizManagerServiceImpl bizManagerServiceMock = new BizManagerServiceImpl();\n\n        Biz biz = mock(Biz.class);\n        when(biz.getIdentity()).thenReturn(\"biz-install-failed-demo:1.0.0\");\n        when(biz.getBizState()).thenReturn(RESOLVED);\n        when(biz.getBizName()).thenReturn(\"biz-install-failed-demo\");\n        when(biz.getBizVersion()).thenReturn(\"1.0.0\");\n        doThrow(new IllegalArgumentException()).when(biz).start(any(), any());\n        when(bizFactoryServiceMock.createBiz((File) any(), (BizConfig) any())).thenReturn(biz);\n\n        // case1: not set AUTO_UNINSTALL_ENABLE\n        try {\n            setBizFactoryService(bizFactoryServiceMock);\n            setBizManagerService(bizManagerServiceMock);\n            doThrow(new Exception()).when(biz).stop();\n\n            installBiz(bizFile, new BizConfig());\n            fail();\n        } catch (Throwable e) {\n            assertFalse(bizManagerServiceMock.getBiz(\"biz-install-failed-demo\").isEmpty());\n        } finally {\n            setBizFactoryService(bizFactoryService);\n            setBizManagerService(bizManagerService);\n        }\n\n        // case2: set AUTO_UNINSTALL_ENABLE=false\n        try {\n            ArkConfigs.putStringValue(AUTO_UNINSTALL_WHEN_FAILED_ENABLE, \"false\");\n            setBizFactoryService(bizFactoryServiceMock);\n            setBizManagerService(bizManagerServiceMock);\n\n            installBiz(bizFile, new BizConfig());\n            fail();\n        } catch (Throwable e) {\n            assertFalse(bizManagerServiceMock.getBiz(\"biz-install-failed-demo\").isEmpty());\n            setBizFactoryService(bizFactoryService);\n            setBizManagerService(bizManagerService);\n        } finally {\n            ArkConfigs.putStringValue(AUTO_UNINSTALL_WHEN_FAILED_ENABLE, \"true\");\n        }\n    }\n\n    @Test\n    public void testUninstallOperation() throws Throwable {\n\n        BizOperation bizOperation = new BizOperation();\n        bizOperation.setOperationType(INSTALL);\n        bizOperation.getParameters().put(CONFIG_BIZ_URL, bizUrl1.toString());\n        bizOperation.setBizName(\"biz-demo\");\n        bizOperation.setBizVersion(\"1.0.0\");\n        installOperation(bizOperation, new String[] {});\n\n        bizOperation.setOperationType(UNINSTALL);\n        ClientResponse response = uninstallOperation(bizOperation);\n        assertEquals(SUCCESS, response.getCode());\n    }\n\n    @Test\n    public void testSwitchOperation() throws Throwable {\n\n        BizOperation bizOperation = new BizOperation();\n        bizOperation.setOperationType(INSTALL);\n        bizOperation.getParameters().put(CONFIG_BIZ_URL, bizUrl1.toString());\n        bizOperation.setBizName(\"biz-demo\");\n        bizOperation.setBizVersion(\"1.0.0\");\n        installOperation(bizOperation, new String[] {});\n\n        bizOperation.setOperationType(SWITCH);\n        ClientResponse response = switchOperation(bizOperation);\n        assertEquals(SUCCESS, response.getCode());\n    }\n\n    @Test\n    public void testCheckOperation() throws Throwable {\n\n        BizOperation bizOperation = new BizOperation();\n        bizOperation.setOperationType(INSTALL);\n        bizOperation.getParameters().put(CONFIG_BIZ_URL, bizUrl1.toString());\n        bizOperation.setBizName(\"biz-demo\");\n        bizOperation.setBizVersion(\"1.0.0\");\n        installOperation(bizOperation, new String[] {});\n\n        bizOperation.setOperationType(CHECK);\n        ClientResponse response = checkOperation(bizOperation);\n        assertEquals(SUCCESS, response.getCode());\n\n        bizOperation.setBizVersion(\"2.0.0\");\n        response = checkOperation(bizOperation);\n        assertEquals(SUCCESS, response.getCode());\n    }\n\n    @Test\n    public void testInvocationReplay() throws Throwable {\n        assertEquals(\"1\", invocationReplay(\"1.0.0\", new Replay() {\n            @Override\n            public Object invoke() {\n                return \"1\";\n            }\n        }));\n    }\n\n    @Test\n    public void testInstallPlugin() throws Throwable {\n        PluginOperation pluginOperation = new PluginOperation();\n        pluginOperation.setPluginName(\"plugin-demo\");\n        ClientResponse clientResponse = installPlugin(pluginOperation);\n        assertEquals(FAILED, clientResponse.getCode());\n\n        pluginOperation.setLocalFile(new File(samplePlugin.toURI()));\n        try {\n            installPlugin(pluginOperation);\n        } catch (Exception exception) {\n            assertTrue(exception instanceof ArkRuntimeException);\n        }\n\n        clientResponse = checkPlugin();\n        assertEquals(1, clientResponse.getPluginInfos().size());\n        clientResponse = checkPlugin(\"plugin-demo\");\n        assertEquals(1, clientResponse.getPluginInfos().size());\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/biz/BizCommandProviderTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.biz;\n\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.container.service.biz.BizCommandProvider.BizCommand;\nimport com.alipay.sofa.ark.container.session.handler.ArkCommandHandler;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.BizState;\nimport com.alipay.sofa.ark.spi.service.biz.BizDeployService;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.injection.InjectionService;\nimport org.junit.Test;\n\nimport java.net.MalformedURLException;\nimport java.net.URL;\n\nimport static com.alipay.sofa.ark.api.ArkConfigs.putStringValue;\nimport static com.alipay.sofa.ark.container.service.biz.BizCommandProvider.HELP_MESSAGE;\nimport static com.alipay.sofa.ark.spi.constant.Constants.MASTER_BIZ;\nimport static com.alipay.sofa.ark.spi.model.BizState.ACTIVATED;\nimport static com.alipay.sofa.ark.spi.model.BizState.DEACTIVATED;\nimport static org.junit.Assert.*;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class BizCommandProviderTest extends BaseTest {\n\n    private BizManagerService  bizManagerService;\n    private InjectionService   injectionService;\n    private BizCommandProvider bizCommandProvider;\n    private BizDeployService   bizDeployService;\n\n    @Override\n    public void before() {\n\n        super.before();\n        bizManagerService = arkServiceContainer.getService(BizManagerService.class);\n        injectionService = arkServiceContainer.getService(InjectionService.class);\n        bizDeployService = arkServiceContainer.getService(BizDeployService.class);\n\n        mockBiz();\n\n        bizDeployService.deploy(new String[] {});\n        bizCommandProvider = new BizCommandProvider();\n        injectionService.inject(bizCommandProvider);\n\n        // trigger telnet command thread pool to be created\n        new ArkCommandHandler();\n    }\n\n    @Test\n    public void testBizCommandPattern() {\n\n        assertFalse(bizCommandProvider.validate(\"biz\"));\n        assertFalse(bizCommandProvider.validate(\"biz -m\"));\n        assertFalse(bizCommandProvider.validate(\"biz -d\"));\n        assertFalse(bizCommandProvider.validate(\"biz -s\"));\n        assertFalse(bizCommandProvider.validate(\"biz -i\"));\n        assertFalse(bizCommandProvider.validate(\"biz -u\"));\n        assertFalse(bizCommandProvider.validate(\"biz -o\"));\n        assertTrue(bizCommandProvider.validate(\"biz -a\"));\n        assertTrue(bizCommandProvider.validate(\"biz -h\"));\n\n        assertFalse(bizCommandProvider.validate(\"biz -ah\"));\n        assertFalse(bizCommandProvider.validate(\"biz -am A1:V1\"));\n        assertFalse(bizCommandProvider.validate(\"biz -hm A1:V1\"));\n        assertFalse(bizCommandProvider.validate(\"biz -mi A1:V1\"));\n        assertFalse(bizCommandProvider.validate(\"biz -mu A1:V1\"));\n        assertFalse(bizCommandProvider.validate(\"biz -mo A1:V1\"));\n        assertTrue(bizCommandProvider.validate(\"biz -msd A1:V1\"));\n        assertTrue(bizCommandProvider.validate(\"biz -msd A1:V1 A2:V2\"));\n\n        assertFalse(bizCommandProvider.validate(\"biz -io A1:V1\"));\n\n        assertFalse(bizCommandProvider.validate(\"biz -i A1:V1 A2:V2\"));\n        assertTrue(bizCommandProvider.validate(\"biz -i A1:V1\"));\n\n        assertFalse(bizCommandProvider.validate(\"biz -u A1:V1 A2:V2\"));\n        assertTrue(bizCommandProvider.validate(\"biz -u A1:V1\"));\n\n        assertFalse(bizCommandProvider.validate(\"biz -o A1:V1 A2:V2\"));\n        assertTrue(bizCommandProvider.validate(\"biz -o A1:V1\"));\n    }\n\n    @Test\n    public void testBizInfo() {\n\n        String multiBizInfo = bizCommandProvider.handleCommand(\"biz -m A1:V1 A1:V2\");\n        String multiOptionBizInfo = bizCommandProvider.handleCommand(\"biz -md A1:V1 B1:V1\");\n\n        assertTrue(multiBizInfo.contains(\"MainClassA1\"));\n        assertTrue(multiBizInfo.contains(\"MainClassA2\"));\n        assertFalse(multiBizInfo.contains(\"ClassLoader\"));\n        assertFalse(multiBizInfo.contains(\"ClassPath\"));\n        assertFalse(multiBizInfo.contains(\"MainClassB1\"));\n\n        assertTrue(multiOptionBizInfo.contains(\"MainClassA1\"));\n        assertTrue(multiOptionBizInfo.contains(\"MainClassB1\"));\n        assertFalse(multiOptionBizInfo.contains(\"MainClassA2\"));\n        assertTrue(multiOptionBizInfo.contains(\"ClassLoader\"));\n        assertTrue(multiOptionBizInfo.contains(\"ClassPath\"));\n    }\n\n    @Test\n    public void testInstallBiz() {\n        String msg = bizCommandProvider.handleCommand(\"biz -i C1:V1\");\n        assertTrue(msg.contains(\"Exists some biz\"));\n\n        ((MockBiz) bizManagerService.getBizByIdentity(\"A1:V1\")).setBizState(ACTIVATED);\n        ((MockBiz) bizManagerService.getBizByIdentity(\"A1:V2\")).setBizState(DEACTIVATED);\n        ((MockBiz) bizManagerService.getBizByIdentity(\"B1:V1\")).setBizState(ACTIVATED);\n\n        msg = bizCommandProvider.handleCommand(\"biz -i C1:V1\");\n        assertTrue(msg.contains(\"Start to process install command now, pls wait and check.\"));\n    }\n\n    @Test\n    public void testSwitchBiz() {\n\n        Biz bizA1 = ((MockBiz) bizManagerService.getBizByIdentity(\"A1:V1\")).setBizState(ACTIVATED);\n        Biz bizA2 = ((MockBiz) bizManagerService.getBizByIdentity(\"A1:V2\"))\n            .setBizState(DEACTIVATED);\n        Biz bizB1 = ((MockBiz) bizManagerService.getBizByIdentity(\"B1:V1\")).setBizState(ACTIVATED);\n        bizCommandProvider.handleCommand(\"biz -o A1:V2\");\n\n        sleep(200);\n\n        assertTrue(bizA1.getBizState().equals(DEACTIVATED));\n        assertTrue(bizA2.getBizState().equals(ACTIVATED));\n        assertTrue(bizB1.getBizState().equals(ACTIVATED));\n    }\n\n    @Test\n    public void testUninstallBiz() {\n\n        Biz bizA1 = ((MockBiz) bizManagerService.getBizByIdentity(\"A1:V1\")).setBizState(ACTIVATED);\n        Biz bizA2 = ((MockBiz) bizManagerService.getBizByIdentity(\"A1:V2\"))\n            .setBizState(DEACTIVATED);\n        Biz bizB1 = ((MockBiz) bizManagerService.getBizByIdentity(\"B1:V1\")).setBizState(ACTIVATED);\n        bizCommandProvider.handleCommand(\"biz -u B1:V1\");\n\n        sleep(200);\n\n        assertTrue(bizA1.getBizState().equals(ACTIVATED));\n        assertTrue(bizA2.getBizState().equals(DEACTIVATED));\n        assertNull(bizManagerService.getBizByIdentity(\"B1:V1\"));\n    }\n\n    @Test\n    public void testUninstallMasterBiz() {\n\n        putStringValue(MASTER_BIZ, \"B1\");\n        Biz bizA1 = ((MockBiz) bizManagerService.getBizByIdentity(\"A1:V1\")).setBizState(ACTIVATED);\n        Biz bizA2 = ((MockBiz) bizManagerService.getBizByIdentity(\"A1:V2\"))\n            .setBizState(DEACTIVATED);\n        Biz bizB1 = ((MockBiz) bizManagerService.getBizByIdentity(\"B1:V1\")).setBizState(ACTIVATED);\n        bizCommandProvider.handleCommand(\"biz -u B1:V1\");\n\n        sleep(200);\n\n        assertTrue(bizA1.getBizState().equals(ACTIVATED));\n        assertTrue(bizA2.getBizState().equals(DEACTIVATED));\n        assertTrue(bizB1.getBizState().equals(ACTIVATED));\n        //        assertNotNull(bizManagerService.getBizByIdentity(\"B1:V1\"));\n    }\n\n    private void mockBiz() {\n\n        MockBiz bizA1 = new MockBiz();\n        bizA1.setBizName(\"A1\").setBizVersion(\"V1\").setWebContextPath(\"/A1\")\n            .setBizState(BizState.RESOLVED).setMainClass(\"MainClassA1\");\n\n        MockBiz bizA2 = new MockBiz();\n        bizA2.setBizName(\"A1\").setBizVersion(\"V2\").setWebContextPath(\"/A2\")\n            .setBizState(BizState.RESOLVED).setMainClass(\"MainClassA2\");\n\n        MockBiz bizB1 = new MockBiz();\n        bizB1.setBizName(\"B1\").setBizVersion(\"V1\").setWebContextPath(\"/B1\")\n            .setBizState(BizState.RESOLVED).setMainClass(\"MainClassB1\");\n\n        bizManagerService.registerBiz(bizA1);\n        bizManagerService.registerBiz(bizA2);\n        bizManagerService.registerBiz(bizB1);\n    }\n\n    private void sleep(long mill) {\n        try {\n            Thread.sleep(mill);\n        } catch (Throwable t) {\n            // ignore\n        }\n    }\n\n    class MockBiz extends BizModel {\n        @Override\n        public void start(String[] args) throws Throwable {\n        }\n\n        @Override\n        public void stop() {\n            // just to mock stop\n            Biz biz = bizManagerService.getBiz(this.getBizName(), this.getBizVersion());\n            if (biz.getBizState() != BizState.RESOLVED) {\n                bizManagerService.unRegisterBiz(this.getBizName(), this.getBizVersion());\n            }\n        }\n    }\n\n    @Test\n    public void testBizCommandInvalidate() throws MalformedURLException {\n\n        BizCommand bizCommand = bizCommandProvider.new BizCommand(\"\");\n        assertFalse(bizCommand.isValidate());\n        bizCommand = bizCommandProvider.new BizCommand(\"biz -\");\n        assertFalse(bizCommand.isValidate());\n        bizCommand = bizCommandProvider.new BizCommand(\"biz -x\");\n        assertFalse(bizCommand.isValidate());\n        bizCommand = bizCommandProvider.new BizCommand(\"biz -h a\");\n        assertFalse(bizCommand.isValidate());\n        assertTrue(bizCommand.process().startsWith(\"Error command format\"));\n\n        bizCommand = bizCommandProvider.new BizCommand(\"biz -h\");\n        assertEquals(HELP_MESSAGE, bizCommand.process());\n\n        mockBiz();\n        bizCommand = bizCommandProvider.new BizCommand(\"biz -a\");\n        assertEquals(\"A1:V1:resolved\\nA1:V2:resolved\\nB1:V1:resolved\\nbiz count = 3\\n\",\n            bizCommand.process());\n        assertTrue(bizCommand.bizInfo(\"a:b\").startsWith(\"Invalid bizIdentity: \"));\n\n        String bizCommandStr = bizCommand.join(\n            new URL[] { new URL(\"file:\\\\a\"), new URL(\"file:\\\\b\") }, \"&\");\n        assertTrue(bizCommandStr.equals(\"\\\\a&\\\\b\") || bizCommandStr.equals(\"/a&/b\"));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/biz/BizFactoryServiceTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.biz;\n\nimport com.alipay.sofa.ark.common.util.FileUtils;\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.BizOperation;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.service.biz.BizFactoryService;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginFactoryService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.io.IOException;\nimport java.net.URL;\n\nimport static com.alipay.sofa.ark.api.ArkConfigs.putStringValue;\nimport static com.alipay.sofa.ark.container.service.ArkServiceContainerHolder.getContainer;\nimport static com.alipay.sofa.ark.spi.constant.Constants.*;\nimport static com.alipay.sofa.ark.spi.constant.Constants.UNPACK_BIZ_WHEN_INSTALL;\nimport static java.lang.Thread.currentThread;\nimport static org.junit.Assert.assertNotNull;\n\n/**\n * @author qilong.zql\n * @since 0.4.0\n */\npublic class BizFactoryServiceTest extends BaseTest {\n\n    private PluginFactoryService pluginFactoryService;\n\n    private PluginManagerService pluginManagerService;\n\n    private BizFactoryService    bizFactoryService;\n\n    private BizManagerService    bizManagerService;\n\n    @Override\n    public void before() {\n        super.before();\n        pluginManagerService = arkServiceContainer.getService(PluginManagerService.class);\n        pluginFactoryService = arkServiceContainer.getService(PluginFactoryService.class);\n        bizFactoryService = arkServiceContainer.getService(BizFactoryService.class);\n        bizManagerService = getContainer().getService(BizManagerService.class);\n    }\n\n    @Test\n    public void test() throws Throwable {\n\n        ClassLoader cl = currentThread().getContextClassLoader();\n        URL samplePlugin = cl.getResource(\"sample-plugin.jar\");\n        Plugin plugin = pluginFactoryService.createPlugin(FileUtils.file(samplePlugin.getFile()));\n        pluginManagerService.registerPlugin(plugin);\n\n        URL sampleBiz = cl.getResource(\"sample-biz.jar\");\n        Biz biz = bizFactoryService.createBiz(FileUtils.file(sampleBiz.getFile()));\n        bizManagerService.registerBiz(biz);\n        assertNotNull(biz);\n        assertNotNull(biz.getBizClassLoader().getResource(ARK_PLUGIN_MARK_ENTRY));\n\n        putStringValue(MASTER_BIZ, \"master-biz\");\n        Biz masterBiz = bizFactoryService.createEmbedMasterBiz(cl);\n        assertNotNull(masterBiz);\n        assertNotNull(masterBiz.getBizClassLoader().getResource(\n            \"com/alipay/sofa/ark/container/service/biz/\"));\n    }\n\n    @Test\n    public void testCreateBizWithoutBizOperation() throws IOException {\n        String originalEmbed = System.getProperty(EMBED_ENABLE);\n        String originalUnpack = System.getProperty(UNPACK_BIZ_WHEN_INSTALL);\n        try {\n            ClassLoader cl = currentThread().getContextClassLoader();\n            URL sampleBiz = cl.getResource(\"sample-biz.jar\");\n            System.setProperty(EMBED_ENABLE, \"true\");\n            System.setProperty(UNPACK_BIZ_WHEN_INSTALL, \"false\");\n            Biz biz = bizFactoryService.createBiz(FileUtils.file(sampleBiz.getFile()));\n            assertNotNull(biz);\n\n        } finally {\n            if (originalEmbed != null) {\n                System.setProperty(EMBED_ENABLE, originalEmbed);\n            } else {\n                System.clearProperty(EMBED_ENABLE);\n            }\n            if (originalUnpack != null) {\n                System.setProperty(UNPACK_BIZ_WHEN_INSTALL, originalUnpack);\n            } else {\n                System.clearProperty(UNPACK_BIZ_WHEN_INSTALL);\n            }\n        }\n    }\n\n    @Test\n    public void testCreateBiz() throws IOException {\n        ClassLoader cl = currentThread().getContextClassLoader();\n        URL sampleBiz = cl.getResource(\"sample-biz.jar\");\n        BizOperation bizOperation = new BizOperation();\n        String mockVersion = \"mock version\";\n        bizOperation.setBizVersion(mockVersion);\n        Biz biz = bizFactoryService.createBiz(bizOperation, FileUtils.file(sampleBiz.getFile()));\n        Assert.assertEquals(biz.getBizVersion(), mockVersion);\n    }\n\n    @Test\n    public void testPackageInfo() throws Throwable {\n        ClassLoader cl = currentThread().getContextClassLoader();\n        URL samplePlugin = cl.getResource(\"sample-ark-plugin-common-0.5.1.jar\");\n        Plugin plugin = pluginFactoryService.createPlugin(FileUtils.file(samplePlugin.getFile()));\n        ClassLoader pluginClassLoader = plugin.getPluginClassLoader();\n        pluginManagerService.registerPlugin(plugin);\n        Class mdc = pluginClassLoader.loadClass(\"org.slf4j.MDC\");\n        Assert.assertTrue(mdc.getClassLoader().equals(pluginClassLoader));\n        assertNotNull(mdc.getPackage().getImplementationVersion());\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/biz/BizManagerServiceTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.biz;\n\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.BizState;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport static com.alipay.sofa.ark.spi.model.BizState.ACTIVATED;\nimport static com.alipay.sofa.ark.spi.model.BizState.RESOLVED;\nimport static org.codehaus.plexus.util.ReflectionUtils.setVariableValueInObject;\nimport static org.junit.Assert.*;\n\n/**\n * @author qilong.zql\n * @since 0.4.0\n */\npublic class BizManagerServiceTest extends BaseTest {\n\n    private BizManagerService bizManagerService = new BizManagerServiceImpl();\n\n    @Before\n    public void before() {\n        super.before();\n        Biz biz = new BizModel().setBizName(\"test-biz\").setBizVersion(\"1.0.0\")\n            .setBizState(RESOLVED);\n        bizManagerService.registerBiz(biz);\n    }\n\n    @Test\n    public void testRegisterBiz() {\n        Biz ret = bizManagerService.getBiz(\"test-biz\", \"1.0.0\");\n        assertNotNull(ret);\n    }\n\n    @Test\n    public void testDuplicatedRegisterBiz() {\n        Biz biz = new BizModel().setBizName(\"test-biz\").setBizVersion(\"1.0.0\")\n            .setBizState(RESOLVED);\n        assertFalse(bizManagerService.registerBiz(biz));\n        assertEquals(1, bizManagerService.getBiz(\"test-biz\").size());\n    }\n\n    @Test\n    public void testRemovingAndAddBiz() {\n\n        Biz adding = new BizModel().setBizName(\"test-biz-adding\").setBizVersion(\"1.0.0\")\n            .setBizState(ACTIVATED);\n        Biz removing = new BizModel().setBizName(\"test-biz-removing\").setBizVersion(\"1.0.0\")\n            .setBizState(RESOLVED);\n\n        bizManagerService.registerBiz(removing);\n        ((BizModel) removing).setBizState(ACTIVATED);\n        bizManagerService.removeAndAddBiz(adding, removing);\n\n        List<Biz> biz = bizManagerService.getBiz(\"test-biz-adding\");\n        assertTrue(biz.size() == 1);\n\n        biz = bizManagerService.getBiz(\"test-biz-removing\");\n        assertTrue(biz.size() == 0);\n        bizManagerService.unRegisterBiz(adding.getBizName(), adding.getBizVersion());\n    }\n\n    @Test\n    public void testUnRegister() {\n\n        Biz biz = bizManagerService.unRegisterBiz(\"test-biz\", \"1.0.1\");\n        assertNull(biz);\n        assertTrue(bizManagerService.getBiz(\"test-biz\").size() == 1);\n        biz = bizManagerService.unRegisterBizStrictly(\"test-biz\", \"1.0.0\");\n        assertNotNull(biz);\n        assertTrue(bizManagerService.getBiz(\"test-biz\").size() == 0);\n\n        bizManagerService.registerBiz(biz);\n        assertTrue(bizManagerService.getBiz(\"test-biz\").size() == 1);\n    }\n\n    @Test\n    public void testBizGet() {\n\n        Biz biz = bizManagerService.getBizByIdentity(\"test-biz:1.0.0\");\n        assertNotNull(biz);\n\n        Set<String> stringSet = bizManagerService.getAllBizNames();\n        assertTrue(stringSet.size() == 1);\n        assertTrue(stringSet.contains(\"test-biz\"));\n\n        biz = bizManagerService.getActiveBiz(\"test-biz\");\n        assertNull(biz);\n\n        BizState bizState = bizManagerService.getBizState(\"test-biz:1.0.0\");\n        assertTrue(bizState == RESOLVED);\n        bizState = bizManagerService.getBizState(\"test-biz\", \"1.0.0\");\n        assertTrue(bizState == RESOLVED);\n        bizState = bizManagerService.getBizState(\"ss\", \"xx\");\n        assertTrue(bizState == BizState.UNRESOLVED);\n\n        biz = new BizModel().setBizName(\"test-biz\").setBizVersion(\"1.0.1\").setBizState(RESOLVED)\n            .setPriority(\"10\");\n        bizManagerService.registerBiz(biz);\n        List<Biz> bizList = bizManagerService.getBizInOrder();\n        assertTrue(bizList.size() == 2);\n        assertTrue(bizList.get(0).getBizVersion().equals(\"1.0.1\"));\n        assertTrue(bizList.get(1).getBizVersion().equals(\"1.0.0\"));\n\n        biz = bizManagerService.getActiveBiz(\"test-biz\");\n        assertNull(biz);\n        bizManagerService.activeBiz(\"test-biz\", \"1.0.1\");\n        assertTrue(bizManagerService.getBizState(\"test-biz\", \"1.0.1\") == RESOLVED);\n        assertTrue(bizManagerService.getBizState(\"test-biz\", \"1.0.0\") == RESOLVED);\n\n        biz = bizManagerService.getBiz(\"test-biz\", \"1.0.1\");\n        ((BizModel) biz).setBizState(BizState.DEACTIVATED);\n        bizManagerService.activeBiz(\"test-biz\", \"1.0.1\");\n        assertTrue(bizManagerService.getBizState(\"test-biz\", \"1.0.1\") == ACTIVATED);\n        assertTrue(bizManagerService.getBizState(\"test-biz\", \"1.0.0\") == RESOLVED);\n\n        bizManagerService.activeBiz(\"test-biz\", \"1.0.0\");\n        assertTrue(bizManagerService.getBizState(\"test-biz\", \"1.0.1\") == ACTIVATED);\n        assertTrue(bizManagerService.getBizState(\"test-biz\", \"1.0.0\") == RESOLVED);\n\n        biz = bizManagerService.getBiz(\"test-biz\", \"1.0.0\");\n        ((BizModel) biz).setBizState(BizState.DEACTIVATED);\n        bizManagerService.activeBiz(\"test-biz\", \"1.0.0\");\n        assertTrue(bizManagerService.getBizState(\"test-biz\", \"1.0.0\") == ACTIVATED);\n        assertTrue(bizManagerService.getBizState(\"test-biz\", \"1.0.1\") == BizState.DEACTIVATED);\n    }\n\n    @Test(expected = ArkRuntimeException.class)\n    public void testDeployWithException() throws IllegalAccessException {\n\n        Biz biz = new BizModel().setBizName(\"test-biz\").setBizVersion(\"1.0.3\")\n            .setBizState(RESOLVED).setPriority(\"10\");\n        bizManagerService.registerBiz(biz);\n\n        DefaultBizDeployer defaultBizDeployer = new DefaultBizDeployer();\n        setVariableValueInObject(defaultBizDeployer, \"bizManagerService\", bizManagerService);\n        defaultBizDeployer.deploy();\n    }\n\n    @Test(expected = ArkRuntimeException.class)\n    public void testUndeployWithException() throws IllegalAccessException {\n\n        Biz biz = new BizModel().setBizName(\"test-biz\").setBizVersion(\"1.0.3\")\n            .setBizState(RESOLVED).setPriority(\"10\");\n        bizManagerService.registerBiz(biz);\n\n        DefaultBizDeployer defaultBizDeployer = new DefaultBizDeployer();\n        setVariableValueInObject(defaultBizDeployer, \"bizManagerService\", bizManagerService);\n        defaultBizDeployer.unDeploy();\n    }\n\n    @Test\n    public void testIsActiveBiz() {\n\n        bizManagerService = new BizManagerServiceImpl();\n        assertNull(bizManagerService.getBizByClassLoader(this.getClass().getClassLoader()));\n\n        Biz biz = new BizModel().setBizName(\"test-biz\").setBizVersion(\"1.0.1\")\n            .setBizState(RESOLVED).setPriority(\"10\");\n        bizManagerService.registerBiz(biz);\n\n        assertFalse(bizManagerService.isActiveBiz(\"test-biz\", \"1.0.1\"));\n        assertFalse(bizManagerService.isActiveBiz(\"test-biz\", \"2.0.1\"));\n        assertNotNull(bizManagerService.getBizRegistration());\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/biz/hook/TestAddBizToStaticDeployHook.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.biz.hook;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.loader.JarBizArchive;\nimport com.alipay.sofa.ark.loader.archive.JarFileArchive;\nimport com.alipay.sofa.ark.spi.archive.BizArchive;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.service.biz.AddBizToStaticDeployHook;\nimport com.alipay.sofa.ark.spi.service.extension.Extension;\nimport com.google.common.collect.Lists;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author lianglipeng.llp@alibaba-inc.com\n * @version $Id: TestBeforeEmbedStaticDeployBizHook.java, v 0.1 2024年06月27日 16:36 立蓬 Exp $\n */\n@Extension(\"before-embed-static-deploy-biz-hook\")\npublic class TestAddBizToStaticDeployHook implements AddBizToStaticDeployHook {\n\n    @Override\n    public List<BizArchive> getStaticBizToAdd() throws Exception {\n        List<BizArchive> archives = new ArrayList<>();\n        File bizFile = new File(this.getClass().getClassLoader()\n            .getResource(\"sample-ark-1.0.0-ark-biz.jar\").toURI());\n        archives.add(new JarBizArchive(new JarFileArchive(bizFile)));\n        return archives;\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/classloader/BizClassLoaderTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.classloader;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.bootstrap.AgentClassLoader;\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.common.util.ClassUtils;\nimport com.alipay.sofa.ark.common.util.FileUtils;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.container.model.PluginModel;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.container.testdata.ITest;\nimport com.alipay.sofa.ark.exception.ArkLoaderException;\nimport com.alipay.sofa.ark.spi.model.*;\nimport com.alipay.sofa.ark.spi.service.biz.BizFactoryService;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.classloader.ClassLoaderService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginDeployService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport com.google.common.cache.Cache;\nimport com.google.common.collect.Sets;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.IOException;\nimport java.lang.reflect.Field;\nimport java.net.URL;\nimport java.util.*;\n\nimport static com.google.common.collect.Lists.newArrayList;\nimport static java.lang.Thread.currentThread;\n\n/**\n * @author ruoshan\n * @since 0.1.0\n */\npublic class BizClassLoaderTest extends BaseTest {\n\n    private URL                  classPathURL = PluginClassLoaderTest.class.getClassLoader()\n                                                  .getResource(\"\");\n\n    private PluginManagerService pluginManagerService;\n\n    private PluginDeployService  pluginDeployService;\n\n    private ClassLoaderService   classloaderService;\n\n    private BizManagerService    bizManagerService;\n\n    @Before\n    public void before() {\n        super.before();\n        pluginManagerService = ArkServiceContainerHolder.getContainer().getService(\n            PluginManagerService.class);\n        pluginDeployService = ArkServiceContainerHolder.getContainer().getService(\n            PluginDeployService.class);\n        classloaderService = ArkServiceContainerHolder.getContainer().getService(\n            ClassLoaderService.class);\n        bizManagerService = ArkServiceContainerHolder.getContainer().getService(\n            BizManagerService.class);\n    }\n\n    @Test\n    public void testImport() throws Exception {\n        PluginModel pluginA = new PluginModel();\n        pluginA\n            .setPluginName(\"plugin A\")\n            .setClassPath(new URL[] { classPathURL })\n            .setImportClasses(StringUtils.EMPTY_STRING)\n            .setImportPackages(StringUtils.EMPTY_STRING)\n            .setExportClasses(\"\")\n            .setExportPackages(ClassUtils.getPackageName(ITest.class.getName()))\n            .setImportResources(StringUtils.EMPTY_STRING)\n            .setExportResources(StringUtils.EMPTY_STRING)\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginA.getPluginName(), pluginA.getClassPath()));\n\n        pluginManagerService.registerPlugin(pluginA);\n        pluginDeployService.deploy();\n        classloaderService.prepareExportClassAndResourceCache();\n\n        BizModel bizModel = createTestBizModel(\"biz A\", \"1.0.0\", BizState.RESOLVED,\n            new URL[] { classPathURL });\n        bizModel.setDenyImportResources(StringUtils.EMPTY_STRING);\n        bizModel.setDenyImportClasses(StringUtils.EMPTY_STRING);\n        bizModel.setDenyImportPackages(StringUtils.EMPTY_STRING);\n        bizModel.getExportNodeAndClassLoaderMap().put(\n            ClassUtils.getPackageName(ITest.class.getName()), pluginA);\n\n        bizManagerService.registerBiz(bizModel);\n\n        Assert.assertEquals(pluginA.getPluginClassLoader().loadClass(ITest.class.getName()),\n            bizModel.getBizClassLoader().loadClass(ITest.class.getName()));\n    }\n\n    @Test\n    public void testAgentClass() throws ClassNotFoundException {\n        BizModel bizModel = createTestBizModel(\"biz A\", \"1.0.0\", BizState.RESOLVED, new URL[] {});\n        bizModel.setDenyImportResources(\"\").setDenyImportClasses(\"\");\n        bizManagerService.registerBiz(bizModel);\n        Class clazz = bizModel.getBizClassLoader().loadClass(\"SampleClass\");\n        Assert.assertFalse(clazz.getClassLoader() instanceof AgentClassLoader);\n\n        Assert.assertTrue(clazz.getClassLoader().getClass().getCanonicalName()\n            .contains(\"Launcher.AppClassLoader\")\n                          || clazz.getClassLoader().getClass().getCanonicalName()\n                              .contains(\"ClassLoaders.AppClassLoader\"));\n    }\n\n    @Test\n    public void testLoadClassFromPluginClassLoader() throws Exception {\n        URL bizUrl = this.getClass().getClassLoader().getResource(\"sample-ark-1.0.0-ark-biz.jar\");\n        URL pluginUrl1 = this.getClass().getClassLoader().getResource(\"sample-ark-plugin-common-0.5.1.jar\");\n        URL pluginUrl2 = this.getClass().getClassLoader().getResource(\"sofa-ark-sample-springboot-ark-0.3.0.jar\");\n        URL pluginUrl3 = this.getClass().getClassLoader().getResource(\"aopalliance-1.0.jar\");\n        URL pluginUrl4 = this.getClass().getClassLoader().getResource(\"com.springsource.org.aopalliance-1.0.0.jar\");\n\n        BizModel bizModel = createTestBizModel(\"biz A\", \"1.0.0\", BizState.RESOLVED,\n            new URL[] { bizUrl });\n        bizModel.setDenyImportClasses(StringUtils.EMPTY_STRING);\n        bizModel.setDenyImportPackages(StringUtils.EMPTY_STRING);\n        bizModel.setDenyImportResources(StringUtils.EMPTY_STRING);\n        bizModel.setDeclaredLibraries(\"sample-ark-plugin-common,com.springsource.org.aopalliance\");\n\n        PluginModel pluginA = new PluginModel();\n        pluginA\n            .setPluginName(\"plugin A\")\n            .setClassPath(new URL[] { classPathURL })\n            .setImportClasses(StringUtils.EMPTY_STRING)\n            .setImportPackages(StringUtils.EMPTY_STRING)\n            .setExportClasses(\"\")\n            .setExportPackages(ClassUtils.getPackageName(ITest.class.getName()))\n            .setImportResources(StringUtils.EMPTY_STRING)\n            .setExportResources(\"META-INF/services/sofa-ark/com.alipay.sofa.ark.container.service.extension.spi.ServiceB, Sample_Resource_Exported_A\")\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginA.getPluginName(), pluginA.getClassPath()));\n\n        PluginModel pluginB = new PluginModel();\n        pluginB\n            .setPluginName(\"plugin B\")\n            .setClassPath(new URL[] { pluginUrl1, pluginUrl2, pluginUrl3, pluginUrl4 })\n            .setImportClasses(StringUtils.EMPTY_STRING)\n            .setImportPackages(StringUtils.EMPTY_STRING)\n            .setExportClasses(\"com.alipay.sofa.ark.sample.common.SampleClassExported,org.aopalliance.aop.Advice\")\n            .setExportPackages(\"\")\n            .setImportResources(StringUtils.EMPTY_STRING)\n            .setExportResources(\"Sample_Resource_Exported, META-INF/spring/service.xml, Sample_Resource_Exported_A\")\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginB.getPluginName(), pluginB.getClassPath()));\n\n        pluginManagerService.registerPlugin(pluginA);\n        pluginManagerService.registerPlugin(pluginB);\n        pluginDeployService.deploy();\n        classloaderService.prepareExportClassAndResourceCache();\n\n        bizModel.getExportNodeAndClassLoaderMap().put(ClassUtils.getPackageName(ITest.class.getName()), pluginA);\n        bizModel.getExportClassAndClassLoaderMap().put(\"org.aopalliance.aop.Advice\", pluginB);\n        bizModel.getExportClassAndClassLoaderMap().put(\"com.alipay.sofa.ark.sample.common.SampleClassExported\", pluginB);\n        bizModel.getExportResourceAndClassLoaderMap().put(\"META-INF/spring/service.xml\", newArrayList(pluginB));\n        bizModel.getExportResourceAndClassLoaderMap().put(\"Sample_Resource_Exported\", newArrayList(pluginB));\n        bizModel.getExportResourceAndClassLoaderMap().put(\"META-INF/services/sofa-ark/com.alipay.sofa.ark.container.service.extension.spi.ServiceB\", newArrayList(pluginA));\n        bizManagerService.registerBiz(bizModel);\n\n        // case 1: find class from multiple libs in plugin classloader\n        Class<?> adviceClazz = bizModel.getBizClassLoader().loadClass(\"org.aopalliance.aop.Advice\");\n        Assert.assertEquals(adviceClazz.getClassLoader(), pluginB.getPluginClassLoader());\n\n        // case 2: find class from plugin but not set provided in biz model\n        Assert.assertThrows(ArkLoaderException.class, () -> bizModel.getBizClassLoader().loadClass(\"com.alipay.sofa.ark.sample.facade.SampleService\"));\n\n        // case 3: find class from plugin in classpath\n        Class<?> itest = bizModel.getBizClassLoader().loadClass(ITest.class.getName());\n        Assert.assertEquals(itest.getClassLoader(), pluginA.getPluginClassLoader());\n\n        // case 4: find class from plugin in jar\n        Class<?> sampleClassExported = bizModel.getBizClassLoader().loadClass(\"com.alipay.sofa.ark.sample.common.SampleClassExported\");\n        Assert.assertEquals(sampleClassExported.getClassLoader(), pluginB.getPluginClassLoader());\n\n        // case 5: find class but not exported\n        Assert.assertThrows(ArkLoaderException.class, () -> bizModel.getBizClassLoader().loadClass(\"com.alipay.sofa.ark.sample.common.SampleClassNotExported\"));\n\n        // case 6: find resource from plugin but not set provided in biz model\n        Assert.assertNull(bizModel.getBizClassLoader().getResource(\"Sample_Resource_Not_Exported\"));\n\n        // case 7: find sofa-ark resources from plugin in biz model\n        Assert.assertNotNull(bizModel.getBizClassLoader().getResource(\"META-INF/spring/service.xml\"));\n\n        // case 8: find resource from plugin in classpath\n        Assert.assertNotNull(bizModel.getBizClassLoader().getResource(\"META-INF/services/sofa-ark/com.alipay.sofa.ark.container.service.extension.spi.ServiceB\"));\n\n        // case 9: find resource from plugin in jar\n        Assert.assertNotNull(bizModel.getBizClassLoader().getResource(\"Sample_Resource_Exported\"));\n\n        // case 10: find resource but not exproted\n        Assert.assertNull(bizModel.getBizClassLoader().getResource(\"Sample_Resource_Not_Exported\"));\n\n        // case 10: find resources from plugin but not set provided in biz model\n        Assert.assertFalse(bizModel.getBizClassLoader().getResources(\"Sample_Resource_Not_Exported\").hasMoreElements());\n\n        // case 11: find resource from plugin in classpath\n        Assert.assertTrue(bizModel.getBizClassLoader().getResources(\"META-INF/services/sofa-ark/com.alipay.sofa.ark.container.service.extension.spi.ServiceB\").hasMoreElements());\n\n        // case 12: find resource from plugin in jar\n        Assert.assertTrue(bizModel.getBizClassLoader().getResources(\"Sample_Resource_Exported\").hasMoreElements());\n\n        // case 13: find resource but not exproted\n        Assert.assertFalse(bizModel.getBizClassLoader().getResources(\"Sample_Resource_Not_Exported\").hasMoreElements());\n    }\n\n    @Test\n    public void testLoadOverrideClassFromPluginClassLoader() throws Exception {\n        URL bizUrl = this.getClass().getClassLoader().getResource(\"sample-ark-1.0.0-ark-biz.jar\");\n        URL pluginUrl1 = this.getClass().getClassLoader()\n            .getResource(\"sample-ark-plugin-common-0.5.1.jar\");\n        URL pluginUrl2 = this.getClass().getClassLoader()\n            .getResource(\"sofa-ark-sample-springboot-ark-0.3.0.jar\");\n\n        BizModel bizModel = createTestBizModel(\"biz A\", \"1.0.0\", BizState.RESOLVED,\n            new URL[] { bizUrl });\n        bizModel.setDenyImportClasses(StringUtils.EMPTY_STRING);\n        bizModel.setDenyImportPackages(StringUtils.EMPTY_STRING);\n        bizModel.setDenyImportResources(StringUtils.EMPTY_STRING);\n        bizModel.setDeclaredLibraries(\"sample-ark-plugin-common, sofa-ark-sample-springboot-ark\");\n\n        PluginModel pluginA = new PluginModel();\n        pluginA\n            .setPluginName(\"plugin A\")\n            .setClassPath(new URL[] { classPathURL, pluginUrl1, pluginUrl2 })\n            .setImportClasses(StringUtils.EMPTY_STRING)\n            .setImportPackages(StringUtils.EMPTY_STRING)\n            .setExportMode(PluginModel.EXPORTMODE_OVERRIDE)\n            .setExportClasses(\"com.alipay.sofa.ark.sample.common.SampleClassExported\")\n            .setExportPackages(\"\")\n            .setImportResources(StringUtils.EMPTY_STRING)\n            .setExportResources(\"\")\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginA.getPluginName(), pluginA.getClassPath()));\n\n        PluginModel pluginB = new PluginModel();\n        pluginB\n            .setPluginName(\"plugin B\")\n            .setClassPath(new URL[] { classPathURL, pluginUrl1, pluginUrl2 })\n            .setImportClasses(StringUtils.EMPTY_STRING)\n            .setImportPackages(StringUtils.EMPTY_STRING)\n            .setExportMode(PluginModel.EXPORTMODE_CLASSLOADER)\n            .setExportClasses(\"com.alipay.sofa.ark.sample.springbootdemo.SpringbootDemoApplication\")\n            .setExportPackages(\"\")\n            .setImportResources(StringUtils.EMPTY_STRING)\n            .setExportResources(\"\")\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginB.getPluginName(), pluginB.getClassPath()));\n\n        bizModel.getExportClassAndClassLoaderMap().put(\n            \"com.alipay.sofa.ark.sample.springbootdemo.SpringbootDemoApplication\", pluginB);\n\n        pluginManagerService.registerPlugin(pluginA);\n        pluginManagerService.registerPlugin(pluginB);\n        pluginDeployService.deploy();\n        classloaderService.prepareExportClassAndResourceCache();\n\n        bizManagerService.registerBiz(bizModel);\n\n        // case 1: find class from multiple libs in plugin classloader\n        Class<?> adviceClazz1 = bizModel.getBizClassLoader().loadClass(\n            \"com.alipay.sofa.ark.sample.common.SampleClassExported\");\n        Assert.assertEquals(adviceClazz1.getClassLoader(), bizModel.getBizClassLoader());\n\n        Class<?> adviceClazz2 = bizModel.getBizClassLoader().loadClass(\n            \"com.alipay.sofa.ark.sample.springbootdemo.SpringbootDemoApplication\");\n        Assert.assertEquals(adviceClazz2.getClassLoader(), pluginB.getPluginClassLoader());\n    }\n\n    @Test\n    public void testGetPluginClassResource() {\n        PluginModel pluginA = new PluginModel();\n        pluginA\n            .setPluginName(\"plugin A\")\n            .setClassPath(new URL[] { classPathURL })\n            .setImportClasses(StringUtils.EMPTY_STRING)\n            .setImportPackages(StringUtils.EMPTY_STRING)\n            .setExportPackages(\"\")\n            .setExportClasses(ITest.class.getName())\n            .setImportResources(StringUtils.EMPTY_STRING)\n            .setExportResources(StringUtils.EMPTY_STRING)\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginA.getPluginName(), pluginA.getClassPath()));\n\n        pluginManagerService.registerPlugin(pluginA);\n        pluginDeployService.deploy();\n        classloaderService.prepareExportClassAndResourceCache();\n\n        BizModel bizModel = createTestBizModel(\"biz A\", \"1.0.0\", BizState.RESOLVED, new URL[0]);\n        bizModel.setDenyImportClasses(StringUtils.EMPTY_STRING)\n            .setDenyImportResources(StringUtils.EMPTY_STRING)\n            .setDenyImportPackages(StringUtils.EMPTY_STRING);\n        bizManagerService.registerBiz(bizModel);\n\n        Assert.assertNotNull(bizModel.getBizClassLoader().getResource(\n            ITest.class.getName().replace(\".\", \"/\") + \".class\"));\n    }\n\n    @Test\n    public void testLoadClassFromAgentClassLoader() throws ClassNotFoundException {\n        BizModel bizModel = createTestBizModel(\"MockBiz\", \"1.0.0\", BizState.RESOLVED, new URL[] {});\n        bizModel.setDenyImportResources(StringUtils.EMPTY_STRING);\n        bizModel.setDenyImportClasses(StringUtils.EMPTY_STRING);\n        bizModel.setDenyImportPackages(StringUtils.EMPTY_STRING);\n\n        bizManagerService.registerBiz(bizModel);\n        BizClassLoader bizClassLoader = (BizClassLoader) bizModel.getBizClassLoader();\n        Assert.assertNotNull(bizClassLoader.loadClass(\"SampleClass\", false));\n\n        Class clazz = bizClassLoader.loadClass(ArkClient.class.getCanonicalName());\n        Assert.assertTrue(clazz.getClassLoader().equals(classloaderService.getArkClassLoader()));\n    }\n\n    @Test\n    public void testDenyImport() throws Exception {\n        PluginModel pluginA = new PluginModel();\n        pluginA\n            .setPluginName(\"pluginA\")\n            .setClassPath(new URL[] { classPathURL })\n            .setImportClasses(StringUtils.EMPTY_STRING)\n            .setImportPackages(StringUtils.EMPTY_STRING)\n            .setImportResources(StringUtils.EMPTY_STRING)\n            .setExportResources(\"pluginA_export_resource1.xml,pluginA_export_resource2.xml\")\n            .setExportClasses(ITest.class.getName())\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginA.getPluginName(), pluginA.getClassPath()));\n\n        pluginManagerService.registerPlugin(pluginA);\n        pluginDeployService.deploy();\n        classloaderService.prepareExportClassAndResourceCache();\n\n        BizModel bizModel = createTestBizModel(\"bizA\", \"1.0.0\", BizState.RESOLVED, new URL[] {});\n        Set<Plugin> plugins = new HashSet<>();\n        plugins.add(pluginA);\n        bizModel.setDependentPlugins(plugins);\n        bizModel.getExportResourceAndClassLoaderMap().put(\"pluginA_export_resource1.xml\",\n            newArrayList(pluginA));\n        bizModel.getExportResourceAndClassLoaderMap().put(\"pluginA_export_resource2.xml\",\n            newArrayList(pluginA));\n\n        bizModel.setDenyImportResources(StringUtils.EMPTY_STRING);\n        bizModel.setDenyImportPackages(StringUtils.EMPTY_STRING);\n        bizModel.setDenyImportClasses(StringUtils.EMPTY_STRING);\n\n        bizManagerService.registerBiz(bizModel);\n\n        Assert.assertNotNull(bizModel.getBizClassLoader().getResource(\n            \"pluginA_export_resource1.xml\"));\n        Assert.assertNotNull(bizModel.getBizClassLoader().getResource(\n            \"pluginA_export_resource2.xml\"));\n        bizModel.setDenyImportResources(\"pluginA_export_resource2.xml\");\n        invalidClassLoaderCache(bizModel.getBizClassLoader());\n        Assert.assertNull(bizModel.getBizClassLoader().getResource(\"pluginA_export_resource2.xml\"));\n\n        Assert.assertFalse(bizModel.getBizClassLoader().loadClass(ITest.class.getName())\n            .getClassLoader() instanceof PluginClassLoader);\n\n        bizModel.setDenyImportPackages(\"com.alipay.sofa.ark.container.testdata\");\n        invalidClassLoaderCache(bizModel.getBizClassLoader());\n        Assert.assertFalse(bizModel.getBizClassLoader().loadClass(ITest.class.getName())\n            .getClassLoader() instanceof PluginClassLoader);\n\n        bizModel.setDenyImportPackages(StringUtils.EMPTY_STRING);\n        bizModel.setDenyImportClasses(ITest.class.getCanonicalName());\n        invalidClassLoaderCache(bizModel.getBizClassLoader());\n        Assert.assertFalse(bizModel.getBizClassLoader().loadClass(ITest.class.getName())\n            .getClassLoader() instanceof PluginClassLoader);\n\n    }\n\n    @Test\n    public void testDenyImportResourceStems() {\n        PluginModel pluginA = new PluginModel();\n        pluginA\n            .setPluginName(\"pluginA\")\n            .setClassPath(new URL[] { classPathURL })\n            .setImportClasses(StringUtils.EMPTY_STRING)\n            .setImportPackages(StringUtils.EMPTY_STRING)\n            .setImportResources(StringUtils.EMPTY_STRING)\n            .setExportResources(\"export/folderA/*,export/folderB/*\")\n            .setExportClasses(ITest.class.getName())\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginA.getPluginName(), pluginA.getClassPath()));\n\n        pluginManagerService.registerPlugin(pluginA);\n        pluginDeployService.deploy();\n        classloaderService.prepareExportClassAndResourceCache();\n\n        BizModel bizModel = createTestBizModel(\"bizA\", \"1.0.0\", BizState.RESOLVED, new URL[0]);\n        bizModel.setDenyImportResources(\"export/folderA/*,export/folderB/test3.xml\");\n        bizModel.setDenyImportPackages(StringUtils.EMPTY_STRING);\n        bizModel.setDenyImportClasses(StringUtils.EMPTY_STRING);\n\n        bizManagerService.registerBiz(bizModel);\n\n        String testResource1 = \"export/folderA/test1.xml\";\n        String testResource2 = \"export/folderA/test2.xml\";\n        String testResource3 = \"export/folderB/test3.xml\";\n        String testResource4 = \"export/folderB/test4.xml\";\n\n        Assert.assertNull(bizModel.getBizClassLoader().getResource(testResource1));\n        Assert.assertNull(bizModel.getBizClassLoader().getResource(testResource2));\n        Assert.assertNull(bizModel.getBizClassLoader().getResource(testResource3));\n        Assert.assertNull(bizModel.getBizClassLoader().getResource(testResource4));\n    }\n\n    @Test\n    public void testSlashResource() throws Throwable {\n        registerMockBiz();\n        //        URLClassLoader urlClassLoader = (URLClassLoader) this.getClass().getClassLoader();\n        //        Field ucpFiled = URLClassLoader.class.getDeclaredField(\"ucp\");\n        //        ucpFiled.setAccessible(true);\n        //        URLClassPath ucp = (URLClassPath) ucpFiled.get(urlClassLoader);\n        //        BizClassLoader bizClassLoader = new BizClassLoader(\"mock:1.0\", ucp.getURLs());\n        ClassLoader classLoader = this.getClass().getClassLoader();\n        BizClassLoader bizClassLoader = new BizClassLoader(\"mock:1.0\",\n            ClassLoaderUtils.getURLs(classLoader));\n        BizManagerService bizManagerService = ArkServiceContainerHolder.getContainer().getService(\n            BizManagerService.class);\n        bizClassLoader.setBizModel((BizModel) bizManagerService.getBiz(\"mock\", \"1.0\"));\n        URL url = bizClassLoader.getResource(\"\");\n        Assert.assertNotNull(url);\n        Assert.assertEquals(url, this.getClass().getResource(\"/\"));\n    }\n\n    @Test\n    public void testGetJdkResource() throws IOException {\n        BizModel bizModel = createTestBizModel(\"biz A\", \"1.0.0\", BizState.RESOLVED, new URL[] {});\n        bizManagerService.registerBiz(bizModel);\n\n        ClassLoader cl = bizModel.getBizClassLoader();\n        //        String name = \"META-INF/services/javax.script.ScriptEngineFactory\";\n        String name = \"javax/lang/model/element/Modifier.class\";\n\n        URL res1 = cl.getResource(name);\n        Assert.assertNotNull(res1);\n\n        URL res2 = ClassLoader.getSystemClassLoader().getResource(name);\n        Assert.assertEquals(res2, res1);\n\n        Enumeration<URL> enu1 = cl.getResources(name);\n        Assert.assertTrue(enu1.hasMoreElements());\n\n        Enumeration<URL> enu2 = ClassLoader.getSystemClassLoader().getResources(name);\n        Assert.assertEquals(Sets.newHashSet(Collections.list(enu2)),\n            Sets.newHashSet(Collections.list(enu1)));\n\n    }\n\n    @Test\n    public void testCacheResource() throws NoSuchFieldException, IllegalAccessException {\n        BizModel bizModel = createTestBizModel(\"biz A\", \"1.0.0\", BizState.RESOLVED, new URL[] {});\n        bizManagerService.registerBiz(bizModel);\n\n        ClassLoader cl = bizModel.getBizClassLoader();\n        //        String name = \"META-INF/services/javax.script.ScriptEngineFactory\";\n        String name = \"javax/lang/model/element/Modifier.class\";\n        URL res1 = cl.getResource(name);\n        Assert.assertNotNull(res1);\n        Assert.assertNotNull(cl.getResource(name));\n        Cache<String, Optional<URL>> urlResourceCache = getUrlResourceCache(cl);\n        Assert.assertNotNull(urlResourceCache.getIfPresent(name));\n        Assert.assertNotNull(urlResourceCache.getIfPresent(name).get());\n\n        // not existing url\n        String notExistingName = \"META-INF/services/javax.script.ScriptEngineFactory/NotExisting\";\n        URL notExistingRes = cl.getResource(notExistingName);\n        Assert.assertNull(notExistingRes);\n        Assert.assertNull(cl.getResource(notExistingName));\n        Assert.assertNotNull(urlResourceCache.getIfPresent(notExistingName));\n        Assert.assertFalse(urlResourceCache.getIfPresent(notExistingName).isPresent());\n    }\n\n    @Test\n    public void testPublicDefineClass() {\n        BizModel bizModel = createTestBizModel(\"biz A\", \"1.0.0\", BizState.RESOLVED, new URL[] {});\n        bizManagerService.registerBiz(bizModel);\n\n        BizClassLoader cl = (BizClassLoader) bizModel.getBizClassLoader();\n        try {\n            cl.publicDefineClass(\"NoExistClass\", new byte[] {}, null);\n            Assert.fail();\n        } catch (Throwable t) {\n            Assert.assertTrue(t instanceof java.lang.ClassFormatError);\n        }\n    }\n\n    private Cache<String, Optional<URL>> getUrlResourceCache(Object classloader)\n                                                                                throws NoSuchFieldException,\n                                                                                IllegalAccessException {\n        Field field = AbstractClasspathClassLoader.class.getDeclaredField(\"urlResourceCache\");\n        field.setAccessible(true);\n        return (Cache<String, Optional<URL>>) field.get(classloader);\n    }\n\n    private void invalidClassLoaderCache(ClassLoader classloader) {\n        if (classloader instanceof AbstractClasspathClassLoader) {\n            ((AbstractClasspathClassLoader) classloader).invalidAllCache();\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/classloader/ClassLoaderConcurrencyTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.classloader;\n\nimport com.alipay.sofa.ark.common.thread.CommonThreadPool;\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.container.testdata.classloader.ClassLoaderTestClass;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.net.URL;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\n/**\n * Test class load concurrency\n * @author ruoshan\n * @since 0.5.0\n */\npublic class ClassLoaderConcurrencyTest extends BaseTest {\n\n    private URL classPathURL = ClassLoaderConcurrencyTest.class.getClassLoader().getResource(\"\");\n\n    @Test\n    public void concurrencyLoadClass() {\n        final BizClassLoader bizClassLoader = new BizClassLoader(\"test:1.0\",\n            new URL[] { classPathURL });\n\n        final AtomicBoolean result = new AtomicBoolean(true);\n\n        int totalTimes = 100;\n        ThreadPoolExecutor executor = new CommonThreadPool().getExecutor();\n        final CountDownLatch countDownLatch = new CountDownLatch(totalTimes);\n\n        for (int index = 0; index < totalTimes; index++) {\n            if (result.get()) {\n                executor.execute(new Runnable() {\n                    @Override\n                    public void run() {\n                        try {\n                            bizClassLoader.loadClass(ClassLoaderTestClass.class.getName(), true);\n                        } catch (ClassNotFoundException e) {\n                            // ingore\n                        } catch (LinkageError e) {\n                            result.set(false);\n                        } finally {\n                            countDownLatch.countDown();\n                        }\n                    }\n                });\n            } else {\n                countDownLatch.countDown();\n            }\n        }\n\n        try {\n            countDownLatch.await();\n        } catch (InterruptedException e) {\n            // ignore\n        } finally {\n            executor.shutdown();\n        }\n\n        Assert.assertTrue(\"should not get linkega error when load class\", result.get());\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/classloader/ClassLoaderHookTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.classloader;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.container.service.classloader.hook.TestBizClassLoaderHook;\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.container.service.classloader.hook.TestDefaultBizClassLoaderHook;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.classloader.ClassLoaderHook;\nimport com.alipay.sofa.ark.spi.service.extension.ArkServiceLoader;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.mockito.MockedStatic;\nimport org.mockito.Mockito;\n\nimport java.net.URL;\nimport java.util.Enumeration;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.*;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class ClassLoaderHookTest extends BaseTest {\n    @Override\n    public void before() {\n        super.before();\n        registerMockBiz();\n        registerMockPlugin();\n    }\n\n    @Test\n    public void testBizClassLoaderSPI() throws Throwable {\n        BizClassLoader bizClassLoader = new BizClassLoader(\"mock:1.0\",\n            (ClassLoaderUtils.getURLs(this.getClass().getClassLoader())));\n        Assert.assertTrue(TestBizClassLoaderHook.ClassA.class.getName().equals(\n            bizClassLoader.loadClass(\"A.A\").getName()));\n        BizManagerService bizManagerService = ArkServiceContainerHolder.getContainer().getService(\n            BizManagerService.class);\n        bizClassLoader.setBizModel((BizModel) bizManagerService.getBiz(\"mock\", \"1.0\"));\n        Assert.assertTrue(TestBizClassLoaderHook.ClassB.class.getName().equals(\n            bizClassLoader.loadClass(\"B\").getName()));\n        Assert.assertTrue(bizClassLoader.getResource(\"R1\").getFile()\n            .endsWith(\"pluginA_export_resource1.xml\"));\n        Assert.assertTrue(bizClassLoader.getResource(\"sample-biz.jar\").getFile()\n            .endsWith(\"sample-biz.jar\"));\n        Assert.assertTrue(bizClassLoader.getResource(\"any\").getFile()\n            .endsWith(\"pluginA_export_resource2.xml\"));\n\n        Enumeration<URL> urls = bizClassLoader.getResources(\"R2\");\n        Assert.assertTrue(urls.hasMoreElements());\n        URL url = urls.nextElement();\n        Assert.assertFalse(urls.hasMoreElements());\n        Assert.assertTrue(url.getFile().contains(\"sample-biz.jar\"));\n\n        urls = bizClassLoader.getResources(\"test.jar\");\n        Assert.assertTrue(urls.hasMoreElements());\n        url = urls.nextElement();\n        Assert.assertFalse(urls.hasMoreElements());\n        Assert.assertTrue(url.getFile().contains(\"test.jar\"));\n\n        urls = bizClassLoader.getResources(\"any\");\n        Assert.assertTrue(urls.hasMoreElements());\n        url = urls.nextElement();\n        Assert.assertFalse(urls.hasMoreElements());\n        Assert.assertTrue(url.getFile().contains(\"sample-plugin.jar\"));\n    }\n\n    @Test\n    public void testDefaultBizClassLoaderSPI() throws Throwable {\n        try (MockedStatic<ArkServiceLoader> arkServiceLoaderMocked = Mockito.mockStatic(ArkServiceLoader.class); MockedStatic<ArkClient> arkClientMocked = Mockito.mockStatic(ArkClient.class)) {\n\n\n            arkServiceLoaderMocked.when(() -> ArkServiceLoader.loadExtensionFromArkBiz(ClassLoaderHook.class,\n                    BIZ_CLASS_LOADER_HOOK, \"mock_default_classloader:1.0\")).thenReturn(null);\n\n\n            arkClientMocked.when(ArkClient::getMasterBiz).thenReturn(new BizModel().setBizName(\"mock_master_biz\").setBizVersion(\"1.0\")\n                    .setClassLoader(this.getClass().getClassLoader()));\n\n            BizClassLoader bizClassLoader = new BizClassLoader(\"mock_default_classloader:1.0\",\n                    (ClassLoaderUtils.getURLs(this.getClass().getClassLoader())));\n            System.setProperty(BIZ_CLASS_LOADER_HOOK_DIR,\n                    \"com.alipay.sofa.ark.container.service.classloader.hook.TestDefaultBizClassLoaderHook\");\n            Assert.assertTrue(TestDefaultBizClassLoaderHook.ClassDefaultA.class.getName().equals(\n                    bizClassLoader.loadClass(\"defaultA\").getName()));\n            System.clearProperty(BIZ_CLASS_LOADER_HOOK_DIR);\n        }\n    }\n\n    @Test\n    public void testPluginClassLoaderSPI() throws Throwable {\n        PluginClassLoader pluginClassLoader = new PluginClassLoader(\"mock\",\n            (ClassLoaderUtils.getURLs(this.getClass().getClassLoader())));\n        Assert.assertTrue(TestBizClassLoaderHook.ClassA.class.getName().equals(\n            pluginClassLoader.loadClass(\"A.A\").getName()));\n        Assert.assertTrue(TestBizClassLoaderHook.ClassB.class.getName().equals(\n            pluginClassLoader.loadClass(\"B\").getName()));\n        Assert.assertTrue(pluginClassLoader.getResource(\"R1\").getFile()\n            .endsWith(\"pluginA_export_resource1.xml\"));\n        Assert.assertTrue(pluginClassLoader.getResource(\"sample-biz.jar\").getFile()\n            .endsWith(\"sample-biz.jar\"));\n        Assert.assertTrue(pluginClassLoader.getResource(\"any\").getFile()\n            .endsWith(\"pluginA_export_resource2.xml\"));\n\n        Enumeration<URL> urls = pluginClassLoader.getResources(\"R2\");\n        Assert.assertTrue(urls.hasMoreElements());\n        URL url = urls.nextElement();\n        Assert.assertFalse(urls.hasMoreElements());\n        Assert.assertTrue(url.getFile().contains(\"sample-biz.jar\"));\n\n        urls = pluginClassLoader.getResources(\"test.jar\");\n        Assert.assertTrue(urls.hasMoreElements());\n        url = urls.nextElement();\n        Assert.assertFalse(urls.hasMoreElements());\n        Assert.assertTrue(url.getFile().contains(\"test.jar\"));\n\n        urls = pluginClassLoader.getResources(\"any\");\n        Assert.assertTrue(urls.hasMoreElements());\n        url = urls.nextElement();\n        Assert.assertFalse(urls.hasMoreElements());\n        Assert.assertTrue(url.getFile().contains(\"sample-plugin.jar\"));\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/classloader/ClassLoaderServiceTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.classloader;\n\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.container.model.PluginModel;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.classloader.ClassLoaderService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.lang.reflect.Field;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport static com.alipay.sofa.ark.common.util.AssertUtils.isFalse;\nimport static com.alipay.sofa.ark.common.util.AssertUtils.isTrue;\nimport static com.alipay.sofa.ark.spi.model.BizState.RESOLVED;\nimport static java.util.Arrays.asList;\nimport static org.junit.Assert.*;\nimport static org.mockito.Mockito.mock;\n\n/**\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class ClassLoaderServiceTest extends BaseTest {\n\n    private ClassLoaderService   classloaderService;\n    private BizManagerService    bizManagerService;\n    private PluginManagerService pluginManagerService;\n\n    @Before\n    public void before() {\n        super.before();\n        classloaderService = ArkServiceContainerHolder.getContainer().getService(\n            ClassLoaderService.class);\n        bizManagerService = ArkServiceContainerHolder.getContainer().getService(\n            BizManagerService.class);\n        pluginManagerService = ArkServiceContainerHolder.getContainer().getService(\n            PluginManagerService.class);\n    }\n\n    @Test\n    public void testIsSunReflect() {\n        assertTrue(classloaderService.isSunReflectClass(\"sun.reflect.GeneratedMethodAccessor100\"));\n    }\n\n    @Test\n    public void testIsNotSunReflect() {\n        assertFalse(classloaderService.isSunReflectClass(\"test\"));\n    }\n\n    @Test\n    public void testIsArkSpiClass() {\n        assertTrue(classloaderService.isArkSpiClass(\"com.alipay.sofa.ark.spi.service.ArkService\"));\n    }\n\n    @Test\n    public void testIsNotArkSpiClass() {\n        assertFalse(classloaderService.isArkSpiClass(\"test\"));\n    }\n\n    @Test\n    public void testJDKClassLoader() {\n        String sunToolClass = \"sun.tools.attach.BsdVirtualMachine\";\n        ClassLoader jdkClassLoader = classloaderService.getJDKClassLoader();\n        assertNotNull(jdkClassLoader);\n        try {\n            // only when this class can be loaded from system classloader,\n            // then it should be loaded successfully from jdkClassLoader\n            classloaderService.getSystemClassLoader().loadClass(sunToolClass);\n            assertNotNull(jdkClassLoader.loadClass(sunToolClass));\n        } catch (ClassNotFoundException e) {\n            // ignore\n        }\n    }\n\n    @Test\n    public void testArkClassLoader() {\n        ClassLoader arkClassLoader = classloaderService.getArkClassLoader();\n        assertNotNull(arkClassLoader);\n    }\n\n    @Test\n    public void testSystemClassLoader() {\n        ClassLoader systemClassLoader = classloaderService.getSystemClassLoader();\n        assertNotNull(systemClassLoader);\n    }\n\n    @Test\n    public void testAgentClassLoader() throws ClassNotFoundException {\n        ClassLoader agentClassLoader = classloaderService.getAgentClassLoader();\n        assertNotNull(agentClassLoader);\n        assertTrue(((URLClassLoader) agentClassLoader).getURLs().length == 2);\n        assertNotNull(agentClassLoader.loadClass(\"SampleClass\"));\n    }\n\n    @Test\n    public void testIsDeniedImportClass() {\n\n        Biz biz = new BizModel().setBizName(\"mockBiz\").setBizVersion(\"1.0.0\")\n            .setDenyImportPackages(\"a.c, a.b.c.*, a.b.c\").setDenyImportClasses(\"\")\n            .setBizState(RESOLVED);\n        bizManagerService.registerBiz(biz);\n        isFalse(classloaderService.isDeniedImportClass(biz.getIdentity(), \"a.c\"), \"Exception error\");\n\n        isTrue(classloaderService.isDeniedImportClass(biz.getIdentity(), \"a.c.E\"),\n            \"Exception error\");\n        isFalse(classloaderService.isDeniedImportClass(biz.getIdentity(), \"a.c.e.G\"),\n            \"Exception error\");\n        isTrue(classloaderService.isDeniedImportClass(biz.getIdentity(), \"a.b.c.E\"),\n            \"Exception error\");\n        isTrue(classloaderService.isDeniedImportClass(biz.getIdentity(), \"a.b.c.e.G\"),\n            \"Exception error\");\n        isFalse(classloaderService.isDeniedImportClass(biz.getIdentity(), \"a.b.c\"),\n            \"Exception error\");\n    }\n\n    @Test\n    public void testIsClassImport() {\n\n        Plugin plugin = new PluginModel().setPluginName(\"mockPlugin\").setImportClasses(null)\n            .setImportPackages(\"a.c,a.b.c.*,a.b.c\");\n        pluginManagerService.registerPlugin(plugin);\n\n        assertTrue(classloaderService.isClassInImport(\"mockPlugin\", \"a.c.e\"));\n        assertFalse(classloaderService.isClassInImport(\"mockPlugin\", \"a.c\"));\n        assertFalse(classloaderService.isClassInImport(\"mockPlugin\", \"a.c.e.f\"));\n        assertFalse(classloaderService.isClassInImport(\"mockPlugin\", \"a.b.c\"));\n        assertTrue(classloaderService.isClassInImport(\"mockPlugin\", \"a.b.c.e\"));\n        assertTrue(classloaderService.isClassInImport(\"mockPlugin\", \"a.b.c.e.f\"));\n    }\n\n    @Test\n    public void testFindExportClass() {\n\n        PluginClassLoader pluginClassLoader = new PluginClassLoader(\"mockPlugin\", new URL[] {});\n        Plugin plugin = new PluginModel().setPluginName(\"mockPlugin\")\n            .setExportPackages(\"a.b.*,a.f,a.b.f\").setExportClasses(\"a.e.f.G\")\n            .setPluginClassLoader(pluginClassLoader).setExportResources(\"\");\n\n        pluginManagerService.registerPlugin(plugin);\n        classloaderService.prepareExportClassAndResourceCache();\n        assertNull(classloaderService.findExportClassLoader(\"a.b\"));\n        assertTrue(pluginClassLoader.equals(classloaderService.findExportClassLoader(\"a.b.e.f\")));\n        assertTrue(pluginClassLoader.equals(classloaderService.findExportClassLoader(\"a.f.g\")));\n        assertTrue(pluginClassLoader.equals(classloaderService.findExportClassLoader(\"a.e.f.G\")));\n        assertTrue(pluginClassLoader.equals(classloaderService.findExportClassLoader(\"a.b.f.m\")));\n        assertTrue(pluginClassLoader.equals(classloaderService.findExportClassLoader(\"a.b.f.m.g\")));\n        assertNull(classloaderService.findExportClassLoader(\"a.f.h.m\"));\n        assertNull(classloaderService.findExportClassLoader(\"a\"));\n        pluginManagerService.getPluginsInOrder().remove(plugin);\n    }\n\n    @Test\n    public void testFindExportResources() {\n\n        PluginClassLoader pluginClassLoader = new PluginClassLoader(\"mockPlugin\", new URL[] {});\n        String exportResources = \"spring-beans.xsd,*.xsd,com/alipay/sofa/*,xml-test.xml\";\n        Plugin plugin = new PluginModel().setPluginName(\"mockPlugin\").setExportPackages(\"\")\n            .setExportClasses(\"\").setPluginClassLoader(pluginClassLoader)\n            .setExportResources(exportResources);\n        pluginManagerService.registerPlugin(plugin);\n        classloaderService.prepareExportClassAndResourceCache();\n\n        Set<String> exportPrefixResourceStems = plugin.getExportPrefixResourceStems();\n        assertTrue(exportPrefixResourceStems.contains(\"com/alipay/sofa/\"));\n\n        Set<String> exportSuffixResourceStems = plugin.getExportSuffixResourceStems();\n        assertTrue(exportSuffixResourceStems.contains(\".xsd\"));\n\n        Set<String> resources = plugin.getExportResources();\n        assertTrue(resources.contains(\"xml-test.xml\"));\n        assertTrue(resources.contains(\"spring-beans.xsd\"));\n\n        plugin.getExportPrefixResourceStems().clear();\n        plugin.getExportSuffixResourceStems().clear();\n        plugin.getExportResources().clear();\n        pluginManagerService.getPluginsInOrder().remove(plugin);\n    }\n\n    @Test\n    public void testFindExportResourceClassLoadersInOrder() throws Exception {\n\n        Field field = ClassLoaderServiceImpl.class\n            .getDeclaredField(\"exportSuffixStemResourceAndClassLoaderMap\");\n        field.setAccessible(true);\n        ConcurrentHashMap exportPrefixStemResourceAndClassLoaderMap = new ConcurrentHashMap<>();\n        Plugin plugin = mock(Plugin.class);\n        exportPrefixStemResourceAndClassLoaderMap.put(\"myaaa\", asList(plugin));\n        field.set(classloaderService, exportPrefixStemResourceAndClassLoaderMap);\n        assertEquals(null, classloaderService.findExportResourceClassLoadersInOrder(\"myaaa\").get(0));\n\n        assertNull(classloaderService.getBizClassLoader(\"aaa:1.0\"));\n        assertNull(classloaderService.getPluginClassLoader(\"aaa:2.0\"));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/classloader/CompoundEnumerationTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.classloader;\n\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.BizState;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.service.biz.BizFactoryService;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginFactoryService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.NoSuchElementException;\n\npublic class CompoundEnumerationTest extends BaseTest {\n    private PluginFactoryService pluginFactoryService;\n\n    private PluginManagerService pluginManagerService;\n\n    private BizFactoryService    bizFactoryService;\n\n    private BizManagerService    bizManagerService;\n\n    @Before\n    public void before() {\n        super.before();\n        pluginManagerService = arkServiceContainer.getService(PluginManagerService.class);\n        pluginFactoryService = arkServiceContainer.getService(PluginFactoryService.class);\n        bizFactoryService = arkServiceContainer.getService(BizFactoryService.class);\n        bizManagerService = ArkServiceContainerHolder.getContainer().getService(\n            BizManagerService.class);\n    }\n\n    @Test(expected = NoSuchElementException.class)\n    public void test() throws IOException {\n        URL bizURL = this.getClass().getClassLoader().getResource(\"sample-ark-1.0.0-ark-biz.jar\");\n        URL pluginURL = this.getClass().getClassLoader().getResource(\"sample-plugin.jar\");\n\n        BizModel bizModel = createTestBizModel(\"biz A\", \"1.0.0\", BizState.RESOLVED, new URL[] {\n                bizURL, pluginURL });\n        bizModel.setDenyImportClasses(StringUtils.EMPTY_STRING);\n        bizModel.setDenyImportPackages(StringUtils.EMPTY_STRING);\n        bizModel.setDenyImportResources(StringUtils.EMPTY_STRING);\n        bizManagerService.registerBiz(bizModel);\n        CompoundEnumeration<URL> e = (CompoundEnumeration<URL>) bizModel.getBizClassLoader()\n            .getResources(Constants.ARK_PLUGIN_MARK_ENTRY);\n\n        Assert.assertTrue(e.hasMoreElements());\n        URL url = e.nextElement();\n        Assert.assertNotNull(url);\n        e.nextElement();\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/classloader/PluginClassLoaderTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.classloader;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.container.model.PluginModel;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.container.testdata.ITest;\nimport com.alipay.sofa.ark.exception.ArkLoaderException;\nimport com.alipay.sofa.ark.spi.service.classloader.ClassLoaderService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginDeployService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.Collections;\nimport java.util.Enumeration;\nimport java.util.List;\n\nimport static com.alipay.sofa.ark.common.util.ClassLoaderUtils.getURLs;\nimport static com.alipay.sofa.ark.common.util.ClassUtils.getPackageName;\nimport static com.alipay.sofa.ark.common.util.StringUtils.EMPTY_STRING;\nimport static com.google.common.collect.Sets.newHashSet;\nimport static java.lang.ClassLoader.getSystemClassLoader;\nimport static org.junit.Assert.*;\n\n/**\n * @author ruoshan\n * @since 0.1.0\n */\npublic class PluginClassLoaderTest extends BaseTest {\n\n    private URL                  classPathURL = PluginClassLoaderTest.class.getClassLoader()\n                                                  .getResource(\"\");\n\n    private PluginManagerService pluginManagerService;\n\n    private PluginDeployService  pluginDeployService;\n\n    private ClassLoaderService   classloaderService;\n\n    @Before\n    public void before() {\n        super.before();\n        pluginManagerService = ArkServiceContainerHolder.getContainer().getService(\n            PluginManagerService.class);\n        pluginDeployService = ArkServiceContainerHolder.getContainer().getService(\n            PluginDeployService.class);\n        classloaderService = ArkServiceContainerHolder.getContainer().getService(\n            ClassLoaderService.class);\n    }\n\n    @Test\n    public void testExportAndImport() throws Exception {\n        PluginModel pluginA = new PluginModel();\n        pluginA\n            .setPluginName(\"plugin A\")\n            .setClassPath(new URL[] { classPathURL })\n            .setImportClasses(EMPTY_STRING)\n            .setImportPackages(EMPTY_STRING)\n            .setExportPackages(getPackageName(ITest.class.getName()))\n            .setExportClasses(\"\")\n            .setExportResources(EMPTY_STRING)\n            .setImportResources(EMPTY_STRING)\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginA.getPluginName(), pluginA.getClassPath()));\n\n        PluginModel pluginB = new PluginModel();\n        pluginB\n            .setPluginName(\"plugin B\")\n            .setPriority(\"1\")\n            .setClassPath(new URL[] { classPathURL })\n            .setImportClasses(ITest.class.getName())\n            .setImportPackages(EMPTY_STRING)\n            .setExportPackages(\"\")\n            .setExportClasses(\"\")\n            .setExportResources(EMPTY_STRING)\n            .setImportResources(EMPTY_STRING)\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginB.getPluginName(), pluginB.getClassPath()));\n\n        pluginManagerService.registerPlugin(pluginA);\n        pluginManagerService.registerPlugin(pluginB);\n        classloaderService.prepareExportClassAndResourceCache();\n        pluginDeployService.deploy();\n\n        assertEquals(pluginA.getPluginClassLoader().loadClass(ITest.class.getName()), pluginB\n            .getPluginClassLoader().loadClass(ITest.class.getName()));\n    }\n\n    @Test\n    public void testExportAndNotImport() throws Exception {\n        PluginModel pluginA = new PluginModel();\n        pluginA\n            .setPluginName(\"plugin A\")\n            .setClassPath(new URL[] { classPathURL })\n            .setImportClasses(EMPTY_STRING)\n            .setImportPackages(EMPTY_STRING)\n            .setExportPackages(\"com.alipay.sofa.ark\")\n            .setExportClasses(\"\")\n            .setExportResources(EMPTY_STRING)\n            .setImportResources(EMPTY_STRING)\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginA.getPluginName(), pluginA.getClassPath()));\n\n        PluginModel pluginB = new PluginModel();\n        pluginB\n            .setPluginName(\"plugin B\")\n            .setPriority(\"1\")\n            .setClassPath(new URL[] { classPathURL })\n            .setImportClasses(EMPTY_STRING)\n            .setImportPackages(EMPTY_STRING)\n            .setExportPackages(\"\")\n            .setExportClasses(\"\")\n            .setExportResources(EMPTY_STRING)\n            .setImportResources(EMPTY_STRING)\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginB.getPluginName(), pluginB.getClassPath()));\n\n        pluginManagerService.registerPlugin(pluginA);\n        pluginManagerService.registerPlugin(pluginB);\n        classloaderService.prepareExportClassAndResourceCache();\n        pluginDeployService.deploy();\n\n        Assert.assertNotEquals(pluginA.getPluginClassLoader().loadClass(ITest.class.getName()),\n            pluginB.getPluginClassLoader().loadClass(ITest.class.getName()));\n\n    }\n\n    @Test\n    public void testExportResource() {\n        PluginModel pluginA = new PluginModel();\n        pluginA\n            .setPluginName(\"pluginA\")\n            .setClassPath(new URL[] { classPathURL })\n            .setImportClasses(EMPTY_STRING)\n            .setImportPackages(EMPTY_STRING)\n            .setExportPackages(getPackageName(ITest.class.getCanonicalName()))\n            .setExportClasses(\"\")\n            .setImportResources(EMPTY_STRING)\n            .setExportResources(\"pluginA_export_resource1.xml,pluginA_export_resource2.xml\")\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginA.getPluginName(), pluginA.getClassPath()));\n\n        PluginModel pluginB = new PluginModel();\n        pluginB\n            .setPluginName(\"pluginB\")\n            .setPriority(\"1\")\n            .setClassPath(new URL[0])\n            .setImportClasses(EMPTY_STRING)\n            .setImportPackages(EMPTY_STRING)\n            .setExportPackages(\"\")\n            .setExportClasses(\"\")\n            .setImportResources(\"pluginA_export_resource1.xml\")\n            .setExportResources(EMPTY_STRING)\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginB.getPluginName(), pluginB.getClassPath()));\n\n        pluginManagerService.registerPlugin(pluginA);\n        pluginManagerService.registerPlugin(pluginB);\n        classloaderService.prepareExportClassAndResourceCache();\n        pluginDeployService.deploy();\n\n        assertNotNull(pluginB.getPluginClassLoader().getResource(\"pluginA_export_resource1.xml\"));\n        Assert.assertNull(pluginB.getPluginClassLoader()\n            .getResource(\"pluginA_export_resource2.xml\"));\n        Assert.assertNull(pluginB.getPluginClassLoader().getResource(\n            \"pluginA_not_export_resource.xml\"));\n\n    }\n\n    @Test\n    public void testMultiExportResource() throws Exception {\n        String resourceName = \"multi_export.xml\";\n\n        PluginModel pluginA = new PluginModel();\n        pluginA\n            .setPluginName(\"pluginA\")\n            .setPriority(\"100\")\n            .setClassPath(new URL[] { classPathURL })\n            .setImportClasses(EMPTY_STRING)\n            .setImportPackages(EMPTY_STRING)\n            .setExportPackages(getPackageName(ITest.class.getCanonicalName()))\n            .setExportClasses(\"\")\n            .setImportResources(EMPTY_STRING)\n            .setExportResources(resourceName)\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginA.getPluginName(), pluginA.getClassPath()));\n\n        PluginModel pluginB = new PluginModel();\n        pluginB\n            .setPluginName(\"pluginB\")\n            .setPriority(\"1\")\n            .setClassPath(new URL[] { classPathURL })\n            .setImportClasses(EMPTY_STRING)\n            .setImportPackages(EMPTY_STRING)\n            .setExportPackages(\"\")\n            .setExportClasses(\"\")\n            .setImportResources(EMPTY_STRING)\n            .setExportResources(resourceName)\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginB.getPluginName(), pluginB.getClassPath()));\n\n        PluginModel pluginC = new PluginModel();\n        pluginC\n            .setPluginName(\"pluginC\")\n            .setPriority(\"1000\")\n            .setClassPath(new URL[] { classPathURL })\n            .setImportClasses(EMPTY_STRING)\n            .setImportPackages(EMPTY_STRING)\n            .setExportPackages(\"\")\n            .setExportClasses(\"\")\n            .setImportResources(EMPTY_STRING)\n            .setExportResources(resourceName)\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginC.getPluginName(), pluginC.getClassPath()));\n\n        PluginModel pluginD = new PluginModel();\n        pluginD\n            .setPluginName(\"pluginD\")\n            .setClassPath(new URL[0])\n            .setImportClasses(EMPTY_STRING)\n            .setImportPackages(EMPTY_STRING)\n            .setExportPackages(\"\")\n            .setExportClasses(\"\")\n            .setImportResources(resourceName)\n            .setExportResources(EMPTY_STRING)\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginD.getPluginName(), pluginD.getClassPath()));\n\n        pluginManagerService.registerPlugin(pluginA);\n        pluginManagerService.registerPlugin(pluginB);\n        pluginManagerService.registerPlugin(pluginC);\n        pluginManagerService.registerPlugin(pluginD);\n        classloaderService.prepareExportClassAndResourceCache();\n        pluginDeployService.deploy();\n\n        Enumeration<URL> urlEnumeration = pluginD.getPluginClassLoader().getResources(resourceName);\n        assertEquals(3, Collections.list(urlEnumeration).size());\n\n        List<ClassLoader> classLoaders = classloaderService\n            .findExportResourceClassLoadersInOrder(resourceName);\n        assertEquals(3, classLoaders.size());\n        assertEquals(pluginB.getPluginClassLoader(), classLoaders.get(0));\n        assertEquals(pluginA.getPluginClassLoader(), classLoaders.get(1));\n        assertEquals(pluginC.getPluginClassLoader(), classLoaders.get(2));\n    }\n\n    @Test\n    public void testExportResourceStems() {\n        PluginModel pluginA = new PluginModel();\n        pluginA\n            .setPluginName(\"pluginA\")\n            .setPriority(\"100\")\n            .setClassPath(new URL[] { classPathURL })\n            .setImportClasses(EMPTY_STRING)\n            .setImportPackages(EMPTY_STRING)\n            .setExportPackages(EMPTY_STRING)\n            .setExportClasses(EMPTY_STRING)\n            .setImportResources(EMPTY_STRING)\n            .setExportResources(\"export/folderA/*,export/folderB/*\")\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginA.getPluginName(), pluginA.getClassPath()));\n\n        PluginModel pluginB = new PluginModel();\n        pluginB\n            .setPluginName(\"pluginB\")\n            .setPriority(\"1\")\n            .setClassPath(new URL[0])\n            .setImportClasses(EMPTY_STRING)\n            .setImportPackages(EMPTY_STRING)\n            .setExportPackages(EMPTY_STRING)\n            .setExportClasses(EMPTY_STRING)\n            .setImportResources(\"export/folderA/*,export/folderB/test3.xml\")\n            .setExportResources(EMPTY_STRING)\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginB.getPluginName(), pluginB.getClassPath()));\n\n        pluginManagerService.registerPlugin(pluginA);\n        pluginManagerService.registerPlugin(pluginB);\n        classloaderService.prepareExportClassAndResourceCache();\n        pluginDeployService.deploy();\n\n        String testResource1 = \"export/folderA/test1.xml\";\n        String testResource2 = \"export/folderA/test2.xml\";\n        String testResource3 = \"export/folderB/test3.xml\";\n        String testResource4 = \"export/folderB/test4.xml\";\n\n        assertEquals(pluginA.getPluginClassLoader().getResource(testResource1), pluginB\n            .getPluginClassLoader().getResource(testResource1));\n        assertEquals(pluginA.getPluginClassLoader().getResource(testResource2), pluginB\n            .getPluginClassLoader().getResource(testResource2));\n        assertEquals(pluginA.getPluginClassLoader().getResource(testResource3), pluginB\n            .getPluginClassLoader().getResource(testResource3));\n        // export/folderB/test4.xml not import\n        Assert.assertNull(pluginB.getPluginClassLoader().getResource(testResource4));\n    }\n\n    @Test\n    public void testLoadClassFromAgentClassLoader() throws ClassNotFoundException {\n        PluginModel mockPlugin = new PluginModel();\n        mockPlugin\n            .setPluginName(\"Mock plugin\")\n            .setClassPath(new URL[] {})\n            .setImportClasses(EMPTY_STRING)\n            .setImportPackages(EMPTY_STRING)\n            .setExportPackages(ITest.class.getCanonicalName())\n            .setPluginClassLoader(\n                new PluginClassLoader(mockPlugin.getPluginName(), mockPlugin.getClassPath()));\n        pluginManagerService.registerPlugin(mockPlugin);\n\n        PluginClassLoader pluginClassLoader = (PluginClassLoader) mockPlugin.getPluginClassLoader();\n        assertNotNull(pluginClassLoader.loadClass(\"SampleClass\", false));\n\n        Class clazz = pluginClassLoader.loadClass(ArkClient.class.getCanonicalName());\n        assertTrue(clazz.getClassLoader().equals(classloaderService.getArkClassLoader()));\n    }\n\n    @Test\n    public void testGetJdkResource() throws IOException {\n\n        PluginModel mockPlugin = new PluginModel();\n        mockPlugin\n            .setPluginName(\"Mock plugin\")\n            .setClassPath(new URL[] {})\n            .setImportResources(EMPTY_STRING)\n            .setImportClasses(EMPTY_STRING)\n            .setImportPackages(EMPTY_STRING)\n            .setExportPackages(ITest.class.getCanonicalName())\n            .setPluginClassLoader(\n                new PluginClassLoader(mockPlugin.getPluginName(), mockPlugin.getClassPath()));\n        pluginManagerService.registerPlugin(mockPlugin);\n\n        ClassLoader cl = mockPlugin.getPluginClassLoader();\n        //         String name = \"META-INF/services/javax.script.ScriptEngineFactory\";\n\n        String name = \"javax/lang/model/element/Modifier.class\";\n        URL res1 = cl.getResource(name);\n        assertNotNull(res1);\n\n        URL res2 = getSystemClassLoader().getResource(name);\n        assertNotNull(res2);\n        assertEquals(res2, res1);\n\n        Enumeration<URL> enu1 = cl.getResources(name);\n        assertTrue(enu1.hasMoreElements());\n\n        Enumeration<URL> enu2 = getSystemClassLoader().getResources(name);\n        assertEquals(newHashSet(Collections.list(enu2)), newHashSet(Collections.list(enu1)));\n    }\n\n    @Test\n    public void testSlashResource() throws Throwable {\n        ClassLoader classLoader = this.getClass().getClassLoader();\n        PluginClassLoader pluginClassLoader = new PluginClassLoader(\"pluginName\",\n            getURLs(classLoader));\n        PluginModel mockPlugin = new PluginModel();\n        mockPlugin.setPluginName(\"pluginName\").setClassPath(new URL[] {})\n            .setImportResources(EMPTY_STRING).setImportClasses(EMPTY_STRING)\n            .setImportPackages(EMPTY_STRING)\n            .setExportPackages(getPackageName(ITest.class.getCanonicalName()))\n            .setExportClasses(EMPTY_STRING).setPluginClassLoader(pluginClassLoader);\n        pluginManagerService.registerPlugin(mockPlugin);\n        URL url = pluginClassLoader.getResource(\"\");\n        assertNotNull(url);\n        assertEquals(url, this.getClass().getResource(\"/\"));\n    }\n\n    @Test(expected = ArkLoaderException.class)\n    public void testLoadClassInternalWithSunClass() throws Exception {\n        PluginClassLoader pluginClassLoader = new PluginClassLoader(\"a\", new URL[] { this\n            .getClass().getResource(\"/\") });\n        assertEquals(\"a\", pluginClassLoader.getPluginName());\n        pluginClassLoader.loadClassInternal(\"sun.reflect.GeneratedMethodAccessor\", true);\n    }\n\n    @Test(expected = ArkLoaderException.class)\n    public void testLoadClassInternalWithResolve() throws Exception {\n        PluginClassLoader pluginClassLoader = new PluginClassLoader(\"a\", new URL[] { this\n            .getClass().getResource(\"/\") });\n        assertEquals(\"a\", pluginClassLoader.getPluginName());\n        pluginClassLoader.loadClassInternal(\"java.lang.a\", true);\n    }\n\n    @Test(expected = ArkLoaderException.class)\n    public void testPreLoadClassWithException() throws Exception {\n        PluginClassLoader pluginClassLoader = new PluginClassLoader(\"a\", new URL[] { this\n            .getClass().getResource(\"/\") });\n        pluginClassLoader.preLoadClass(\"a\");\n    }\n\n    @Test(expected = ArkLoaderException.class)\n    public void testPostLoadClassWithException() throws Exception {\n        PluginClassLoader pluginClassLoader = new PluginClassLoader(\"a\", new URL[] { this\n            .getClass().getResource(\"/\") });\n        pluginClassLoader.postLoadClass(\"a\");\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/classloader/hook/AbstractClassLoaderHook.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.classloader.hook;\n\nimport com.alipay.sofa.ark.spi.service.classloader.ClassLoaderHook;\nimport com.alipay.sofa.ark.spi.service.classloader.ClassLoaderService;\nimport com.alipay.sofa.ark.container.service.classloader.CompoundEnumeration;\n\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Enumeration;\nimport java.util.List;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class AbstractClassLoaderHook<T> implements ClassLoaderHook<T> {\n    @Override\n    public Class<?> preFindClass(String name, ClassLoaderService classLoaderService, T t) {\n        if (\"A.A\".equals(name)) {\n            return TestBizClassLoaderHook.ClassA.class;\n        }\n        return null;\n    }\n\n    @Override\n    public Class<?> postFindClass(String name, ClassLoaderService classLoaderService, T t) {\n        return TestBizClassLoaderHook.ClassB.class;\n    }\n\n    @Override\n    public URL preFindResource(String name, ClassLoaderService classLoaderService, T t) {\n        if (\"R1\".equals(name)) {\n            return classLoaderService.getArkClassLoader().getResource(\n                \"pluginA_export_resource1.xml\");\n        }\n        return null;\n    }\n\n    @Override\n    public URL postFindResource(String name, ClassLoaderService classLoaderService, T t) {\n        if (\"any\".equals(name)) {\n            return classLoaderService.getArkClassLoader().getResource(\n                \"pluginA_export_resource2.xml\");\n        }\n        return null;\n    }\n\n    @Override\n    public Enumeration<URL> preFindResources(String name, ClassLoaderService classLoaderService, T t)\n                                                                                                     throws IOException {\n        if (\"R2\".equals(name)) {\n            return classLoaderService.getArkClassLoader().getResources(\"sample-biz.jar\");\n        }\n        List<Enumeration<URL>> enumerationList = new ArrayList<>();\n        return new CompoundEnumeration<>(enumerationList.toArray(new Enumeration[] {}));\n    }\n\n    @Override\n    public Enumeration<URL> postFindResources(String name, ClassLoaderService classLoaderService,\n                                              T t) throws IOException {\n        if (\"any\".equals(name)) {\n            return classLoaderService.getArkClassLoader().getResources(\"sample-plugin.jar\");\n        }\n        List<Enumeration<URL>> enumerationList = new ArrayList<>();\n        return new CompoundEnumeration<>(enumerationList.toArray(new Enumeration[] {}));\n    }\n\n    public static class ClassA {\n    }\n\n    public static class ClassB {\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/classloader/hook/TestBizClassLoaderHook.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.classloader.hook;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.service.extension.Extension;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@Extension(Constants.BIZ_CLASS_LOADER_HOOK)\npublic class TestBizClassLoaderHook extends AbstractClassLoaderHook<Biz> {\n    public class ClassA {\n    }\n\n    public class ClassB {\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/classloader/hook/TestDefaultBizClassLoaderHook.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.classloader.hook;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.service.classloader.ClassLoaderHook;\nimport com.alipay.sofa.ark.spi.service.classloader.ClassLoaderService;\nimport com.alipay.sofa.ark.spi.service.extension.Extension;\n\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.Enumeration;\n\n/**\n * @author jinle.xjl\n * @since 1.1.7\n */\n@Extension(value = Constants.BIZ_CLASS_LOADER_HOOK, order = 99)\npublic class TestDefaultBizClassLoaderHook implements ClassLoaderHook<Biz> {\n    @Override\n    public Class<?> preFindClass(String name, ClassLoaderService classLoaderService, Biz biz)\n                                                                                             throws ClassNotFoundException {\n        if (\"defaultA\".equals(name)) {\n            return ClassDefaultA.class;\n        }\n        return null;\n    }\n\n    @Override\n    public Class<?> postFindClass(String name, ClassLoaderService classLoaderService, Biz biz)\n                                                                                              throws ClassNotFoundException {\n        if (\"defaultB\".equals(name)) {\n            return ClassDefaultB.class;\n        }\n        return null;\n    }\n\n    @Override\n    public URL preFindResource(String name, ClassLoaderService classLoaderService, Biz biz) {\n        return null;\n    }\n\n    @Override\n    public URL postFindResource(String name, ClassLoaderService classLoaderService, Biz biz) {\n        return null;\n    }\n\n    @Override\n    public Enumeration<URL> preFindResources(String name, ClassLoaderService classLoaderService,\n                                             Biz biz) throws IOException {\n        return null;\n    }\n\n    @Override\n    public Enumeration<URL> postFindResources(String name, ClassLoaderService classLoaderService,\n                                              Biz biz) throws IOException {\n        return null;\n    }\n\n    public class ClassDefaultA {\n    }\n\n    public class ClassDefaultB {\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/classloader/hook/TestPluginClassLoaderHook.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.classloader.hook;\n\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.service.extension.Extension;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@Extension(Constants.PLUGIN_CLASS_LOADER_HOOK)\npublic class TestPluginClassLoaderHook extends AbstractClassLoaderHook<Plugin> {\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/event/EventAdminServiceTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.event;\n\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.spi.event.biz.AfterBizStopEvent;\nimport com.alipay.sofa.ark.spi.event.biz.BeforeBizStopEvent;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.BizState;\nimport com.alipay.sofa.ark.spi.service.event.EventAdminService;\nimport com.alipay.sofa.ark.spi.service.event.EventHandler;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.lang.reflect.Field;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * @author qilong.zql\n * @since 0.4.0\n */\npublic class EventAdminServiceTest extends BaseTest {\n\n    private static int mark = 5;\n    static Map         map  = new HashMap();\n\n    @Test\n    public void test() throws Throwable {\n\n        try {\n            Field field = EventAdminServiceImpl.class.getDeclaredField(\"SUBSCRIBER_MAP\");\n            field.setAccessible(true);\n            map = (Map) field.get(null);\n        } catch (Throwable throwable) {\n            Assert.assertNull(throwable);\n        }\n        EventAdminService eventAdminService = ArkServiceContainerHolder.getContainer().getService(\n            EventAdminService.class);\n        eventAdminService.register(new LowPriorityMockEventHandler());\n        eventAdminService.register(new HighPriorityMockEventHandler());\n        eventAdminService.register(new BeforeBizStopEventHandler());\n\n        ClassLoader bizClassLoader = getClass().getClassLoader();\n        Biz biz = new BizModel().setBizState(BizState.DEACTIVATED).setBizName(\"mock name\")\n            .setBizVersion(\"mock name\").setClassLoader(bizClassLoader);\n        Assert.assertNotNull(map.get(bizClassLoader));\n        biz.stop();\n        Assert.assertNull(map.get(bizClassLoader));\n        Assert.assertTrue(mark == 50);\n        EventHandler eventHandler = new LowPriorityMockEventHandler();\n        eventAdminService.register(eventHandler);\n        Assert.assertNotNull(map.get(bizClassLoader));\n        eventAdminService.unRegister(eventHandler);\n        Assert.assertFalse(((Set) map.get(bizClassLoader)).contains(eventHandler));\n    }\n\n    class HighPriorityMockEventHandler implements EventHandler<BeforeBizStopEvent> {\n\n        @Override\n        public void handleEvent(BeforeBizStopEvent event) {\n            mark *= mark;\n        }\n\n        @Override\n        public int getPriority() {\n            return 10;\n        }\n    }\n\n    class LowPriorityMockEventHandler implements EventHandler<BeforeBizStopEvent> {\n\n        @Override\n        public void handleEvent(BeforeBizStopEvent event) {\n            mark += mark;\n        }\n\n        @Override\n        public int getPriority() {\n            return 100;\n        }\n    }\n\n    class BeforeBizStopEventHandler implements EventHandler<AfterBizStopEvent> {\n\n        @Override\n        public void handleEvent(AfterBizStopEvent event) {\n            map.remove(getClass().getClassLoader());\n        }\n\n        @Override\n        public int getPriority() {\n            return 100;\n        }\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/event/EventTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.event;\n\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.container.model.PluginModel;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.event.AbstractArkEvent;\nimport com.alipay.sofa.ark.spi.event.AfterFinishDeployEvent;\nimport com.alipay.sofa.ark.spi.event.AfterFinishStartupEvent;\nimport com.alipay.sofa.ark.spi.event.biz.AfterBizStartupEvent;\nimport com.alipay.sofa.ark.spi.event.biz.AfterBizStopEvent;\nimport com.alipay.sofa.ark.spi.event.biz.AfterBizSwitchEvent;\nimport com.alipay.sofa.ark.spi.event.biz.BeforeBizRecycleEvent;\nimport com.alipay.sofa.ark.spi.event.biz.BeforeBizStartupEvent;\nimport com.alipay.sofa.ark.spi.event.biz.BeforeBizStopEvent;\nimport com.alipay.sofa.ark.spi.event.biz.BeforeBizSwitchEvent;\nimport com.alipay.sofa.ark.spi.event.biz.AfterBizStartupFailedEvent;\nimport com.alipay.sofa.ark.spi.event.plugin.AfterPluginStartupEvent;\nimport com.alipay.sofa.ark.spi.event.plugin.AfterPluginStopEvent;\nimport com.alipay.sofa.ark.spi.event.plugin.BeforePluginStartupEvent;\nimport com.alipay.sofa.ark.spi.event.plugin.BeforePluginStopEvent;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.BizState;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.service.event.EventAdminService;\nimport com.alipay.sofa.ark.spi.service.event.EventHandler;\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * test for specified event type\n *\n * @author guolei.sgl (guolei.sgl@antfin.com) 2019/11/5 10:39 AM\n * @since\n **/\npublic class EventTest extends BaseTest {\n\n    EventAdminService   eventAdminService;\n\n    static List<String> result = new ArrayList<>();\n\n    @Before\n    public void before() {\n        super.before();\n        eventAdminService = ArkServiceContainerHolder.getContainer().getService(\n            EventAdminService.class);\n        eventAdminService.register(new AfterBizStartupEventHandler());\n        eventAdminService.register(new AfterBizStopEventHandler());\n        eventAdminService.register(new AfterBizSwitchEventHandler());\n        eventAdminService.register(new BeforeBizStartupEventHandler());\n        eventAdminService.register(new BeforeBizStopEventHandler());\n        eventAdminService.register(new BeforeBizSwitchEventHandler());\n        eventAdminService.register(new AfterPluginStartupEventHandler());\n        eventAdminService.register(new BeforePluginStopEventHandler());\n        eventAdminService.register(new AfterPluginStopEventHandler());\n        eventAdminService.register(new BeforePluginStartupEventHandler());\n        eventAdminService.register(new AfterFinishDeployEventHandler());\n        eventAdminService.register(new AfterFinishStartupEventHandler());\n        eventAdminService.register(new BeforeBizRecycleEventEventHandler());\n        eventAdminService.register(new TestArkEventHandler());\n        eventAdminService.register(new BizFailedEventHandler());\n    }\n\n    @After\n    public void after() {\n        result.clear();\n        super.after();\n    }\n\n    @Test\n    public void testEvent() {\n        Biz biz = new BizModel().setBizName(\"test-biz\").setBizVersion(\"1.0.0\")\n            .setBizState(BizState.RESOLVED);\n        Plugin plugin = new PluginModel().setPluginName(\"test-plugin\").setVersion(\"1.0.0\");\n        eventAdminService.sendEvent(new AfterBizStartupEvent(biz));\n        Assert.assertTrue(result.get(0).equalsIgnoreCase(\n            Constants.BIZ_EVENT_TOPIC_AFTER_INVOKE_BIZ_START));\n        eventAdminService.sendEvent(new BeforeBizStartupEvent(biz));\n        Assert.assertTrue(result.get(1).equalsIgnoreCase(\n            Constants.BIZ_EVENT_TOPIC_BEFORE_INVOKE_BIZ_START));\n        eventAdminService.sendEvent(new BeforeBizStopEvent(biz));\n        Assert.assertTrue(result.get(2).equalsIgnoreCase(\n            Constants.BIZ_EVENT_TOPIC_BEFORE_INVOKE_BIZ_STOP));\n        eventAdminService.sendEvent(new AfterBizStopEvent(biz));\n        Assert.assertTrue(result.get(3).equalsIgnoreCase(\n            Constants.BIZ_EVENT_TOPIC_AFTER_INVOKE_BIZ_STOP));\n        eventAdminService.sendEvent(new BeforeBizSwitchEvent(biz));\n        Assert.assertTrue(result.get(4).equalsIgnoreCase(\n            Constants.BIZ_EVENT_TOPIC_BEFORE_INVOKE_BIZ_SWITCH));\n        eventAdminService.sendEvent(new AfterBizSwitchEvent(biz));\n        Assert.assertTrue(result.get(5).equalsIgnoreCase(\n            Constants.BIZ_EVENT_TOPIC_AFTER_INVOKE_BIZ_SWITCH));\n        eventAdminService.sendEvent(new AfterPluginStartupEvent(plugin));\n        Assert.assertTrue(result.get(6).equalsIgnoreCase(\n            Constants.PLUGIN_EVENT_TOPIC_AFTER_INVOKE_PLUGIN_START));\n        eventAdminService.sendEvent(new AfterPluginStopEvent(plugin));\n        Assert.assertTrue(result.get(7).equalsIgnoreCase(\n            Constants.PLUGIN_EVENT_TOPIC_AFTER_INVOKE_PLUGIN_STOP));\n\n        eventAdminService.sendEvent(new BeforePluginStartupEvent(plugin));\n        Assert.assertTrue(result.get(8).equalsIgnoreCase(\n            Constants.PLUGIN_EVENT_TOPIC_BEFORE_INVOKE_PLUGIN_START));\n        eventAdminService.sendEvent(new BeforePluginStopEvent(plugin));\n        Assert.assertTrue(result.get(9).equalsIgnoreCase(\n            Constants.PLUGIN_EVENT_TOPIC_BEFORE_INVOKE_PLUGIN_STOP));\n\n        eventAdminService.sendEvent(new AfterFinishDeployEvent());\n        Assert.assertTrue(result.get(10).equalsIgnoreCase(\n            Constants.ARK_EVENT_TOPIC_AFTER_FINISH_DEPLOY_STAGE));\n        eventAdminService.sendEvent(new AfterFinishStartupEvent());\n        Assert.assertTrue(result.get(11).equalsIgnoreCase(\n            Constants.ARK_EVENT_TOPIC_AFTER_FINISH_STARTUP_STAGE));\n\n        eventAdminService.sendEvent(new TestArkEvent(\"\"));\n        Assert.assertTrue(result.get(12).equalsIgnoreCase(\"test-ark\"));\n\n        eventAdminService.sendEvent(new BeforeBizRecycleEvent(biz));\n        Assert.assertTrue(result.get(13).equalsIgnoreCase(\n            Constants.BIZ_EVENT_TOPIC_BEFORE_RECYCLE_BIZ));\n\n        eventAdminService.sendEvent(new AfterBizStartupFailedEvent(biz, new Throwable()));\n        Assert.assertTrue(result.get(14).equalsIgnoreCase(\n            Constants.BIZ_EVENT_TOPIC_AFTER_BIZ_FAILED));\n    }\n\n    static class TestArkEvent extends AbstractArkEvent {\n\n        public TestArkEvent(Object source) {\n            super(source);\n            this.topic = \"test-ark\";\n        }\n    }\n\n    static class TestArkEventHandler implements EventHandler<TestArkEvent> {\n\n        @Override\n        public void handleEvent(TestArkEvent event) {\n            result.add(event.getTopic());\n        }\n\n        @Override\n        public int getPriority() {\n            return 0;\n        }\n    }\n\n    static class AfterBizStartupEventHandler implements EventHandler<AfterBizStartupEvent> {\n\n        @Override\n        public void handleEvent(AfterBizStartupEvent event) {\n            result.add(event.getTopic());\n        }\n\n        @Override\n        public int getPriority() {\n            return 0;\n        }\n    }\n\n    static class BeforeBizRecycleEventEventHandler implements EventHandler<BeforeBizRecycleEvent> {\n\n        @Override\n        public void handleEvent(BeforeBizRecycleEvent event) {\n            result.add(event.getTopic());\n        }\n\n        @Override\n        public int getPriority() {\n            return 0;\n        }\n    }\n\n    static class AfterBizStopEventHandler implements EventHandler<AfterBizStopEvent> {\n\n        @Override\n        public void handleEvent(AfterBizStopEvent event) {\n            result.add(event.getTopic());\n        }\n\n        @Override\n        public int getPriority() {\n            return 0;\n        }\n    }\n\n    static class AfterBizSwitchEventHandler implements EventHandler<AfterBizSwitchEvent> {\n\n        @Override\n        public void handleEvent(AfterBizSwitchEvent event) {\n            result.add(event.getTopic());\n        }\n\n        @Override\n        public int getPriority() {\n            return 0;\n        }\n    }\n\n    static class BeforeBizStartupEventHandler implements EventHandler<BeforeBizStartupEvent> {\n\n        @Override\n        public void handleEvent(BeforeBizStartupEvent event) {\n            result.add(event.getTopic());\n        }\n\n        @Override\n        public int getPriority() {\n            return 0;\n        }\n    }\n\n    static class BeforeBizStopEventHandler implements EventHandler<BeforeBizStopEvent> {\n\n        @Override\n        public void handleEvent(BeforeBizStopEvent event) {\n            result.add(event.getTopic());\n        }\n\n        @Override\n        public int getPriority() {\n            return 0;\n        }\n    }\n\n    static class BeforeBizSwitchEventHandler implements EventHandler<BeforeBizSwitchEvent> {\n\n        @Override\n        public void handleEvent(BeforeBizSwitchEvent event) {\n            result.add(event.getTopic());\n        }\n\n        @Override\n        public int getPriority() {\n            return 0;\n        }\n    }\n\n    static class AfterPluginStartupEventHandler implements EventHandler<AfterPluginStartupEvent> {\n\n        @Override\n        public void handleEvent(AfterPluginStartupEvent event) {\n            result.add(event.getTopic());\n        }\n\n        @Override\n        public int getPriority() {\n            return 0;\n        }\n    }\n\n    static class BeforePluginStopEventHandler implements EventHandler<BeforePluginStopEvent> {\n\n        @Override\n        public void handleEvent(BeforePluginStopEvent event) {\n            result.add(event.getTopic());\n        }\n\n        @Override\n        public int getPriority() {\n            return 0;\n        }\n    }\n\n    static class AfterPluginStopEventHandler implements EventHandler<AfterPluginStopEvent> {\n\n        @Override\n        public void handleEvent(AfterPluginStopEvent event) {\n            result.add(event.getTopic());\n        }\n\n        @Override\n        public int getPriority() {\n            return 0;\n        }\n    }\n\n    static class BeforePluginStartupEventHandler implements EventHandler<BeforePluginStartupEvent> {\n\n        @Override\n        public void handleEvent(BeforePluginStartupEvent event) {\n            result.add(event.getTopic());\n        }\n\n        @Override\n        public int getPriority() {\n            return 0;\n        }\n    }\n\n    static class AfterFinishDeployEventHandler implements EventHandler<AfterFinishDeployEvent> {\n\n        @Override\n        public void handleEvent(AfterFinishDeployEvent event) {\n            result.add(event.getTopic());\n        }\n\n        @Override\n        public int getPriority() {\n            return 0;\n        }\n    }\n\n    static class AfterFinishStartupEventHandler implements EventHandler<AfterFinishStartupEvent> {\n\n        @Override\n        public void handleEvent(AfterFinishStartupEvent event) {\n            result.add(event.getTopic());\n        }\n\n        @Override\n        public int getPriority() {\n            return 0;\n        }\n    }\n\n    static class BizFailedEventHandler implements EventHandler<AfterBizStartupFailedEvent> {\n\n        @Override\n        public void handleEvent(AfterBizStartupFailedEvent event) {\n            result.add(event.getTopic());\n        }\n\n        @Override\n        public int getPriority() {\n            return 0;\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/event/GlobalEventHandlerTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.event;\n\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.container.model.PluginModel;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.spi.event.AbstractArkEvent;\nimport com.alipay.sofa.ark.spi.event.ArkEvent;\nimport com.alipay.sofa.ark.spi.event.biz.AfterBizStartupEvent;\nimport com.alipay.sofa.ark.spi.event.plugin.BeforePluginStartupEvent;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.BizState;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.service.event.EventAdminService;\nimport com.alipay.sofa.ark.spi.service.event.EventHandler;\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author guolei.sgl (guolei.sgl@antfin.com) 2019/11/19 3:46 PM\n * @since\n **/\npublic class GlobalEventHandlerTest extends BaseTest {\n\n    EventAdminService       eventAdminService;\n\n    ArkEventHandler         arkEventHandler         = new ArkEventHandler();\n    ArkEventHandler1        arkEventHandler1        = new ArkEventHandler1();\n    AbstractArkEventHandler abstractArkEventHandler = new AbstractArkEventHandler();\n\n    static List<String>     result                  = new ArrayList<>();\n\n    @Before\n    public void before() {\n        super.before();\n        eventAdminService = ArkServiceContainerHolder.getContainer().getService(\n            EventAdminService.class);\n        eventAdminService.register(arkEventHandler);\n        eventAdminService.register(arkEventHandler1);\n        eventAdminService.register(abstractArkEventHandler);\n    }\n\n    @After\n    public void after() {\n        result.clear();\n        eventAdminService.unRegister(arkEventHandler);\n        eventAdminService.unRegister(arkEventHandler1);\n        eventAdminService.unRegister(abstractArkEventHandler);\n        arkEventHandler = null;\n        arkEventHandler1 = null;\n        super.after();\n    }\n\n    @Test\n    public void testEvent() {\n        Biz biz = new BizModel().setBizName(\"test-biz\").setBizVersion(\"1.0.0\")\n            .setBizState(BizState.RESOLVED);\n        Plugin plugin = new PluginModel().setPluginName(\"test-plugin\").setVersion(\"1.0.0\");\n        eventAdminService.sendEvent(new AfterBizStartupEvent(biz));\n        Assert.assertTrue(result.size() == 3);\n        Assert.assertTrue(result.contains(\"AbstractArkEvent->AfterBizStartupEvent\"));\n        eventAdminService.sendEvent(new BeforePluginStartupEvent(plugin));\n        Assert.assertTrue(result.size() == 5);\n\n        // test for ArkEvent.class.isAssignableFrom(event.getClass()\n        eventAdminService.sendEvent(new ArkEvent() {\n            @Override\n            public String getTopic() {\n                return \"ark-event\";\n            }\n        });\n        Assert.assertTrue(result.size() == 7);\n    }\n\n    static class ArkEventHandler implements EventHandler {\n\n        @Override\n        public void handleEvent(ArkEvent event) {\n            result.add(event.getTopic());\n        }\n\n        @Override\n        public int getPriority() {\n            return 0;\n        }\n    }\n\n    static class ArkEventHandler1 implements EventHandler<ArkEvent> {\n\n        @Override\n        public void handleEvent(ArkEvent event) {\n            result.add(event.getTopic());\n        }\n\n        @Override\n        public int getPriority() {\n            return 0;\n        }\n    }\n\n    static class AbstractArkEventHandler implements EventHandler<AbstractArkEvent> {\n\n        @Override\n        public void handleEvent(AbstractArkEvent event) {\n            if (event instanceof AfterBizStartupEvent) {\n                result.add(\"AbstractArkEvent->AfterBizStartupEvent\");\n            }\n        }\n\n        @Override\n        public int getPriority() {\n            return 0;\n        }\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/event/MultiEventTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.event;\n\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.event.AbstractArkEvent;\nimport com.alipay.sofa.ark.spi.event.AfterFinishStartupEvent;\nimport com.alipay.sofa.ark.spi.service.event.EventAdminService;\nimport com.alipay.sofa.ark.spi.service.event.EventHandler;\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author guolei.sgl (guolei.sgl@antfin.com) 2019/11/19 11:43 AM\n * @since\n **/\npublic class MultiEventTest extends BaseTest {\n\n    EventAdminService   eventAdminService;\n\n    static List<String> result = new ArrayList<>();\n\n    @Before\n    public void before() {\n        super.before();\n        eventAdminService = ArkServiceContainerHolder.getContainer().getService(\n            EventAdminService.class);\n        eventAdminService.register(new MultiAfterFinishStartupEventHandler());\n\n        eventAdminService.register(new AbstractEventHandler());\n    }\n\n    @After\n    public void after() {\n        result.clear();\n        super.after();\n    }\n\n    @Test\n    public void testEvent() {\n        eventAdminService.sendEvent(new AfterFinishStartupEvent());\n        Assert.assertTrue(result.get(0).equalsIgnoreCase(\n            Constants.ARK_EVENT_TOPIC_AFTER_FINISH_STARTUP_STAGE));\n    }\n\n    static class MultiAfterFinishStartupEventHandler implements\n                                                    EventHandler<AfterFinishStartupEvent>,\n                                                    OtherHandler<TestEvent.SubTestEvent> {\n\n        @Override\n        public void handleEvent(AfterFinishStartupEvent event) {\n            result.add(event.getTopic());\n        }\n\n        @Override\n        public int getPriority() {\n            return 0;\n        }\n    }\n\n    interface OtherHandler<T extends TestEvent> {\n    }\n\n    static class TestEvent {\n        public static class SubTestEvent extends TestEvent {\n\n        }\n    }\n\n    static class AbstractEventHandler implements EventHandler<AbstractArkEvent> {\n\n        @Override\n        public void handleEvent(AbstractArkEvent event) {\n            if (event instanceof AfterFinishStartupEvent) {\n                result.add(event.getTopic());\n            }\n        }\n\n        @Override\n        public int getPriority() {\n            return 0;\n        }\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/extension/ExtensionClassTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.extension;\n\nimport com.alipay.sofa.ark.container.service.extension.spi.SingletonService;\nimport com.alipay.sofa.ark.container.service.extension.spi.impl.SingletonServiceImpl;\nimport com.alipay.sofa.ark.spi.service.extension.Extensible;\nimport com.alipay.sofa.ark.spi.service.extension.Extension;\nimport com.alipay.sofa.ark.spi.service.extension.ExtensionClass;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class ExtensionClassTest {\n    @Test\n    public void testExtensionClass() {\n        Extensible extensible = SingletonService.class.getAnnotation(Extensible.class);\n        Extension extension = SingletonServiceImpl.class.getAnnotation(Extension.class);\n        ExtensionClass extensionClass = new ExtensionClass();\n        extensionClass.setExtensible(extensible);\n        extensionClass.setExtension(extension);\n        extensionClass.setInterfaceClass(SingletonService.class);\n        extensionClass.setImplementClass(SingletonServiceImpl.class);\n        Object object1 = extensionClass.getObject();\n        Object object2 = extensionClass.getObject();\n        Object singleton = extensionClass.getSingleton();\n        Assert.assertTrue(object1.equals(object2));\n        Assert.assertTrue(object1.equals(singleton));\n        Assert.assertEquals(200, extensionClass.getPriority());\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/extension/ExtensionServiceTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.extension;\n\nimport com.alipay.sofa.ark.container.ArkContainer;\nimport com.alipay.sofa.ark.container.ArkContainerTest;\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.container.model.PluginModel;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.container.service.extension.spi.ServiceA;\nimport com.alipay.sofa.ark.container.service.extension.spi.ServiceB;\nimport com.alipay.sofa.ark.container.service.extension.spi.ServiceC;\nimport com.alipay.sofa.ark.container.service.extension.spi.ServiceD;\nimport com.alipay.sofa.ark.container.service.extension.spi.impl.ServiceBImpl4;\nimport com.alipay.sofa.ark.container.service.extension.spi.impl.ServiceBImpl3;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.service.extension.ArkServiceLoader;\nimport com.alipay.sofa.ark.spi.service.extension.Extensible;\nimport com.alipay.sofa.ark.spi.service.extension.Extension;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.net.URL;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class ExtensionServiceTest extends BaseTest {\n\n    private URL  jarURL = ArkContainerTest.class.getClassLoader().getResource(\"test.jar\");\n    ArkContainer arkContainer;\n\n    @Override\n    public void before() {\n        String[] args = new String[] { \"-Ajar=\" + jarURL.toExternalForm() };\n        arkContainer = (ArkContainer) ArkContainer.main(args);\n    }\n\n    @Override\n    public void after() {\n        arkContainer.stop();\n    }\n\n    @Test\n    public void testExtensionServiceNotInstance() {\n        PluginManagerService pluginManagerService = ArkServiceContainerHolder.getContainer()\n            .getService(PluginManagerService.class);\n        PluginModel pluginModel = new PluginModel().setPluginClassLoader(\n            this.getClass().getClassLoader()).setPluginName(\"mock-plugin\");\n        pluginManagerService.registerPlugin(pluginModel);\n        try {\n            ArkServiceLoader.loadExtensionFromArkPlugin(ServiceA.class, \"\", \"mock-plugin\");\n        } catch (ArkRuntimeException ex) {\n            Assert.assertTrue(ex.getMessage().contains(\"not type of\"));\n        }\n    }\n\n    @Test\n    public void testNotExtensibleService() {\n        PluginManagerService pluginManagerService = ArkServiceContainerHolder.getContainer()\n            .getService(PluginManagerService.class);\n        PluginModel pluginModel = new PluginModel().setPluginClassLoader(\n            this.getClass().getClassLoader()).setPluginName(\"mock-plugin\");\n        pluginManagerService.registerPlugin(pluginModel);\n        try {\n            ArkServiceLoader.loadExtensionFromArkPlugin(ServiceC.class, \"\", \"mock-plugin\");\n        } catch (ArkRuntimeException ex) {\n            Assert.assertTrue(ex.getMessage().contains(\n                String.format(\"is not annotated by %s.\", Extensible.class)));\n        }\n    }\n\n    @Test\n    public void testNoExtensionAnnotation() {\n        PluginManagerService pluginManagerService = ArkServiceContainerHolder.getContainer()\n            .getService(PluginManagerService.class);\n        PluginModel pluginModel = new PluginModel().setPluginClassLoader(\n            this.getClass().getClassLoader()).setPluginName(\"mock-plugin\");\n        pluginManagerService.registerPlugin(pluginModel);\n        try {\n            ArkServiceLoader.loadExtensionFromArkPlugin(ServiceD.class, \"\", \"mock-plugin\");\n        } catch (ArkRuntimeException ex) {\n            Assert.assertTrue(ex.getMessage().contains(\n                String.format(\"is not annotated by %s.\", Extension.class)));\n        }\n    }\n\n    @Test\n    public void testExtensionServiceLoader() {\n        PluginManagerService pluginManagerService = ArkServiceContainerHolder.getContainer()\n            .getService(PluginManagerService.class);\n        PluginModel pluginModel = new PluginModel().setPluginClassLoader(\n            this.getClass().getClassLoader()).setPluginName(\"mock-plugin\");\n        pluginManagerService.registerPlugin(pluginModel);\n        ServiceB impl1 = ArkServiceLoader.loadExtensionFromArkPlugin(ServiceB.class, \"type1\",\n            \"mock-plugin\");\n        Assert.assertTrue(impl1 instanceof ServiceBImpl3);\n        ServiceB impl2 = ArkServiceLoader.loadExtensionFromArkPlugin(ServiceB.class, \"type2\",\n            \"mock-plugin\");\n        Assert.assertTrue(impl2 instanceof ServiceBImpl4);\n        ServiceB impl3 = ArkServiceLoader.loadExtensionFromArkPlugin(ServiceB.class, \"type1\",\n            \"mock-plugin\");\n        Assert.assertTrue(impl3 instanceof ServiceBImpl3);\n        ServiceB impl4 = ArkServiceLoader.loadExtensionFromArkPlugin(ServiceB.class, \"type2\",\n            \"mock-plugin\");\n        Assert.assertTrue(impl4 instanceof ServiceBImpl4);\n        Assert.assertFalse(impl1 == impl3);\n        Assert.assertFalse(impl2 == impl4);\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/extension/spi/ServiceA.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.extension.spi;\n\nimport com.alipay.sofa.ark.spi.service.extension.Extensible;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@Extensible(file = \"serviceA\")\npublic interface ServiceA {\n    String service();\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/extension/spi/ServiceB.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.extension.spi;\n\nimport com.alipay.sofa.ark.spi.service.extension.Extensible;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@Extensible(singleton = false)\npublic interface ServiceB {\n    String service();\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/extension/spi/ServiceC.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.extension.spi;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic interface ServiceC {\n    String service();\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/extension/spi/ServiceD.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.extension.spi;\n\nimport com.alipay.sofa.ark.spi.service.extension.Extensible;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@Extensible(file = \"serviceD\")\npublic interface ServiceD {\n    String service();\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/extension/spi/SingletonService.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.extension.spi;\n\nimport com.alipay.sofa.ark.spi.service.extension.Extensible;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@Extensible\npublic interface SingletonService {\n    String service();\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/extension/spi/impl/ServiceBImpl1.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.extension.spi.impl;\n\nimport com.alipay.sofa.ark.container.service.extension.spi.ServiceB;\nimport com.alipay.sofa.ark.spi.service.extension.Extension;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@Extension(value = \"type1\", order = 200)\npublic class ServiceBImpl1 implements ServiceB {\n    @Override\n    public String service() {\n        return \"ServiceBImpl1\";\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/extension/spi/impl/ServiceBImpl2.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.extension.spi.impl;\n\nimport com.alipay.sofa.ark.container.service.extension.spi.ServiceB;\nimport com.alipay.sofa.ark.spi.service.extension.Extension;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@Extension(value = \"type1\", order = 300)\npublic class ServiceBImpl2 implements ServiceB {\n    @Override\n    public String service() {\n        return \"ServiceBImpl2\";\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/extension/spi/impl/ServiceBImpl3.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.extension.spi.impl;\n\nimport com.alipay.sofa.ark.container.service.extension.spi.ServiceB;\nimport com.alipay.sofa.ark.spi.service.extension.Extension;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@Extension(value = \"type1\", order = 50)\npublic class ServiceBImpl3 implements ServiceB {\n    @Override\n    public String service() {\n        return \"ServiceBImpl3\";\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/extension/spi/impl/ServiceBImpl4.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.extension.spi.impl;\n\nimport com.alipay.sofa.ark.container.service.extension.spi.ServiceB;\nimport com.alipay.sofa.ark.spi.service.extension.Extension;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@Extension(value = \"type2\", order = 10)\npublic class ServiceBImpl4 implements ServiceB {\n    @Override\n    public String service() {\n        return \"ServiceBImpl4\";\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/extension/spi/impl/ServiceDImpl.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.extension.spi.impl;\n\nimport com.alipay.sofa.ark.container.service.extension.spi.ServiceD;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class ServiceDImpl implements ServiceD {\n    @Override\n    public String service() {\n        return \"ServiceD\";\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/extension/spi/impl/SingletonServiceImpl.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.extension.spi.impl;\n\nimport com.alipay.sofa.ark.container.service.extension.spi.SingletonService;\nimport com.alipay.sofa.ark.spi.service.extension.Extension;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@Extension(value = \"singletonService\", order = 200)\npublic class SingletonServiceImpl implements SingletonService {\n    @Override\n    public String service() {\n        return \"SingletonService\";\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/injection/InjectionServiceTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.injection;\n\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.container.registry.ContainerServiceProvider;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.spi.service.ArkInject;\nimport com.alipay.sofa.ark.spi.service.biz.BizFactoryService;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.classloader.ClassLoaderService;\nimport com.alipay.sofa.ark.spi.service.registry.RegistryService;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author qilong.zql\n * @since 0.4.0\n */\npublic class InjectionServiceTest extends BaseTest {\n    @Test\n    public void test() {\n        RegistryService registryService = ArkServiceContainerHolder.getContainer().getService(\n            RegistryService.class);\n        PluginMockService pluginMockService = new PluginMockService();\n        registryService.publishService(PluginMockService.class, pluginMockService,\n            new ContainerServiceProvider());\n        Assert.assertNotNull(pluginMockService.getBizFactoryService());\n        Assert.assertNotNull(pluginMockService.getBizManagerService());\n        Assert.assertNull(pluginMockService.getClassLoaderService());\n    }\n\n    public class PluginMockService {\n        @ArkInject\n        private BizManagerService bizManagerService;\n\n        @ArkInject\n        BizFactoryService         bizFactoryService;\n\n        @ArkInject\n        public ClassLoaderService classloaderService;\n\n        public BizManagerService getBizManagerService() {\n            return bizManagerService;\n        }\n\n        public BizFactoryService getBizFactoryService() {\n            return bizFactoryService;\n        }\n\n        public ClassLoaderService getClassLoaderService() {\n            return classloaderService;\n        }\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/pipeline/HandleArchiveTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.pipeline;\n\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.container.model.PluginModel;\nimport com.alipay.sofa.ark.container.pipeline.HandleArchiveStage;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.BIZ_ACTIVE_EXCLUDE;\nimport static com.alipay.sofa.ark.spi.constant.Constants.BIZ_ACTIVE_INCLUDE;\nimport static com.alipay.sofa.ark.spi.constant.Constants.PLUGIN_ACTIVE_EXCLUDE;\nimport static com.alipay.sofa.ark.spi.constant.Constants.PLUGIN_ACTIVE_INCLUDE;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class HandleArchiveTest {\n\n    @Test\n    public void testIncludeExcludePlugin() {\n        try {\n            HandleArchiveStage handleArchiveStage = new HandleArchiveStage();\n            System.setProperty(PLUGIN_ACTIVE_INCLUDE, \"pluginA\");\n            System.setProperty(PLUGIN_ACTIVE_EXCLUDE, \"pluginA,pluginB\");\n            PluginModel pluginModel = new PluginModel();\n            pluginModel.setPluginName(\"pluginA\");\n            Assert.assertFalse(handleArchiveStage.isPluginExcluded(pluginModel));\n            pluginModel.setPluginName(\"pluginB\");\n            Assert.assertTrue(handleArchiveStage.isPluginExcluded(pluginModel));\n            pluginModel.setPluginName(\"pluginC\");\n            Assert.assertTrue(handleArchiveStage.isPluginExcluded(pluginModel));\n        } finally {\n            System.clearProperty(PLUGIN_ACTIVE_EXCLUDE);\n            System.clearProperty(PLUGIN_ACTIVE_INCLUDE);\n        }\n    }\n\n    @Test\n    public void testIncludePlugin() {\n        try {\n            HandleArchiveStage handleArchiveStage = new HandleArchiveStage();\n            System.setProperty(PLUGIN_ACTIVE_INCLUDE, \"pluginA\");\n            PluginModel pluginModel = new PluginModel();\n            pluginModel.setPluginName(\"pluginA\");\n            Assert.assertFalse(handleArchiveStage.isPluginExcluded(pluginModel));\n            pluginModel.setPluginName(\"pluginB\");\n            Assert.assertTrue(handleArchiveStage.isPluginExcluded(pluginModel));\n            pluginModel.setPluginName(\"pluginC\");\n            Assert.assertTrue(handleArchiveStage.isPluginExcluded(pluginModel));\n        } finally {\n            System.clearProperty(PLUGIN_ACTIVE_INCLUDE);\n        }\n    }\n\n    @Test\n    public void testExcludePlugin() {\n        try {\n            HandleArchiveStage handleArchiveStage = new HandleArchiveStage();\n            System.setProperty(PLUGIN_ACTIVE_EXCLUDE, \"pluginA,pluginB\");\n            PluginModel pluginModel = new PluginModel();\n            pluginModel.setPluginName(\"pluginA\");\n            Assert.assertTrue(handleArchiveStage.isPluginExcluded(pluginModel));\n            pluginModel.setPluginName(\"pluginB\");\n            Assert.assertTrue(handleArchiveStage.isPluginExcluded(pluginModel));\n            pluginModel.setPluginName(\"pluginC\");\n            Assert.assertFalse(handleArchiveStage.isPluginExcluded(pluginModel));\n        } finally {\n            System.clearProperty(PLUGIN_ACTIVE_EXCLUDE);\n        }\n    }\n\n    @Test\n    public void testNoIncludeExcludeBiz() {\n        HandleArchiveStage handleArchiveStage = new HandleArchiveStage();\n        PluginModel pluginModel = new PluginModel();\n        pluginModel.setPluginName(\"pluginA\");\n        Assert.assertFalse(handleArchiveStage.isPluginExcluded(pluginModel));\n        pluginModel.setPluginName(\"pluginB\");\n        Assert.assertFalse(handleArchiveStage.isPluginExcluded(pluginModel));\n    }\n\n    @Test\n    public void testIncludeExcludeBiz() {\n        try {\n            HandleArchiveStage handleArchiveStage = new HandleArchiveStage();\n            System.setProperty(BIZ_ACTIVE_INCLUDE, \"bizA:1.0.0\");\n            System.setProperty(BIZ_ACTIVE_EXCLUDE, \"bizA:1.0.0,BizB:1.0.0\");\n            BizModel bizModel = new BizModel();\n            bizModel.setBizName(\"bizA\").setBizVersion(\"1.0.0\");\n            Assert.assertFalse(handleArchiveStage.isBizExcluded(bizModel));\n            bizModel.setBizName(\"bizB\");\n            Assert.assertTrue(handleArchiveStage.isBizExcluded(bizModel));\n            bizModel.setBizName(\"bizC\");\n            Assert.assertTrue(handleArchiveStage.isBizExcluded(bizModel));\n        } finally {\n            System.clearProperty(BIZ_ACTIVE_INCLUDE);\n            System.clearProperty(BIZ_ACTIVE_EXCLUDE);\n        }\n    }\n\n    @Test\n    public void testIncludeBiz() {\n        try {\n            HandleArchiveStage handleArchiveStage = new HandleArchiveStage();\n            System.setProperty(BIZ_ACTIVE_INCLUDE, \"bizA:1.0.0\");\n            BizModel bizModel = new BizModel();\n            bizModel.setBizName(\"bizA\").setBizVersion(\"1.0.0\");\n            Assert.assertFalse(handleArchiveStage.isBizExcluded(bizModel));\n            bizModel.setBizName(\"pluginB\");\n            Assert.assertTrue(handleArchiveStage.isBizExcluded(bizModel));\n            bizModel.setBizName(\"pluginC\");\n            Assert.assertTrue(handleArchiveStage.isBizExcluded(bizModel));\n        } finally {\n            System.clearProperty(BIZ_ACTIVE_INCLUDE);\n        }\n    }\n\n    @Test\n    public void testExcludeBiz() {\n        try {\n            HandleArchiveStage handleArchiveStage = new HandleArchiveStage();\n            System.setProperty(BIZ_ACTIVE_EXCLUDE, \"bizA:1.0.0,bizB:1.0.0\");\n            BizModel bizModel = new BizModel();\n            bizModel.setBizName(\"bizA\").setBizVersion(\"1.0.0\");\n            Assert.assertTrue(handleArchiveStage.isBizExcluded(bizModel));\n            bizModel.setBizName(\"bizB\");\n            Assert.assertTrue(handleArchiveStage.isBizExcluded(bizModel));\n            bizModel.setBizName(\"bizC\");\n            Assert.assertFalse(handleArchiveStage.isBizExcluded(bizModel));\n        } finally {\n            System.clearProperty(BIZ_ACTIVE_EXCLUDE);\n        }\n    }\n\n    @Test\n    public void testNoIncludeExcludePlugin() {\n        HandleArchiveStage handleArchiveStage = new HandleArchiveStage();\n        BizModel bizModel = new BizModel();\n        bizModel.setBizName(\"bizA\").setBizVersion(\"1.0.0\");\n        Assert.assertFalse(handleArchiveStage.isBizExcluded(bizModel));\n        bizModel.setBizName(\"bizB\");\n        Assert.assertFalse(handleArchiveStage.isBizExcluded(bizModel));\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/plugin/PluginCommandProviderTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.plugin;\n\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.container.model.PluginModel;\nimport com.alipay.sofa.ark.container.service.plugin.PluginCommandProvider.PluginCommand;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport org.junit.Test;\n\nimport java.lang.reflect.Field;\nimport java.net.URL;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport static com.google.common.collect.Sets.newHashSet;\nimport static org.junit.Assert.*;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class PluginCommandProviderTest {\n\n    @Test\n    public void testPluginCommandFormat() {\n\n        PluginCommandProvider pluginCommandProvider = new PluginCommandProvider();\n        assertFalse(pluginCommandProvider.validate(\" plugin \"));\n        assertTrue(pluginCommandProvider.validate(\" plugin -h \"));\n        assertTrue(pluginCommandProvider.validate(\" plugin -m pluginA \"));\n        assertTrue(pluginCommandProvider.validate(\" plugin -s pluginB pluginA \"));\n        assertTrue(pluginCommandProvider.validate(\" plugin -d plugin* \"));\n        assertTrue(pluginCommandProvider.validate(\" plugin -m -d -s plugin* \"));\n        assertTrue(pluginCommandProvider.validate(\" plugin -msd pluginA \"));\n\n        assertFalse(pluginCommandProvider.validate(\" plu\"));\n        assertFalse(pluginCommandProvider.validate(\" plugin -h pluginA \"));\n        assertFalse(pluginCommandProvider.validate(\" plugin -hm pluginA \"));\n        assertFalse(pluginCommandProvider.validate(\" plugin -mb pluginA \"));\n        assertFalse(pluginCommandProvider.validate(\" plugin -m -b pluginA \"));\n        assertFalse(pluginCommandProvider.validate(\" plugin -m  \"));\n    }\n\n    @Test\n    public void testPluginCommandProcess() {\n\n        URL[] urls = ClassLoaderUtils.getURLs(this.getClass().getClassLoader());\n        final Plugin pluginA = new PluginModel().setPluginName(\"pluginA\")\n            .setImportClasses(\"com.google.common.io.AppendableWriter\")\n            .setImportPackages(\" com.google.common.cache, com.google.common.base \")\n            .setImportResources(\" import_resource1\").setExportPackages(\"com.alipay.sofa.*\")\n            .setExportResources(\" export_resource1, export_resource2\")\n            .setPluginActivator(\"com.alipay.sofa.pluginA.Activator\").setClassPath(urls)\n            .setGroupId(\"com.alipay.sofa\").setArtifactId(\"ark-mock-pluginA\").setVersion(\"1.0.0\");\n\n        final Plugin pluginB = new PluginModel().setPluginName(\"pluginB\")\n            .setImportClasses(\"com.google.common.io.AppendableWriter\")\n            .setImportPackages(\" com.google.common.cache, com.google.common.base \")\n            .setImportResources(\" import_resource1\").setExportPackages(\"com.alipay.sofa.*\")\n            .setExportResources(\" export_resource1, export_resource2\")\n            .setPluginActivator(\"com.alipay.sofa.pluginA.Activator\").setClassPath(urls)\n            .setGroupId(\"com.alipay.sofa\").setArtifactId(\"ark-mock-pluginB\").setVersion(\"1.0.0\");\n\n        final Set<String> set = new HashSet<>();\n        set.add(\"pluginA\");\n        set.add(\"pluginB\");\n\n        PluginManagerService pluginManagerService = mock(PluginManagerService.class);\n        when(pluginManagerService.getAllPluginNames()).thenReturn(set);\n        when(pluginManagerService.getPluginByName(\"pluginA\")).thenReturn(pluginA);\n        when(pluginManagerService.getPluginByName(\"pluginB\")).thenReturn(pluginB);\n\n        PluginCommandProvider pluginCommandProvider = new PluginCommandProvider();\n        try {\n            Field field = PluginCommandProvider.class.getDeclaredField(\"pluginManagerService\");\n            field.setAccessible(true);\n            field.set(pluginCommandProvider, pluginManagerService);\n        } catch (Throwable throwable) {\n            // ignore\n        }\n\n        String errorMessage = \"Error command format. Pls type 'plugin -h' to get help message\\n\";\n\n        assertTrue(errorMessage.equals(pluginCommandProvider.handleCommand(\"plu\")));\n        assertTrue(errorMessage.equals(pluginCommandProvider.handleCommand(\"plugin -h pluginA\")));\n        assertTrue(errorMessage.equals(pluginCommandProvider.handleCommand(\"plugin -b pluginA\")));\n        assertTrue(errorMessage.equals(pluginCommandProvider.handleCommand(\"plu\")));\n\n        assertTrue(pluginCommandProvider.getHelp().equals(\n            pluginCommandProvider.handleCommand(\"plugin -h\")));\n        assertTrue(errorMessage.equals(pluginCommandProvider.handleCommand(\"plugin \")));\n\n        String details = pluginCommandProvider.handleCommand(\"plugin -m -d -s pluginA\");\n        assertTrue(details.contains(\"Activator\"));\n        assertTrue(details.contains(\"GroupId\"));\n\n        details = pluginCommandProvider.handleCommand(\"plugin -d pluginB\");\n        assertTrue(details.contains(\"GroupId\"));\n        assertFalse(details.contains(\"Activator\"));\n\n        details = pluginCommandProvider.handleCommand(\"plugin -m plugin.\");\n        assertTrue(details.contains(\"pluginA\"));\n        assertTrue(details.contains(\"pluginB\"));\n\n        details = pluginCommandProvider.handleCommand(\"plugin -m pluginC\");\n        assertTrue(details.contains(\"no matched plugin candidates.\"));\n    }\n\n    @Test\n    public void testPluginList() {\n\n        PluginCommandProvider pluginCommandProvider = new PluginCommandProvider();\n        PluginCommand pluginCommand = pluginCommandProvider.new PluginCommand(null);\n        assertFalse(pluginCommand.isValidate());\n        pluginCommand = pluginCommandProvider.new PluginCommand(\"plugin -\");\n        assertFalse(pluginCommand.isValidate());\n        pluginCommand = pluginCommandProvider.new PluginCommand(\"plugin -a\");\n\n        PluginManagerService pluginManagerService = mock(PluginManagerService.class);\n        try {\n            Field field = PluginCommandProvider.class.getDeclaredField(\"pluginManagerService\");\n            field.setAccessible(true);\n            field.set(pluginCommandProvider, pluginManagerService);\n        } catch (Throwable throwable) {\n            // ignore\n        }\n\n        assertEquals(\"no plugins.\\nplugin count = 0\\n\", pluginCommand.process());\n        when(pluginManagerService.getAllPluginNames()).thenReturn(newHashSet(\"a\"));\n        assertEquals(\"a\\nplugin count = 1\\n\", pluginCommand.process());\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/plugin/PluginFactoryServiceTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.plugin;\n\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.loader.JarPluginArchive;\nimport com.alipay.sofa.ark.loader.archive.JarFileArchive;\nimport com.alipay.sofa.ark.loader.jar.JarFile;\nimport com.alipay.sofa.ark.spi.archive.PluginArchive;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginFactoryService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport static com.alipay.sofa.ark.api.ArkConfigs.putStringValue;\nimport static com.alipay.sofa.ark.spi.constant.Constants.PLUGIN_EXTENSION_FORMAT;\nimport static java.lang.String.format;\nimport static java.util.Arrays.asList;\nimport static org.junit.Assert.*;\nimport static org.mockito.Mockito.*;\n\n/**\n * @author qilong.zql\n * @since 0.4.0\n */\npublic class PluginFactoryServiceTest extends BaseTest {\n    private PluginFactoryService pluginFactoryService = new PluginFactoryServiceImpl();\n\n    @Test\n    public void test() throws Throwable {\n        ClassLoader cl = Thread.currentThread().getContextClassLoader();\n        URL samplePlugin = cl.getResource(\"sample-plugin.jar\");\n        Plugin plugin = pluginFactoryService.createPlugin(new File(samplePlugin.getFile()));\n        assertNotNull(plugin);\n    }\n\n    @Test\n    public void testCreatePluginWithExtensions() throws Throwable {\n\n        ClassLoader cl = Thread.currentThread().getContextClassLoader();\n        URL samplePlugin = cl.getResource(\"sample-plugin.jar\");\n        File file = new File(samplePlugin.getFile());\n        JarFile pluginFile = new JarFile(file);\n        JarFileArchive jarFileArchive = new JarFileArchive(pluginFile);\n        JarPluginArchive jarPluginArchive = new JarPluginArchive(jarFileArchive);\n\n        // inject\n        URL[] extensions = new URL[1];\n        URL sampleBiz = cl.getResource(\"sample-biz.jar\");\n        JarFile bizFile = new JarFile(new File(sampleBiz.getFile()));\n        extensions[0] = bizFile.getUrl();\n\n        // export\n        Set<String> exportPackages = new HashSet<>();\n        exportPackages.add(\"com.alipay.test.export.*\");\n        putStringValue(format(PLUGIN_EXTENSION_FORMAT, \"sample-ark-plugin\"), \"tracer-core:3.0.10\");\n\n        Plugin plugin = pluginFactoryService.createPlugin(jarPluginArchive, extensions,\n            exportPackages);\n        assertNotNull(plugin);\n        assertEquals(plugin.getExportPackages().size(), 2);\n        assertTrue(asList(plugin.getClassPath()).contains(bizFile.getUrl()));\n    }\n\n    @Test\n    public void testCreateEmbedPlugin() throws IOException {\n        ClassLoader cl = Thread.currentThread().getContextClassLoader();\n        URL samplePlugin = cl.getResource(\"sample-plugin.jar\");\n        PluginArchive archive = new JarPluginArchive(new JarFileArchive(new File(\n            samplePlugin.getFile())));\n        Plugin plugin = pluginFactoryService.createEmbedPlugin(archive, this.getClass()\n            .getClassLoader());\n        assertNotNull(plugin);\n    }\n\n    @Test(expected = ArkRuntimeException.class)\n    public void testDeploy() {\n        PluginDeployServiceImpl pluginDeployServiceImpl = new PluginDeployServiceImpl();\n        PluginManagerService pluginManagerService = mock(PluginManagerService.class);\n        Plugin plugin = mock(Plugin.class);\n        doThrow(new ArkRuntimeException(\"test\")).when(plugin).start();\n        when(pluginManagerService.getPluginsInOrder()).thenReturn(asList(plugin));\n        pluginDeployServiceImpl.pluginManagerService = pluginManagerService;\n        pluginDeployServiceImpl.deploy();\n    }\n\n    @Test(expected = ArkRuntimeException.class)\n    public void testUndeploy() {\n        PluginDeployServiceImpl pluginDeployServiceImpl = new PluginDeployServiceImpl();\n        PluginManagerService pluginManagerService = mock(PluginManagerService.class);\n        Plugin plugin = mock(Plugin.class);\n        doThrow(new ArkRuntimeException(\"test\")).when(plugin).stop();\n        when(pluginManagerService.getPluginsInOrder()).thenReturn(asList(plugin));\n        pluginDeployServiceImpl.pluginManagerService = pluginManagerService;\n        pluginDeployServiceImpl.unDeploy();\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/plugin/PluginManagerServiceTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.plugin;\n\nimport com.alipay.sofa.ark.container.model.PluginModel;\nimport com.alipay.sofa.ark.container.service.plugin.PluginManagerServiceImpl;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class PluginManagerServiceTest {\n\n    private PluginManagerService pluginManagerService = new PluginManagerServiceImpl();\n\n    @Test\n    public void testGetPluginByName() {\n        PluginModel plugin = new PluginModel();\n        plugin.setPluginName(\"plugin A\");\n        pluginManagerService.registerPlugin(plugin);\n        assertNotNull(pluginManagerService.getPluginByName(plugin.getPluginName()));\n        assertNull(pluginManagerService.getPluginByName(\"test\"));\n    }\n\n    @Test\n    public void testGetAllPluginNames() {\n        PluginModel pluginA = new PluginModel();\n        pluginA.setPluginName(\"plugin A\");\n        pluginManagerService.registerPlugin(pluginA);\n\n        PluginModel pluginB = new PluginModel();\n        pluginB.setPluginName(\"plugin B\");\n        pluginManagerService.registerPlugin(pluginB);\n\n        assertTrue(pluginManagerService.getAllPluginNames().contains(pluginA.getPluginName()));\n        assertTrue(pluginManagerService.getAllPluginNames().contains(pluginB.getPluginName()));\n        assertEquals(2, pluginManagerService.getAllPluginNames().size());\n    }\n\n    @Test\n    public void testGetPluginsInOrder() {\n\n        PluginModel pluginA = new PluginModel();\n        pluginA.setPluginName(\"plugin A\").setPriority(\"100\");\n        pluginManagerService.registerPlugin(pluginA);\n\n        PluginModel pluginB = new PluginModel();\n        pluginB.setPluginName(\"plugin B\").setPriority(\"10\");\n        pluginManagerService.registerPlugin(pluginB);\n\n        PluginModel pluginC = new PluginModel();\n        pluginC.setPluginName(\"plugin C\").setPriority(\"1000\");\n        pluginManagerService.registerPlugin(pluginC);\n\n        assertEquals(3, pluginManagerService.getPluginsInOrder().size());\n        assertEquals(pluginB, pluginManagerService.getPluginsInOrder().get(0));\n        assertEquals(pluginA, pluginManagerService.getPluginsInOrder().get(1));\n        assertEquals(pluginC, pluginManagerService.getPluginsInOrder().get(2));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/registry/ServiceRegistrationTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.registry;\n\nimport com.alipay.sofa.ark.common.util.ClassUtils;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.container.model.PluginContextImpl;\nimport com.alipay.sofa.ark.container.model.PluginModel;\nimport com.alipay.sofa.ark.container.registry.ContainerServiceProvider;\nimport com.alipay.sofa.ark.container.registry.DefaultServiceFilter;\nimport com.alipay.sofa.ark.container.registry.PluginServiceProvider;\nimport com.alipay.sofa.ark.container.registry.ServiceMetadataImpl;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.container.service.classloader.PluginClassLoader;\nimport com.alipay.sofa.ark.container.testdata.ITest;\nimport com.alipay.sofa.ark.container.testdata.activator.PluginActivatorA;\nimport com.alipay.sofa.ark.container.testdata.activator.PluginActivatorADup;\nimport com.alipay.sofa.ark.container.testdata.activator.PluginActivatorB;\nimport com.alipay.sofa.ark.container.testdata.activator.PluginActivatorC;\nimport com.alipay.sofa.ark.container.testdata.impl.TestObjectA;\nimport com.alipay.sofa.ark.container.testdata.impl.TestObjectB;\nimport com.alipay.sofa.ark.container.testdata.impl.TestObjectC;\nimport com.alipay.sofa.ark.spi.model.Plugin;\nimport com.alipay.sofa.ark.spi.registry.ServiceFilter;\nimport com.alipay.sofa.ark.spi.registry.ServiceProvider;\nimport com.alipay.sofa.ark.spi.registry.ServiceProviderType;\nimport com.alipay.sofa.ark.spi.registry.ServiceReference;\nimport com.alipay.sofa.ark.spi.service.classloader.ClassLoaderService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginDeployService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport com.alipay.sofa.ark.spi.service.registry.RegistryService;\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.net.URL;\nimport java.util.List;\n\nimport static com.google.common.collect.Sets.newHashSet;\nimport static org.junit.Assert.*;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author ruoshan\n * @since 0.1.0\n */\npublic class ServiceRegistrationTest extends BaseTest {\n\n    private RegistryService      registryService;\n\n    private PluginManagerService pluginManagerService;\n\n    private PluginDeployService  pluginDeployService;\n\n    private ClassLoaderService   classloaderService;\n\n    private URL                  classPathURL    = ServiceRegistrationTest.class.getClassLoader()\n                                                     .getResource(\"\");\n\n    private static final String  INTERFACE_CLASS = ITest.class.getName();\n\n    @Before\n    public void before() {\n        super.before();\n        registryService = ArkServiceContainerHolder.getContainer()\n            .getService(RegistryService.class);\n        pluginManagerService = ArkServiceContainerHolder.getContainer().getService(\n            PluginManagerService.class);\n        pluginDeployService = ArkServiceContainerHolder.getContainer().getService(\n            PluginDeployService.class);\n        classloaderService = ArkServiceContainerHolder.getContainer().getService(\n            ClassLoaderService.class);\n    }\n\n    @After\n    @SuppressWarnings(\"unchecked\")\n    public void after() {\n        registryService.unPublishServices(new DefaultServiceFilter()\n            .setServiceInterface(ITest.class));\n        super.after();\n    }\n\n    @Test\n    @SuppressWarnings(\"unchecked\")\n    public void testPublishService() {\n        ServiceReference<ITest> iTestServiceReference = registryService.publishService(ITest.class,\n            new TestObjectA(), new ContainerServiceProvider());\n        Assert.assertNotNull(iTestServiceReference);\n        assertEquals(TestObjectA.OUTPUT, iTestServiceReference.getService().test());\n\n        int c = registryService.unPublishServices(new DefaultServiceFilter().setServiceInterface(\n            ITest.class).setProviderType(ServiceProviderType.ARK_CONTAINER));\n        assertTrue(c == 1);\n\n        iTestServiceReference = registryService.referenceService(ITest.class);\n        Assert.assertNull(iTestServiceReference);\n    }\n\n    @Test\n    public void testReferenceService() {\n        registryService.publishService(ITest.class, new TestObjectA(),\n            new ContainerServiceProvider());\n        ServiceReference<ITest> iTestServiceReference = registryService\n            .referenceService(ITest.class);\n        Assert.assertNotNull(iTestServiceReference);\n        assertEquals(TestObjectA.OUTPUT, iTestServiceReference.getService().test());\n    }\n\n    @Test\n    @SuppressWarnings(\"unchecked\")\n    public void testPublishDuplicateService() {\n        registryService.publishService(ITest.class, new TestObjectA(),\n            new ContainerServiceProvider());\n        registryService.publishService(ITest.class, new TestObjectB(),\n            new ContainerServiceProvider());\n\n        // 只有第一个服务发布成功\n        assertEquals(1, registryService.referenceServices(ITest.class).size());\n        assertEquals(TestObjectA.OUTPUT, registryService.referenceService(ITest.class).getService()\n            .test());\n\n        registryService.unPublishServices(new DefaultServiceFilter()\n            .setServiceInterface(ITest.class));\n\n        assertEquals(0, registryService.referenceServices(ITest.class).size());\n\n        registryService.publishService(ITest.class, new TestObjectA(), \"testA\",\n            new ContainerServiceProvider());\n        registryService.publishService(ITest.class, new TestObjectB(), \"testB\",\n            new ContainerServiceProvider());\n\n        assertEquals(\n            2,\n            registryService.referenceServices(\n                new DefaultServiceFilter().setServiceInterface(ITest.class)).size());\n        assertEquals(TestObjectA.OUTPUT, registryService.referenceService(ITest.class, \"testA\")\n            .getService().test());\n        assertEquals(TestObjectB.OUTPUT, registryService.referenceService(ITest.class, \"testB\")\n            .getService().test());\n\n        int c = registryService.unPublishServices(new DefaultServiceFilter().setUniqueId(\"testA\"));\n        assertTrue(c == 1);\n\n        c = registryService.unPublishServices(new DefaultServiceFilter().setProviderType(\n            ServiceProviderType.ARK_CONTAINER).setServiceInterface(ITest.class));\n        assertTrue(c == 1);\n\n        assertEquals(0, registryService.referenceServices(ITest.class).size());\n    }\n\n    @Test\n    public void testPublishDuplicateServiceInPlugin() throws Exception {\n\n        PluginModel pluginA = new PluginModel();\n        PluginContextImpl pluginContext = new PluginContextImpl(pluginA);\n        pluginA\n            .setPluginName(\"plugin A\")\n            .setPriority(\"10\")\n            .setClassPath(new URL[] { classPathURL })\n            .setImportClasses(StringUtils.EMPTY_STRING)\n            .setImportPackages(StringUtils.EMPTY_STRING)\n            .setExportPackages(ClassUtils.getPackageName(INTERFACE_CLASS))\n            .setExportClasses(StringUtils.EMPTY_STRING)\n            .setImportResources(StringUtils.EMPTY_STRING)\n            .setExportResources(StringUtils.EMPTY_STRING)\n            .setPluginActivator(PluginActivatorADup.class.getName())\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginA.getPluginName(), pluginA.getClassPath()))\n            .setPluginContext(new PluginContextImpl(pluginA));\n\n        pluginManagerService.registerPlugin(pluginA);\n        classloaderService.prepareExportClassAndResourceCache();\n        pluginDeployService.deploy();\n\n        assertEquals(\n            1,\n            registryService.referenceServices(\n                pluginA.getPluginClassLoader().loadClass(ITest.class.getCanonicalName())).size());\n\n        int c = registryService.unPublishServices(new DefaultServiceFilter()\n            .setProviderType(ServiceProviderType.ARK_PLUGIN));\n        assertEquals(1, c);\n\n        assertEquals(pluginA, pluginContext.getPlugin());\n        assertEquals(null, pluginContext.getPlugin(\"notexists\"));\n        assertEquals(PluginClassLoader.class, pluginContext.getClassLoader().getClass());\n        assertEquals(newHashSet(\"plugin A\"), pluginContext.getPluginNames());\n    }\n\n    @Test\n    @SuppressWarnings(\"unchecked\")\n    public void testMultipleService() throws Exception {\n        // 非插件发布的服务，优先级别最低\n        registryService.publishService(ITest.class, new TestObjectA(),\n            new ContainerServiceProvider());\n\n        PluginModel pluginA = new PluginModel();\n        pluginA\n            .setPluginName(\"plugin A\")\n            .setPriority(\"10\")\n            .setClassPath(new URL[] { classPathURL })\n            .setImportClasses(StringUtils.EMPTY_STRING)\n            .setImportPackages(StringUtils.EMPTY_STRING)\n            .setExportPackages(ClassUtils.getPackageName(INTERFACE_CLASS))\n            .setExportClasses(\"\")\n            .setImportResources(StringUtils.EMPTY_STRING)\n            .setExportResources(StringUtils.EMPTY_STRING)\n            .setPluginActivator(PluginActivatorA.class.getName())\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginA.getPluginName(), pluginA.getClassPath()))\n            .setPluginContext(new PluginContextImpl(pluginA));\n\n        PluginModel pluginB = new PluginModel();\n        pluginB\n            .setPluginName(\"plugin B\")\n            .setPriority(\"1\")\n            .setClassPath(new URL[] { classPathURL })\n            .setImportClasses(INTERFACE_CLASS)\n            .setImportPackages(StringUtils.EMPTY_STRING)\n            .setExportPackages(\"\")\n            .setExportClasses(\"\")\n            .setImportResources(StringUtils.EMPTY_STRING)\n            .setExportResources(StringUtils.EMPTY_STRING)\n            .setPluginActivator(PluginActivatorB.class.getName())\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginB.getPluginName(), pluginB.getClassPath()))\n            .setPluginContext(new PluginContextImpl(pluginB));\n\n        PluginModel pluginC = new PluginModel();\n        pluginC\n            .setPluginName(\"plugin C\")\n            .setPriority(\"100\")\n            .setClassPath(new URL[] { classPathURL })\n            .setImportClasses(INTERFACE_CLASS)\n            .setImportPackages(StringUtils.EMPTY_STRING)\n            .setExportPackages(\"\")\n            .setExportClasses(\"\")\n            .setImportResources(StringUtils.EMPTY_STRING)\n            .setExportResources(StringUtils.EMPTY_STRING)\n            .setPluginActivator(PluginActivatorC.class.getName())\n            .setPluginClassLoader(\n                new PluginClassLoader(pluginC.getPluginName(), pluginC.getClassPath()))\n            .setPluginContext(new PluginContextImpl(pluginC));\n\n        pluginManagerService.registerPlugin(pluginA);\n        pluginManagerService.registerPlugin(pluginB);\n        pluginManagerService.registerPlugin(pluginC);\n\n        classloaderService.prepareExportClassAndResourceCache();\n        pluginDeployService.deploy();\n\n        Class iTest = pluginA.getPluginClassLoader().loadClass(ITest.class.getCanonicalName());\n        assertEquals(\n            3,\n            pluginA.getPluginContext()\n                .referenceServices(new DefaultServiceFilter().setServiceInterface(iTest)).size());\n\n        // 应该获取到优先级别比较高的服务\n        ServiceReference reference = pluginC.getPluginContext().referenceService(iTest);\n        PluginServiceProvider provider = (PluginServiceProvider) reference.getServiceMetadata()\n            .getServiceProvider();\n        assertEquals(pluginB.getPluginName(), provider.getPluginName());\n\n        List<ServiceReference> references = pluginC.getPluginContext().referenceServices(\n            new DefaultServiceFilter().setServiceInterface(iTest));\n\n        provider = (PluginServiceProvider) references.get(0).getServiceMetadata()\n            .getServiceProvider();\n        assertEquals(pluginB.getPluginName(), provider.getPluginName());\n\n    }\n\n    @Test\n    @SuppressWarnings(\"unchecked\")\n    public void testFilter() {\n        final PluginModel pluginA = new PluginModel();\n        pluginA.setPluginName(\"plugin A\").setPriority(\"10\");\n\n        PluginModel pluginB = new PluginModel();\n        pluginB.setPluginName(\"plugin B\").setPriority(\"1\");\n\n        pluginManagerService.registerPlugin(pluginA);\n        pluginManagerService.registerPlugin(pluginB);\n\n        registryService.publishService(ITest.class, new TestObjectA(), new PluginServiceProvider(\n            pluginA));\n\n        registryService.publishService(ITest.class, new TestObjectB(), new PluginServiceProvider(\n            pluginB));\n\n        registryService.publishService(ITest.class, new TestObjectC(),\n            new ContainerServiceProvider());\n\n        List<ServiceReference> references = registryService\n            .referenceServices(new DefaultServiceFilter().setServiceInterface(ITest.class)\n                .setProviderType(ServiceProviderType.ARK_PLUGIN));\n        assertTrue(2 == references.size());\n\n        PluginServiceProvider provider = (PluginServiceProvider) references.get(0)\n            .getServiceMetadata().getServiceProvider();\n        assertEquals(pluginB.getPluginName(), provider.getPluginName());\n\n        references = registryService.referenceServices(new ServiceFilter() {\n            @Override\n            public boolean match(ServiceReference serviceReference) {\n                ServiceProvider serviceProvider = serviceReference.getServiceMetadata()\n                    .getServiceProvider();\n                if (serviceProvider instanceof PluginServiceProvider) {\n                    if (((PluginServiceProvider) serviceProvider).getPluginName().equals(\n                        pluginA.getPluginName())) {\n                        return true;\n                    }\n                }\n                return false;\n            }\n        });\n        assertTrue(1 == references.size());\n        provider = (PluginServiceProvider) references.get(0).getServiceMetadata()\n            .getServiceProvider();\n        assertEquals(pluginA.getPluginName(), provider.getPluginName());\n\n        references = registryService.referenceServices(new DefaultServiceFilter()\n            .setServiceInterface(ITest.class));\n        assertTrue(3 == references.size());\n\n        references = registryService.referenceServices(new DefaultServiceFilter().setProviderType(\n            ServiceProviderType.ARK_CONTAINER).setServiceInterface(ITest.class));\n        assertTrue(1 == references.size());\n\n        assertEquals(\"TestObject C\", ((TestObjectC) references.get(0).getService()).test());\n    }\n\n    @Test\n    public void testContainerService() {\n        registryService.publishService(ITest.class, new TestObjectA(),\n            new ContainerServiceProvider(20000));\n        registryService.publishService(ITest.class, new TestObjectB(),\n            new ContainerServiceProvider(100));\n        registryService.publishService(ITest.class, new TestObjectC(),\n            new ContainerServiceProvider(200));\n\n        assertEquals(TestObjectB.OUTPUT, registryService.referenceService(ITest.class).getService()\n            .test());\n        assertEquals(3, registryService.referenceServices(ITest.class).size());\n    }\n\n    @Test\n    public void testEqualsHashCode() {\n\n        ContainerServiceProvider containerServiceProvider = new ContainerServiceProvider();\n        containerServiceProvider.hashCode();\n        assertEquals(containerServiceProvider, containerServiceProvider);\n\n        ServiceMetadataImpl serviceMetadataImpl = new ServiceMetadataImpl(this.getClass(), \"a\",\n            containerServiceProvider);\n        serviceMetadataImpl.hashCode();\n        assertEquals(serviceMetadataImpl, serviceMetadataImpl);\n        assertFalse(serviceMetadataImpl.equals(null));\n\n        PluginServiceProvider pluginServiceProvider = new PluginServiceProvider(mock(Plugin.class));\n        pluginServiceProvider.hashCode();\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/retrieval/ClassInfoMethodTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.retrieval;\n\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.container.session.handler.ArkCommandHandler;\nimport com.alipay.sofa.ark.spi.service.injection.InjectionService;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author yanzhu\n * @date 2023/10/17 17:55\n */\npublic class ClassInfoMethodTest extends BaseTest {\n\n    @Test\n    public void testCreateClassInfo() {\n\n        String classInfo = ClassInfoMethod.createClassInfo(ArkCommandHandler.class, \"A1:V1\");\n\n        Assert.assertTrue(classInfo.contains(\"class-info\"));\n        Assert.assertTrue(classInfo.contains(\"code-source\"));\n        Assert.assertTrue(classInfo.contains(\"isInterface\"));\n        Assert.assertTrue(classInfo.contains(\"isAnnotation\"));\n        Assert.assertTrue(classInfo.contains(\"isEnum\"));\n        Assert.assertTrue(classInfo.contains(\"container-name\"));\n        Assert.assertTrue(classInfo.contains(\"simple-name\"));\n        Assert.assertTrue(classInfo.contains(\"modifier\"));\n        Assert.assertTrue(classInfo.contains(\"super-class\"));\n        Assert.assertTrue(classInfo.contains(\"class-loader\"));\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/retrieval/InfoQueryCommandProviderTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.retrieval;\n\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.container.session.handler.ArkCommandHandler;\nimport com.alipay.sofa.ark.spi.service.injection.InjectionService;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author yanzhu\n * @date 2023/10/17 17:55\n */\npublic class InfoQueryCommandProviderTest extends BaseTest {\n\n    private InjectionService         injectionService;\n    private InfoQueryCommandProvider queryCommandProvider;\n\n    @Override\n    public void before() {\n        super.before();\n\n        injectionService = arkServiceContainer.getService(InjectionService.class);\n\n        queryCommandProvider = new InfoQueryCommandProvider();\n        injectionService.inject(queryCommandProvider);\n\n        // trigger telnet command thread pool to be created\n        new ArkCommandHandler();\n    }\n\n    @Test\n    public void testInfoQueryCommandPattern() {\n        Assert.assertFalse(queryCommandProvider.validate(\"ck\"));\n\n        Assert.assertTrue(queryCommandProvider.validate(\"ck -h\"));\n        Assert.assertFalse(queryCommandProvider.validate(\"ck -c\"));\n        Assert.assertFalse(queryCommandProvider.validate(\"ck -ch\"));\n\n        Assert.assertTrue(queryCommandProvider.validate(\"ck -c com.example.HelloWorld\"));\n    }\n\n    @Test\n    public void testClassInfo() {\n        String classInfo = queryCommandProvider\n            .handleCommand(\"ck -c com.alipay.sofa.ark.container.session.handler.ArkCommandHandler\");\n\n        Assert.assertTrue(classInfo.contains(\"class-info\"));\n        Assert.assertTrue(classInfo.contains(\"code-source\"));\n        Assert.assertTrue(classInfo.contains(\"container-name\"));\n        Assert.assertTrue(classInfo.contains(\"class-loader\"));\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/retrieval/ViewRenderTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.retrieval;\n\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.container.session.handler.ArkCommandHandler;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author yanzhu\n * @date 2023/10/17 17:55\n */\npublic class ViewRenderTest extends BaseTest {\n\n    @Test\n    public void testRenderClassInfo() {\n\n        ClassInfoVO classInfo = new ClassInfoVO();\n        classInfo.setClassInfo(\"com.example.HelloWorld\");\n        classInfo.setCodeSource(\"/home/admin\");\n        classInfo.setInterface(false);\n        classInfo.setAnnotation(false);\n        classInfo.setEnum(false);\n        classInfo.setContainerName(\"A1:V1\");\n        classInfo.setSimpleName(\"helloWorld\");\n        classInfo.setModifier(\"public\");\n        classInfo.setSuperClass(new String[] { \"com.example\" });\n        classInfo.setClassloader(new String[] { \"com.example.ClassLoader\" });\n\n        String renderClassInfo = ViewRender.renderClassInfo(classInfo);\n\n        Assert.assertTrue(renderClassInfo.contains(\"class-info\"));\n        Assert.assertTrue(renderClassInfo.contains(\"code-source\"));\n        Assert.assertTrue(renderClassInfo.contains(\"isInterface\"));\n        Assert.assertTrue(renderClassInfo.contains(\"isAnnotation\"));\n        Assert.assertTrue(renderClassInfo.contains(\"isEnum\"));\n        Assert.assertTrue(renderClassInfo.contains(\"container-name\"));\n        Assert.assertTrue(renderClassInfo.contains(\"simple-name\"));\n        Assert.assertTrue(renderClassInfo.contains(\"modifier\"));\n        Assert.assertTrue(renderClassInfo.contains(\"super-class\"));\n        Assert.assertTrue(renderClassInfo.contains(\"class-loader\"));\n\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/service/session/CommandHandlerTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.service.session;\n\nimport com.alipay.sofa.ark.container.BaseTest;\nimport com.alipay.sofa.ark.container.registry.ContainerServiceProvider;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.container.session.handler.ArkCommandHandler;\nimport com.alipay.sofa.ark.spi.service.registry.RegistryService;\nimport com.alipay.sofa.ark.spi.service.session.CommandProvider;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author qilong.zql\n * @since 0.4.0\n */\npublic class CommandHandlerTest extends BaseTest {\n\n    @Test\n    public void test() {\n        ArkCommandHandler arkCommandHandler = new ArkCommandHandler();\n        RegistryService registryService = ArkServiceContainerHolder.getContainer().getService(\n            RegistryService.class);\n        registryService.publishService(CommandProvider.class, new MockCommandProvider(),\n            new ContainerServiceProvider());\n\n        Assert.assertTrue(arkCommandHandler.handleCommand(\"any\").contains(\"mock help\"));\n        Assert.assertTrue(\"mock command provider\".equals(arkCommandHandler.handleCommand(\"mock\")));\n    }\n\n    public class MockCommandProvider implements CommandProvider {\n\n        @Override\n        public String getHelp() {\n            return \"mock help\";\n        }\n\n        @Override\n        public boolean validate(String command) {\n            return command.contains(\"mock\");\n        }\n\n        @Override\n        public String handleCommand(String command) {\n            return \"mock command provider\";\n        }\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/session/NettyTelnetServerTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.session;\n\nimport com.alipay.sofa.ark.container.service.ArkServiceContainer;\nimport com.alipay.sofa.ark.container.session.NettyTelnetServer.NettyTelnetHandler;\nimport com.alipay.sofa.ark.container.session.NettyTelnetServer.NettyTelnetInitializer;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport io.netty.channel.Channel;\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.channel.ChannelPipeline;\nimport io.netty.channel.socket.SocketChannel;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.net.InetSocketAddress;\n\nimport static com.alipay.sofa.ark.common.util.EnvironmentUtils.getProperty;\nimport static com.alipay.sofa.ark.common.util.EnvironmentUtils.setProperty;\nimport static com.alipay.sofa.ark.container.service.ArkServiceContainerHolder.getContainer;\nimport static com.alipay.sofa.ark.container.service.ArkServiceContainerHolder.setContainer;\nimport static com.alipay.sofa.ark.spi.constant.Constants.*;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\npublic class NettyTelnetServerTest {\n\n    private ArkServiceContainer originalArkServiceContainer;\n\n    private String              originalSecurityEnable;\n\n    private String              originalTelnetPort;\n\n    @Before\n    public void before() {\n        originalArkServiceContainer = getContainer();\n        originalSecurityEnable = getProperty(TELNET_SERVER_SECURITY_ENABLE);\n        originalTelnetPort = getProperty(TELNET_PORT_ATTRIBUTE);\n    }\n\n    @After\n    public void after() {\n        setContainer(originalArkServiceContainer);\n        setProperty(TELNET_SERVER_SECURITY_ENABLE,\n            originalSecurityEnable != null ? originalSecurityEnable : \"\");\n        setProperty(TELNET_PORT_ATTRIBUTE, originalTelnetPort != null ? originalTelnetPort : \"\");\n    }\n\n    @Test\n    public void testNettyTelnetInitializer() throws Exception {\n\n        SocketChannel socketChannel = mock(SocketChannel.class);\n        ChannelPipeline pipeline = mock(ChannelPipeline.class);\n        when(socketChannel.pipeline()).thenReturn(pipeline);\n\n        InetSocketAddress inetSocketAddress = mock(InetSocketAddress.class);\n        when(socketChannel.remoteAddress()).thenReturn(inetSocketAddress);\n        when(inetSocketAddress.getHostName()).thenReturn(LOCAL_HOST + \"1\");\n        setContainer(mock(ArkServiceContainer.class));\n\n        NettyTelnetInitializer nettyTelnetInitializer = new NettyTelnetInitializer();\n        nettyTelnetInitializer.initChannel(socketChannel);\n\n        setProperty(TELNET_SERVER_SECURITY_ENABLE, \"true\");\n        nettyTelnetInitializer.initChannel(socketChannel);\n        verify(pipeline, times(4)).addLast(any());\n    }\n\n    @Test\n    public void testNettyTelnetHandler() throws Exception {\n\n        setContainer(mock(ArkServiceContainer.class));\n        NettyTelnetHandler nettyTelnetHandler = new NettyTelnetHandler();\n        ChannelHandlerContext context = mock(ChannelHandlerContext.class);\n        when(context.channel()).thenReturn(mock(Channel.class));\n\n        nettyTelnetHandler.channelActive(context);\n        nettyTelnetHandler.exceptionCaught(context, new Exception());\n        nettyTelnetHandler.channelRead0(context, \"\");\n        nettyTelnetHandler.channelRead0(context, \"q\");\n    }\n\n    @Test(expected = ArkRuntimeException.class)\n    public void testStandardTelnetServerImplWithInvalidNumber() {\n        setProperty(TELNET_PORT_ATTRIBUTE, \"a\");\n        new StandardTelnetServerImpl();\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/test/TestHelperTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.test;\n\nimport com.alipay.sofa.ark.container.ArkContainer;\nimport com.alipay.sofa.ark.container.ArkContainerTest;\nimport org.junit.Test;\n\nimport java.net.URL;\n\nimport static com.alipay.sofa.ark.container.ArkContainer.main;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\npublic class TestHelperTest {\n\n    private URL jarURL = ArkContainerTest.class.getClassLoader().getResource(\"test.jar\");\n\n    @Test\n    public void testCreateNoneDelegateTestClassLoader() {\n        ArkContainer arkContainer = null;\n        try {\n            String[] args = new String[] { \"-Ajar=\" + jarURL.toExternalForm(),\n                    \"-Aclasspath=\" + jarURL.toString() };\n            arkContainer = (ArkContainer) main(args);\n            TestHelper testHelper = new TestHelper(arkContainer);\n            assertTrue(testHelper.isStarted());\n            assertEquals(NoneDelegateTestClassLoader.class, testHelper\n                .createNoneDelegateTestClassLoader().getClass());\n        } finally {\n            if (arkContainer != null) {\n                arkContainer.stop();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/testdata/ITest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.testdata;\n\n/**\n * @author ruoshan\n * @since 0.1.0\n */\npublic interface ITest {\n\n    /**\n     * a simple facade\n     * @return\n     */\n    String test();\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/testdata/activator/PluginActivatorA.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.testdata.activator;\n\nimport com.alipay.sofa.ark.container.testdata.ITest;\nimport com.alipay.sofa.ark.container.testdata.impl.TestObjectA;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.model.PluginContext;\nimport com.alipay.sofa.ark.spi.service.PluginActivator;\n\n/**\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class PluginActivatorA implements PluginActivator {\n\n    @Override\n    public void start(PluginContext context) throws ArkRuntimeException {\n        context.publishService(ITest.class, new TestObjectA());\n    }\n\n    @Override\n    public void stop(PluginContext context) throws ArkRuntimeException {\n\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/testdata/activator/PluginActivatorADup.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.testdata.activator;\n\nimport com.alipay.sofa.ark.container.testdata.ITest;\nimport com.alipay.sofa.ark.container.testdata.impl.TestObjectA;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.model.PluginContext;\nimport com.alipay.sofa.ark.spi.service.PluginActivator;\n\n/**\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class PluginActivatorADup implements PluginActivator {\n\n    @Override\n    public void start(PluginContext context) throws ArkRuntimeException {\n        context.publishService(ITest.class, new TestObjectA());\n        context.publishService(ITest.class, new TestObjectA());\n    }\n\n    @Override\n    public void stop(PluginContext context) throws ArkRuntimeException {\n\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/testdata/activator/PluginActivatorB.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.testdata.activator;\n\nimport com.alipay.sofa.ark.container.testdata.ITest;\nimport com.alipay.sofa.ark.container.testdata.impl.TestObjectB;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.model.PluginContext;\nimport com.alipay.sofa.ark.spi.service.PluginActivator;\n\n/**\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class PluginActivatorB implements PluginActivator {\n\n    @Override\n    public void start(PluginContext context) throws ArkRuntimeException {\n        context.publishService(ITest.class, new TestObjectB());\n    }\n\n    @Override\n    public void stop(PluginContext context) throws ArkRuntimeException {\n\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/testdata/activator/PluginActivatorC.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.testdata.activator;\n\nimport com.alipay.sofa.ark.container.testdata.ITest;\nimport com.alipay.sofa.ark.container.testdata.impl.TestObjectC;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.model.PluginContext;\nimport com.alipay.sofa.ark.spi.service.PluginActivator;\n\n/**\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class PluginActivatorC implements PluginActivator {\n\n    @Override\n    public void start(PluginContext context) throws ArkRuntimeException {\n        context.publishService(ITest.class, new TestObjectC());\n    }\n\n    @Override\n    public void stop(PluginContext context) throws ArkRuntimeException {\n\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/testdata/classloader/ClassLoaderTestClass.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.testdata.classloader;\n\n/**\n *\n * @author ruoshan\n * @since 0.5.0\n */\npublic class ClassLoaderTestClass {\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/testdata/impl/TestObjectA.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.testdata.impl;\n\nimport com.alipay.sofa.ark.container.testdata.ITest;\n\n/**\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class TestObjectA implements ITest {\n\n    public static final String OUTPUT = \"TestObject A\";\n\n    @Override\n    public String test() {\n        return OUTPUT;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/testdata/impl/TestObjectB.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.testdata.impl;\n\nimport com.alipay.sofa.ark.container.testdata.ITest;\n\n/**\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class TestObjectB implements ITest {\n\n    public static final String OUTPUT = \"TestObject B\";\n\n    @Override\n    public String test() {\n        return OUTPUT;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/java/com/alipay/sofa/ark/container/testdata/impl/TestObjectC.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.container.testdata.impl;\n\nimport com.alipay.sofa.ark.container.testdata.ITest;\n\n/**\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class TestObjectC implements ITest {\n\n    @Override\n    public String test() {\n        return \"TestObject C\";\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/resources/META-INF/services/sofa-ark/com.alipay.sofa.ark.container.service.extension.spi.ServiceB",
    "content": "com.alipay.sofa.ark.container.service.extension.spi.impl.ServiceBImpl1\ncom.alipay.sofa.ark.container.service.extension.spi.impl.ServiceBImpl2\ncom.alipay.sofa.ark.container.service.extension.spi.impl.ServiceBImpl3\ncom.alipay.sofa.ark.container.service.extension.spi.impl.ServiceBImpl4"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/resources/META-INF/services/sofa-ark/com.alipay.sofa.ark.spi.service.biz.AddBizToStaticDeployHook",
    "content": "com.alipay.sofa.ark.container.service.biz.hook.TestAddBizToStaticDeployHook"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/resources/META-INF/services/sofa-ark/com.alipay.sofa.ark.spi.service.classloader.ClassLoaderHook",
    "content": "com.alipay.sofa.ark.container.service.classloader.hook.TestBizClassLoaderHook\ncom.alipay.sofa.ark.container.service.classloader.hook.TestPluginClassLoaderHook"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/resources/META-INF/services/sofa-ark/serviceA",
    "content": "com.alipay.sofa.ark.container.service.extension.spi.SingletonService"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/resources/META-INF/services/sofa-ark/serviceD",
    "content": "com.alipay.sofa.ark.container.service.extension.spi.impl.ServiceDImpl"
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/resources/export/folderA/test1.xml",
    "content": ""
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/resources/export/folderA/test2.xml",
    "content": ""
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/resources/export/folderB/test3.xml",
    "content": ""
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/resources/export/folderB/test4.xml",
    "content": ""
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/resources/multi_export.xml",
    "content": ""
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/resources/pluginA_export_resource1.xml",
    "content": ""
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/resources/pluginA_export_resource2.xml",
    "content": ""
  },
  {
    "path": "sofa-ark-parent/core-impl/container/src/test/resources/pluginA_not_export_resource.xml",
    "content": ""
  },
  {
    "path": "sofa-ark-parent/core-impl/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <artifactId>sofa-ark-parent</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n    </parent>\n\n    <artifactId>sofa-ark-core-impl</artifactId>\n    <name>${project.groupId}:${project.artifactId}</name>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>container</module>\n        <module>archive</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "sofa-ark-parent/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>sofa-ark-bom</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n        <relativePath>../sofa-ark-bom</relativePath>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>sofa-ark-parent</artifactId>\n    <name>${project.groupId}:${project.artifactId}</name>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>assembly</module>\n        <module>core</module>\n        <module>core-impl</module>\n        <module>support</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-gradle-plugin/README.md",
    "content": "# Ark Gradle打包插件使用\n`sofa-ark-gradle-plugin`模块是Ark打包工具的Gradle版本实现，和Maven打包工具`sofa-ark-maven-plugin`有同样的功能，用于打包ark包和biz包。\n# 配置\n`sofa-ark-gradle-plugin` 使用 arkConfig 来进行配置。\n\n# 如何使用\n1. 本地发布引用\n2. 远程仓库引入（待申请）\n\n参考`sofa-ark-plugin-gradle-plugin`的本地发布和引入。\n使用Gradle刷新后，如果一切正常，会在IDEA右侧Gradle任务列表中出现arkJar，具体如下： Tasks > build > arkJar，点击arkJar执行，会在指定的outputDirectory中输出ark包和biz包。"
  },
  {
    "path": "sofa-ark-parent/support/ark-gradle-plugin/build.gradle",
    "content": "plugins {\n    id 'java'\n    id 'java-gradle-plugin'\n    id 'maven-publish'\n}\n\next {\n    arkGradlePluginId = 'sofa-ark-gradle-plugin'\n}\n\ngroup = 'com.alipay.sofa'\nversion = '1.0.0'\nsourceCompatibility = '1.8'\n\npublishing {\n    publications {\n        maven(MavenPublication) {\n            from components.java\n            groupId = project.group\n            artifactId = arkGradlePluginId\n            version = project.version\n        }\n    }\n\n    repositories {\n        maven {\n            mavenLocal()\n        }\n    }\n}\n\ngradlePlugin {\n    plugins {\n        DependenciesPlugin{\n            id = arkGradlePluginId\n            implementationClass = 'com.alipay.sofa.ark.plugin.SofaArkGradlePlugin'\n        }\n    }\n}\n\n\nrepositories {\n    mavenLocal()\n    mavenCentral()\n}\n\ndependencies {\n    implementation 'org.ow2.asm:asm:9.4'\n    implementation 'org.apache.commons:commons-compress:1.26.1'\n    implementation 'com.alipay.sofa:sofa-ark-tools:2.2.12'\n}\n\ntest {\n    useJUnitPlatform()\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-gradle-plugin/settings.gradle",
    "content": "rootProject.name = 'ark-gradle-plugin'\n\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkArchiveSupport.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TreeMap;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarFile;\nimport org.gradle.api.GradleException;\nimport org.gradle.api.file.CopySpec;\nimport org.gradle.api.file.FileCopyDetails;\nimport org.gradle.api.file.FileTreeElement;\nimport org.gradle.api.file.RelativePath;\nimport org.gradle.api.internal.file.copy.CopyAction;\nimport org.gradle.api.internal.file.copy.CopyActionProcessingStream;\nimport org.gradle.api.internal.file.copy.FileCopyDetailsInternal;\nimport org.gradle.api.java.archives.Attributes;\nimport org.gradle.api.java.archives.Manifest;\nimport org.gradle.api.provider.Property;\nimport org.gradle.api.specs.Spec;\nimport org.gradle.api.specs.Specs;\nimport org.gradle.api.tasks.WorkResult;\nimport org.gradle.api.tasks.bundling.Jar;\nimport org.gradle.api.tasks.util.PatternSet;\nimport org.gradle.util.GradleVersion;\n\nimport com.alipay.sofa.ark.tools.git.GitInfo;\nimport com.alipay.sofa.ark.tools.git.JGitParser;\n\npublic class ArkArchiveSupport {\n\n    private static final byte[] ZIP_FILE_HEADER = new byte[] { 'P', 'K', 3, 4 };\n\n    private static final String BIZ_MARKER = \"com/alipay/sofa/ark/biz/mark\";\n    private static final String PLUGIN_MARKER = \"com/alipay/sofa/ark/plugin/mark\";\n    private static final String CONTAINER_MARK = \"com/alipay/sofa/ark/container/mark\";\n\n    private static final Set<String> DEFAULT_LAUNCHER_CLASSES;\n\n    static {\n        Set<String> defaultLauncherClasses = new HashSet<>();\n        defaultLauncherClasses.add(\"org.springframework.boot.loader.JarLauncher\");\n        defaultLauncherClasses.add(\"org.springframework.boot.loader.PropertiesLauncher\");\n        defaultLauncherClasses.add(\"org.springframework.boot.loader.WarLauncher\");\n        DEFAULT_LAUNCHER_CLASSES = Collections.unmodifiableSet(defaultLauncherClasses);\n    }\n\n    private final PatternSet requiresUnpack = new PatternSet();\n\n    private final PatternSet exclusions = new PatternSet();\n\n    private final String loaderMainClass;\n\n    private final Spec<FileCopyDetails> librarySpec;\n\n    private final Function<FileCopyDetails, ZipCompression> compressionResolver;\n\n    private final String arkVersion;\n\n    private SofaArkGradlePluginExtension arkExtension;\n\n    private final GitInfo gitInfo;\n\n    private java.util.jar.Manifest arkManifest = new java.util.jar.Manifest();\n\n    private final List<File> pluginFiles = new ArrayList<>();\n    private final List<File> bizFiles = new ArrayList<>();\n    private List<File> conFile = new ArrayList<>();\n\n    public ArkArchiveSupport(String loaderMainClass, Spec<FileCopyDetails> librarySpec,\n        Function<FileCopyDetails, ZipCompression> compressionResolver, File gitDic, SofaArkGradlePluginExtension arkExtension) {\n        this.loaderMainClass = loaderMainClass;\n        this.librarySpec = librarySpec;\n        this.compressionResolver = compressionResolver;\n        this.requiresUnpack.include(Specs.satisfyNone());\n        // TODO: configure as the version of sofa-ark\n        this.arkVersion = \"2.2.14\";\n        this.arkExtension = arkExtension;\n        this.gitInfo = JGitParser.parse(gitDic);\n        buildArkManifest();\n    }\n\n\n    public void configureBizManifest(Manifest manifest, String mainClass, String classes, String lib, String classPathIndex) {\n        Attributes attributes = manifest.getAttributes();\n        attributes.putIfAbsent(\"Start-Class\", mainClass);\n        attributes.putIfAbsent(\"Main-Class\", mainClass);\n        attributes.putIfAbsent(\"Spring-Boot-Classes\", classes);\n        buildModuleManifest(manifest);\n\n    }\n\n    public void buildArkManifest(){\n        this.arkManifest.getMainAttributes().putValue(\"Manifest-Version\", \"1.0\");\n        this.arkManifest.getMainAttributes().putValue(\"Main-Class\", this.loaderMainClass);\n        this.arkManifest.getMainAttributes().putValue(\"Start-Class\", this.loaderMainClass);\n        this.arkManifest.getMainAttributes().putValue(\"Sofa-Ark-Version\",this.arkVersion);\n        this.arkManifest.getMainAttributes().putValue(\"Ark-Container-Root\",\"SOFA-ARK/container/\");\n        this.arkManifest.getMainAttributes().putValue(\"build-time\",\n            new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ssZ\").format(new Date()));\n\n        if (gitInfo != null) {\n            this.arkManifest.getMainAttributes().putValue(\"remote-origin-url\", gitInfo.getRepository());\n            this.arkManifest.getMainAttributes().putValue(\"commit-branch\", gitInfo.getBranchName());\n            this.arkManifest.getMainAttributes().putValue(\"commit-id\", gitInfo.getLastCommitId());\n            this.arkManifest.getMainAttributes().putValue(\"commit-user-name\", gitInfo.getLastCommitUser());\n            this.arkManifest.getMainAttributes()\n                .putValue(\"commit-user-email\", gitInfo.getLastCommitEmail());\n            this.arkManifest.getMainAttributes().putValue(\"COMMIT_TIME\", gitInfo.getLastCommitDateTime());\n            this.arkManifest.getMainAttributes().putValue(\"COMMIT_TIMESTAMP\",\n                String.valueOf(gitInfo.getLastCommitTime()));\n            this.arkManifest.getMainAttributes().putValue(\"build-user\", gitInfo.getBuildUser());\n            this.arkManifest.getMainAttributes().putValue(\"build-email\", gitInfo.getBuildEmail());\n        }\n    }\n\n    private void buildModuleManifest(Manifest manifest){\n        Attributes attributes = manifest.getAttributes();\n        attributes.putIfAbsent(\"Ark-Biz-Name\",this.arkExtension.getBizName().get());\n        attributes.putIfAbsent(\"Ark-Biz-Version\",this.arkExtension.getBizVersion().get());\n        attributes.putIfAbsent(\"priority\",this.arkExtension.getPriority().get());\n        attributes.putIfAbsent(\"web-context-path\", this.arkExtension.getWebContextPath().get());\n        attributes.putIfAbsent(\"deny-import-packages\",joinSet(this.arkExtension.getDenyImportPackages().get()));\n        attributes.putIfAbsent(\"deny-import-classes\",joinSet(this.arkExtension.getDenyImportClasses().get()));\n        attributes.putIfAbsent(\"deny-import-resources\",joinSet(this.arkExtension.getDenyImportResources().get()));\n        attributes.putIfAbsent(\"inject-plugin-dependencies\", joinSet(this.arkExtension.getInjectPluginDependencies().get()));\n        attributes.putIfAbsent(\"inject-export-packages\",joinSet(this.arkExtension.getInjectPluginExportPackages().get()));\n        appendBuildInfo(manifest);\n    }\n\n    private String joinSet(Set<String> set) {\n        return set != null ? String.join(\",\", set) : \"\";\n    }\n\n\n    private void appendBuildInfo(Manifest manifest) {\n        Attributes attributes = manifest.getAttributes();\n        attributes.putIfAbsent(\"build-time\",  new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ssZ\").format(new Date()));\n\n        if (gitInfo != null) {\n            attributes.putIfAbsent(\"remote-origin-url\", gitInfo.getRepository());\n            attributes.putIfAbsent(\"commit-branch\", gitInfo.getBranchName());\n            attributes.putIfAbsent(\"commit-id\", gitInfo.getLastCommitId());\n            attributes.putIfAbsent(\"commit-user-name\", gitInfo.getLastCommitUser());\n            attributes.putIfAbsent(\"commit-user-email\", gitInfo.getLastCommitEmail());\n            attributes.putIfAbsent(\"COMMIT_TIME\", gitInfo.getLastCommitDateTime());\n            attributes.putIfAbsent(\"COMMIT_TIMESTAMP\", String.valueOf(gitInfo.getLastCommitTime()));\n            attributes.putIfAbsent(\"build-user\", gitInfo.getBuildUser());\n            attributes.putIfAbsent(\"build-email\", gitInfo.getBuildEmail());\n        }\n\n    }\n\n    private String determineSpringBootVersion() {\n        String version = getClass().getPackage().getImplementationVersion();\n        return (version != null) ? version : \"unknown\";\n    }\n\n    public CopyAction createCopyAction(Jar jar) throws IOException {\n        return createCopyAction(jar, null);\n    }\n\n    public CopyAction createCopyAction(Jar jar, String layerToolsLocation) throws IOException {\n        File bizOutput = getTargetFile(jar, this.arkExtension.getBizClassifier().get());\n        File arkOutput = getTargetFile(jar, this.arkExtension.getArkClassifier().get());\n\n        Manifest manifest = jar.getManifest();\n        boolean preserveFileTimestamps = jar.isPreserveFileTimestamps();\n        Integer dirMode = getDirMode(jar);\n        Integer fileMode = getFileMode(jar);\n        boolean includeDefaultLoader = isUsingDefaultLoader(jar);\n        Spec<FileTreeElement> requiresUnpack = this.requiresUnpack.getAsSpec();\n        Spec<FileTreeElement> exclusions = this.exclusions.getAsExcludeSpec();\n        Spec<FileCopyDetails> librarySpec = this.librarySpec;\n        Function<FileCopyDetails, ZipCompression> compressionResolver = this.compressionResolver;\n        String encoding = jar.getMetadataCharset();\n\n        CopyAction action = new ArkBizCopyAction(bizOutput,arkOutput, manifest, preserveFileTimestamps, dirMode, fileMode,\n            includeDefaultLoader,  requiresUnpack, exclusions, librarySpec,\n            compressionResolver, encoding, this.arkManifest, pluginFiles, bizFiles, conFile);\n\n\n        return jar.isReproducibleFileOrder() ? new ReproducibleOrderingCopyAction(action) : action;\n    }\n\n\n    private File getTargetFile(Jar jar, String classifier) {\n        File outputDir = this.arkExtension.getOutputDirectory()\n            .getOrElse(jar.getDestinationDirectory().get())\n            .getAsFile();\n\n        if (!outputDir.exists()) {\n            boolean created = outputDir.mkdirs();\n            if (!created) {\n                throw new GradleException(\"Failed to create output directory: \" + outputDir.getAbsolutePath());\n            }\n            System.out.println(\"Created output directory: \" + outputDir.getAbsolutePath());\n        }\n        File targetFile = new File(outputDir, getArkBizName(jar, classifier));\n        System.out.println(\"Target file will be created at: \" + targetFile.getAbsolutePath());\n\n        return targetFile;\n    }\n\n    private String getArkBizName(Jar jar, String classifier){\n        String name = \"\";\n        name += maybe(name, jar.getArchiveBaseName().getOrNull());\n        name += maybe(name,  jar.getArchiveAppendix().getOrNull());\n        name += maybe(name,  jar.getArchiveVersion().getOrNull());\n        name += maybe(name,  jar.getArchiveClassifier().getOrNull());\n        name += maybe(name,  classifier);\n        String extension = jar.getArchiveExtension().getOrNull();\n        name += (isTrue(extension) ? \".\" + extension : \"\");\n        return name;\n    }\n\n    private Boolean isTrue(Object object){\n        if (object instanceof String) {\n            return !((String) object).isEmpty();\n        }\n        return false;\n    }\n\n    private String maybe(String prefix, String value) {\n        if (isTrue(value)) {\n            return isTrue(prefix) ? \"-\".concat(value) : value;\n        } else {\n            return \"\";\n        }\n    }\n\n\n    private Integer getDirMode(CopySpec copySpec) {\n        return getMode(copySpec, \"getDirPermissions\", copySpec::getDirMode);\n    }\n\n    private Integer getFileMode(CopySpec copySpec) {\n        return getMode(copySpec, \"getFilePermissions\", copySpec::getFileMode);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private Integer getMode(CopySpec copySpec, String methodName, Supplier<Integer> fallback) {\n        if (GradleVersion.current().compareTo(GradleVersion.version(\"8.3\")) >= 0) {\n            try {\n                Object filePermissions = ((Property<Object>) copySpec.getClass().getMethod(methodName).invoke(copySpec))\n                    .getOrNull();\n                return (filePermissions != null)\n                    ? (int) filePermissions.getClass().getMethod(\"toUnixNumeric\").invoke(filePermissions) : null;\n            }\n            catch (Exception ex) {\n                throw new GradleException(\"Failed to get permissions\", ex);\n            }\n        }\n        return fallback.get();\n    }\n\n    private boolean isUsingDefaultLoader(Jar jar) {\n        return DEFAULT_LAUNCHER_CLASSES.contains(jar.getManifest().getAttributes().get(\"Main-Class\"));\n    }\n\n    void requiresUnpack(String... patterns) {\n        this.requiresUnpack.include(patterns);\n    }\n\n    void requiresUnpack(Spec<FileTreeElement> spec) {\n        this.requiresUnpack.include(spec);\n    }\n\n    void excludeNonZipLibraryFiles(FileCopyDetails details) {\n        if (this.librarySpec.isSatisfiedBy(details)) {\n            excludeNonZipFiles(details);\n        }\n    }\n\n    public void excludeNonZipFiles(FileCopyDetails details) {\n        if (!isZip(details.getFile())  || isSofaArk(details.getFile())) {\n            details.exclude();\n        }\n    }\n\n    private boolean isZip(File file) {\n        try {\n            try (FileInputStream fileInputStream = new FileInputStream(file)) {\n                return isZip(fileInputStream);\n            }\n        }\n        catch (IOException ex) {\n            return false;\n        }\n    }\n\n    private boolean isZip(InputStream inputStream) throws IOException {\n        for (byte headerByte : ZIP_FILE_HEADER) {\n            if (inputStream.read() != headerByte) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n\n    private boolean isSofaArk(File jarFile){\n        try (JarFile jar = new JarFile(jarFile)) {\n            for (JarEntry entry : Collections.list(jar.entries())) {\n                if (entry.getName().contains(BIZ_MARKER)) {\n                    bizFiles.add(jarFile);\n                    return true;\n                } else if (entry.getName().contains(PLUGIN_MARKER)) {\n                    pluginFiles.add(jarFile);\n                    return true;\n                } else if (entry.getName().contains(CONTAINER_MARK)){\n                    conFile.add(jarFile);\n                    return true;\n                }\n            }\n        } catch (IOException e) {\n\n        }\n        return false;\n    }\n\n\n    public void moveModuleInfoToRoot(CopySpec spec) {\n        spec.filesMatching(\"module-info.class\", this::moveToRoot);\n    }\n\n    public void moveToRoot(FileCopyDetails details) {\n        details.setRelativePath(details.getRelativeSourcePath());\n    }\n\n    /**\n     * {@link CopyAction} variant that sorts entries to ensure reproducible ordering.\n     */\n    private static final class ReproducibleOrderingCopyAction implements CopyAction {\n\n        private final CopyAction delegate;\n\n        private ReproducibleOrderingCopyAction(CopyAction delegate) {\n            this.delegate = delegate;\n        }\n\n        @Override\n        public WorkResult execute(CopyActionProcessingStream stream) {\n            return this.delegate.execute((action) -> {\n                Map<RelativePath, FileCopyDetailsInternal> detailsByPath = new TreeMap<>();\n                stream.process((details) -> detailsByPath.put(details.getRelativePath(), details));\n                detailsByPath.values().forEach(action::processFile);\n            });\n        }\n\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkBizCopyAction.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin;\n\nimport com.alipay.sofa.ark.common.util.FileUtils;\nimport java.io.BufferedInputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.time.OffsetDateTime;\nimport java.time.ZoneOffset;\nimport java.util.Enumeration;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarFile;\nimport java.util.jar.JarInputStream;\nimport java.util.zip.CRC32;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipInputStream;\nimport org.apache.commons.compress.archivers.zip.UnixStat;\nimport org.apache.commons.compress.archivers.zip.ZipArchiveEntry;\nimport org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;\nimport org.gradle.api.GradleException;\nimport org.gradle.api.file.FileCopyDetails;\nimport org.gradle.api.file.FileTreeElement;\nimport org.gradle.api.internal.file.copy.CopyAction;\nimport org.gradle.api.internal.file.copy.CopyActionProcessingStream;\nimport org.gradle.api.java.archives.Manifest;\nimport org.gradle.api.specs.Spec;\nimport org.gradle.api.tasks.WorkResult;\nimport org.gradle.api.tasks.WorkResults;\nimport org.gradle.util.GradleVersion;\n\npublic class ArkBizCopyAction implements CopyAction {\n\n    static final long CONSTANT_TIME_FOR_ZIP_ENTRIES = OffsetDateTime.of(1980, 2, 1, 0, 0, 0, 0, ZoneOffset.UTC)\n        .toInstant()\n        .toEpochMilli();\n\n    private final File bizOutput;\n\n    private final Manifest manifest;\n\n    private final boolean preserveFileTimestamps;\n\n    private final Integer dirMode;\n\n    private final Integer fileMode;\n\n    private final boolean includeDefaultLoader;\n\n    private final Spec<FileTreeElement> requiresUnpack;\n\n    private final Spec<FileTreeElement> exclusions;\n\n    private final Spec<FileCopyDetails> librarySpec;\n\n    private final Function<FileCopyDetails, ZipCompression> compressionResolver;\n\n    private final String encoding;\n\n    private final File arkOutput;\n\n    private String arkBootFile;\n\n    private final java.util.jar.Manifest arkManifest;\n\n    private List<File> pluginFiles;\n    private List<File> bizFiles;\n    private List<File> conFile;\n\n\n\n    ArkBizCopyAction(File bizOutput,File arkOutput, Manifest manifest, boolean preserveFileTimestamps, Integer dirMode, Integer fileMode,\n        boolean includeDefaultLoader,  Spec<FileTreeElement> requiresUnpack,\n        Spec<FileTreeElement> exclusions, Spec<FileCopyDetails> librarySpec,\n        Function<FileCopyDetails, ZipCompression> compressionResolver, String encoding, java.util.jar.Manifest arkManifest, List<File> pluginFiles, List<File> bizFiles, List<File> conFile\n    ) throws IOException {\n        this.bizOutput = bizOutput;\n        this.arkOutput = arkOutput;\n        this.manifest = manifest;\n        this.preserveFileTimestamps = preserveFileTimestamps;\n        this.dirMode = dirMode;\n        this.fileMode = fileMode;\n        this.includeDefaultLoader = includeDefaultLoader;\n        this.requiresUnpack = requiresUnpack;\n        this.exclusions = exclusions;\n        this.librarySpec = librarySpec;\n        this.compressionResolver = compressionResolver;\n        this.encoding = encoding;\n        this.arkManifest = arkManifest;\n        this.pluginFiles = pluginFiles;\n        this.bizFiles = bizFiles;\n        this.conFile = conFile;\n    }\n\n    @Override\n    public WorkResult execute(CopyActionProcessingStream copyActions) {\n        try {\n            writeBizArchive(copyActions);\n            writeArkArchive();\n            return WorkResults.didWork(true);\n        }\n        catch (IOException ex) {\n            throw new GradleException(\"Failed to create \" + this.bizOutput, ex);\n        }\n    }\n\n    private void writeBizArchive(CopyActionProcessingStream copyActions) throws IOException {\n        OutputStream output = new FileOutputStream(this.bizOutput);\n        try {\n            writeArchive(copyActions, output);\n        }\n        finally {\n            closeQuietly(output);\n        }\n    }\n\n    private void writeArkArchive() throws IOException {\n        OutputStream output = new FileOutputStream(this.arkOutput);\n        try {\n            writeArkArchive(output);\n        }\n        finally {\n            closeQuietly(output);\n        }\n    }\n\n    private void writeArkArchive(OutputStream output) throws IOException {\n        ZipArchiveOutputStream zipOutput = new ZipArchiveOutputStream(output);\n        try {\n            setEncodingIfNecessary(zipOutput);\n            Processor1 processor = new Processor1(zipOutput);\n            processor.process();\n        }\n        finally {\n            closeQuietly(zipOutput);\n        }\n    }\n\n    private void writeArchive(CopyActionProcessingStream copyActions, OutputStream output) throws IOException {\n        ZipArchiveOutputStream zipOutput = new ZipArchiveOutputStream(output);\n        try {\n            setEncodingIfNecessary(zipOutput);\n            Processor processor = new Processor(zipOutput);\n            copyActions.process(processor::process);\n            processor.finish();\n        }\n        finally {\n            closeQuietly(zipOutput);\n        }\n    }\n\n    private void closeQuietly(OutputStream outputStream) {\n        try {\n            outputStream.close();\n        }\n        catch (IOException ex) {\n        }\n    }\n\n    private void setEncodingIfNecessary(ZipArchiveOutputStream zipOutputStream) {\n        if (this.encoding != null) {\n            zipOutputStream.setEncoding(this.encoding);\n        }\n    }\n\nprivate class Processor1{\n    private final ZipArchiveOutputStream out;\n\n    private LoaderZipEntries.WrittenEntries writtenLoaderEntries;\n\n    private final Set<String> writtenDirectories = new LinkedHashSet<>();\n\n    private final Set<String> writtenLibraries = new LinkedHashSet<>();\n\n    private String arkFile;\n\n\n    Processor1(ZipArchiveOutputStream out) throws IOException {\n        this.out = out;\n        this.arkFile = conFile.get(0).getAbsolutePath();\n    }\n\n    void process() throws IOException {\n        writePluginJar();\n        writeBootstrapEntry();\n        writeArkManifest();\n        writeContainer();\n        writeBizJar();\n    }\n\n    void writePluginJar() throws IOException {\n        writeFiles(pluginFiles, \"SOFA-ARK/plugin/\");\n    }\n\n    void writeArkManifest() throws IOException {\n        ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(\"META-INF/MANIFEST.MF\");\n        this.out.putArchiveEntry(zipArchiveEntry);\n        ArkBizCopyAction.this.arkManifest.write(this.out);\n        this.out.closeArchiveEntry();\n    }\n\n    private void writeBootstrapEntry() throws IOException {\n        try (JarFile jarFileSource = new JarFile(this.arkFile)){\n            Enumeration<JarEntry> entries = jarFileSource.entries();\n            while (entries.hasMoreElements()) {\n                JarEntry entry = entries.nextElement();\n                if (entry.getName().contains(\"sofa-ark-archive\")\n                    || entry.getName().contains(\"sofa-ark-spi\")\n                    || entry.getName().contains(\"sofa-ark-common\")) {\n\n                    JarInputStream inputStream = new JarInputStream(new BufferedInputStream(\n                        jarFileSource.getInputStream(entry)));\n                    writeLoaderClasses(inputStream, jarFileSource);\n                }\n            }\n        } catch (NullPointerException exception){\n            throw new RuntimeException(\"No sofa-ark-all file find, please configure it\");\n        }\n\n    }\n\n    void writeContainer() throws IOException {\n        File file = new File(arkFile);\n        try(  FileInputStream fileInputStream = new FileInputStream(file)) {\n            ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(\"SOFA-ARK/container/\"+ file.getName());\n            writeEntry(fileInputStream, zipArchiveEntry);\n        }\n    }\n\n    void writeBizJar() throws IOException {\n        bizFiles.add(bizOutput);\n        writeFiles(bizFiles, \"SOFA-ARK/biz/\");\n    }\n\n    void writeFiles(List<File> files, String path){\n        for(File file : files){\n            try(  FileInputStream fileInputStream = new FileInputStream(file)) {\n                ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(path + file.getName());\n                writeEntry(fileInputStream, zipArchiveEntry);\n            } catch (IOException e) {\n                throw new RuntimeException(e);\n            }\n        }\n    }\n\n    private void writeEntry(FileInputStream fileInputStream, ZipArchiveEntry zipArchiveEntry) throws IOException {\n        this.out.putArchiveEntry(zipArchiveEntry);\n        byte[] buffer = new byte[1024];\n        int len;\n        while ((len = fileInputStream.read(buffer)) > 0) {\n            this.out.write(buffer, 0, len);\n        }\n        this.out.closeArchiveEntry();\n    }\n\n\n\n    private void writeDirectory(ZipArchiveEntry entry, ZipArchiveOutputStream out) throws IOException {\n        out.putArchiveEntry(entry);\n        out.closeArchiveEntry();\n    }\n\n    private void writeClass(ZipArchiveEntry entry, ZipInputStream in, ZipArchiveOutputStream out) throws IOException {\n        out.putArchiveEntry(entry);\n        StringUtils.copyTo(in, out);\n        out.closeArchiveEntry();\n    }\n\n    private int getDirMode() {\n        return (ArkBizCopyAction.this.dirMode != null) ? ArkBizCopyAction.this.dirMode\n            : UnixStat.DIR_FLAG | UnixStat.DEFAULT_DIR_PERM;\n    }\n\n\n    private void writeLoaderClasses(JarInputStream jarInputStream, JarFile jarFileSource) throws IOException {\n        JarEntry entry;\n        while ((entry = jarInputStream.getNextJarEntry()) != null) {\n            if (entry.getName().endsWith(\".class\")\n                && (entry.getName().contains(\"com/alipay/sofa/ark/spi/archive\")\n                || entry.getName().contains(\"com/alipay/sofa/ark/loader\")\n                || entry.getName().contains(\"com/alipay/sofa/ark/bootstrap\")\n                || entry.getName().contains(\"com/alipay/sofa/ark/common/util/StringUtils\")\n                || entry.getName().contains(\"com/alipay/sofa/ark/common/util/AssertUtils\") || entry\n                .getName().contains(\"com/alipay/sofa/ark/spi/constant\"))) {\n\n                ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(entry.getName());\n                this.out.putArchiveEntry(zipArchiveEntry);\n\n                byte[] bytes = new byte[1024];\n                int length;\n                while ((length = jarInputStream.read(bytes)) >= 0) {\n                    this.out.write(bytes, 0, length);\n                }\n                this.out.closeArchiveEntry();\n\n\n            }\n        }\n        jarInputStream.close();\n    }\n\n\n}\n\n    private class Processor {\n\n        private final ZipArchiveOutputStream out;\n\n        private LoaderZipEntries.WrittenEntries writtenLoaderEntries;\n\n        private final Set<String> writtenDirectories = new LinkedHashSet<>();\n\n        private final Set<String> writtenLibraries = new LinkedHashSet<>();\n\n        Processor(ZipArchiveOutputStream out) throws IOException {\n            this.out = out;\n        }\n\n        void process(FileCopyDetails details) {\n            if (skipProcessing(details)) {\n                return;\n            }\n            try {\n                if (details.isDirectory()) {\n                    processDirectory(details);\n                } else {\n                    processFile(details);\n                }\n            } catch (IOException ex) {\n                throw new GradleException(\"Failed to add \" + details + \" to \" + ArkBizCopyAction.this.bizOutput, ex);\n            }\n        }\n\n\n        private boolean skipProcessing(FileCopyDetails details) {\n            return ArkBizCopyAction.this.exclusions.isSatisfiedBy(details)\n                || (this.writtenLoaderEntries != null && this.writtenLoaderEntries.isWrittenDirectory(details));\n        }\n\n        private void processDirectory(FileCopyDetails details) throws IOException {\n            String name = details.getRelativePath().getPathString();\n            ZipArchiveEntry entry = new ZipArchiveEntry(name + '/');\n            prepareEntry(entry, name, getTime(details), getFileMode(details));\n            this.out.putArchiveEntry(entry);\n            this.out.closeArchiveEntry();\n            this.writtenDirectories.add(name);\n        }\n\n        private void processFile(FileCopyDetails details) throws IOException {\n            String name = details.getRelativePath().getPathString();\n            ZipArchiveEntry entry = new ZipArchiveEntry(name);\n            prepareEntry(entry, name, getTime(details), getFileMode(details));\n            ZipCompression compression = ArkBizCopyAction.this.compressionResolver.apply(details);\n            if (compression == ZipCompression.STORED) {\n                prepareStoredEntry(details, entry);\n            }\n            this.out.putArchiveEntry(entry);\n            details.copyTo(this.out);\n            this.out.closeArchiveEntry();\n            if (ArkBizCopyAction.this.librarySpec.isSatisfiedBy(details)) {\n                this.writtenLibraries.add(name);\n            }\n\n        }\n\n        private String getParentDirectory(String name) {\n            int lastSlash = name.lastIndexOf('/');\n            if (lastSlash == -1) {\n                return null;\n            }\n            return name.substring(0, lastSlash);\n        }\n\n        private void prepareEntry(ZipArchiveEntry entry, String name, Long time, int mode) throws IOException {\n            writeParentDirectoriesIfNecessary(name, time);\n            entry.setUnixMode(mode);\n            if (time != null) {\n                entry.setTime(DefaultTimeZoneOffset.INSTANCE.removeFrom(time));\n            }\n        }\n\n        private void writeParentDirectoriesIfNecessary(String name, Long time) throws IOException {\n            String parentDirectory = getParentDirectory(name);\n            if (parentDirectory != null && this.writtenDirectories.add(parentDirectory)) {\n                ZipArchiveEntry entry = new ZipArchiveEntry(parentDirectory + '/');\n                prepareEntry(entry, parentDirectory, time, getDirMode());\n                this.out.putArchiveEntry(entry);\n                this.out.closeArchiveEntry();\n            }\n        }\n\n\n\n        void finish() throws IOException {\n            writeArkBizMark();\n        }\n\n        private void writeArkBizMark() throws IOException {\n            String info = \"a mark file included in sofa-ark module.\";\n            String name = \"com/alipay/sofa/ark/biz/mark\";\n            ZipArchiveEntry entry = new ZipArchiveEntry(name);\n            prepareEntry(entry, name, getTime(), getFileMode());\n            this.out.putArchiveEntry(entry);\n            byte[] data = info.getBytes(StandardCharsets.UTF_8);\n            this.out.write(data, 0, data.length);\n            this.out.closeArchiveEntry();\n        }\n\n        private void prepareStoredEntry(FileCopyDetails details, ZipArchiveEntry archiveEntry) throws IOException {\n            prepareStoredEntry(details.open(), archiveEntry);\n            if (ArkBizCopyAction.this.requiresUnpack.isSatisfiedBy(details)) {\n                archiveEntry.setComment(\"UNPACK:\" + FileUtils.sha1Hash(details.getFile()));\n            }\n        }\n\n        private void prepareStoredEntry(InputStream input, ZipArchiveEntry archiveEntry) throws IOException {\n            new CrcAndSize(input).setUpStoredEntry(archiveEntry);\n        }\n\n        private Long getTime() {\n            return getTime(null);\n        }\n\n        private Long getTime(FileCopyDetails details) {\n            if (!ArkBizCopyAction.this.preserveFileTimestamps) {\n                return CONSTANT_TIME_FOR_ZIP_ENTRIES;\n            }\n            if (details != null) {\n                return details.getLastModified();\n            }\n            return null;\n        }\n\n        private int getDirMode() {\n            return (ArkBizCopyAction.this.dirMode != null) ? ArkBizCopyAction.this.dirMode\n                : UnixStat.DIR_FLAG | UnixStat.DEFAULT_DIR_PERM;\n        }\n\n        private int getFileMode() {\n            return (ArkBizCopyAction.this.fileMode != null) ? ArkBizCopyAction.this.fileMode\n                : UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM;\n        }\n\n        private int getFileMode(FileCopyDetails details) {\n            return (ArkBizCopyAction.this.fileMode != null) ? ArkBizCopyAction.this.fileMode\n                : UnixStat.FILE_FLAG | getPermissions(details);\n        }\n\n        private int getPermissions(FileCopyDetails details) {\n            if (GradleVersion.current().compareTo(GradleVersion.version(\"8.3\")) >= 0) {\n                try {\n                    Object permissions = details.getClass().getMethod(\"getPermissions\").invoke(details);\n                    return (int) permissions.getClass().getMethod(\"toUnixNumeric\").invoke(permissions);\n                }\n                catch (Exception ex) {\n                    throw new GradleException(\"Failed to get permissions\", ex);\n                }\n            }\n            return details.getMode();\n        }\n\n\n    }\n\n\n    /**\n     * Data holder for CRC and Size.\n     */\n    private static class CrcAndSize {\n\n        private static final int BUFFER_SIZE = 32 * 1024;\n\n        private final CRC32 crc = new CRC32();\n\n        private long size;\n\n        CrcAndSize(InputStream inputStream) throws IOException {\n            try {\n                load(inputStream);\n            }\n            finally {\n                inputStream.close();\n            }\n        }\n\n        private void load(InputStream inputStream) throws IOException {\n            byte[] buffer = new byte[BUFFER_SIZE];\n            int bytesRead;\n            while ((bytesRead = inputStream.read(buffer)) != -1) {\n                this.crc.update(buffer, 0, bytesRead);\n                this.size += bytesRead;\n            }\n        }\n\n        void setUpStoredEntry(ZipArchiveEntry entry) {\n            entry.setSize(this.size);\n            entry.setCompressedSize(this.size);\n            entry.setCrc(this.crc.getValue());\n            entry.setMethod(ZipEntry.STORED);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkJar.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.concurrent.Callable;\nimport java.util.function.Function;\nimport org.gradle.api.Action;\nimport org.gradle.api.Project;\nimport org.gradle.api.file.CopySpec;\nimport org.gradle.api.file.FileCollection;\nimport org.gradle.api.file.FileCopyDetails;\nimport org.gradle.api.file.FileTreeElement;\nimport org.gradle.api.internal.file.copy.CopyAction;\nimport org.gradle.api.provider.Property;\nimport org.gradle.api.specs.Spec;\nimport org.gradle.api.tasks.Internal;\nimport org.gradle.api.tasks.bundling.Jar;\n\npublic class ArkJar extends Jar implements BootArchive {\n\n    private static final String LAUNCHER = \"com.alipay.sofa.ark.bootstrap.ArkLauncher\";\n\n    private static final String CLASSES_DIRECTORY = \"classes/\";\n\n    private static final String LIB_DIRECTORY = \"lib/\";\n\n    private static final String ARK_BIZ_MARK  = \"com/alipay/sofa/ark/biz/mark\";\n\n    private static final String CLASSPATH_INDEX = \"classpath.idx\";\n\n    private final ArkArchiveSupport support;\n\n    private final CopySpec bootInfSpec;\n\n    private final Property<String> mainClass;\n\n    private FileCollection classpath;\n\n    private File gitInfo;\n\n    private SofaArkGradlePluginExtension arkExtension;\n\n\n    /**\n     * Creates a new {@code BootJar} task.\n     */\n    public ArkJar() {\n        Project project = getProject();\n        this.gitInfo = getGitDirectory(project);\n\n        this.arkExtension = project.getExtensions().findByType(SofaArkGradlePluginExtension.class);\n        this.support = new ArkArchiveSupport(LAUNCHER, new LibrarySpec(), new ZipCompressionResolver(), gitInfo, arkExtension);\n\n        this.bootInfSpec = project.copySpec().into(\"\");\n        this.mainClass = project.getObjects().property(String.class);\n        configureBootInfSpec(this.bootInfSpec);\n        getMainSpec().with(this.bootInfSpec);\n\n    }\n\n    private File getGitDirectory(Project project) {\n        File projectDir = project.getRootDir();\n        File gitFolder = new File(projectDir, \".git\");\n        if (gitFolder.exists() && gitFolder.isDirectory()) {\n            return gitFolder;\n        }\n        return new File(\" \");\n    }\n\n\n    private void configureBootInfSpec(CopySpec bootInfSpec) {\n        bootInfSpec.into(\"\", fromCallTo(this::classpathDirectories));\n        bootInfSpec.into(\"lib\", fromCallTo(this::classpathFiles)).eachFile(this.support::excludeNonZipFiles);\n\n        this.support.moveModuleInfoToRoot(bootInfSpec);\n        moveMetaInfToRoot(bootInfSpec);\n    }\n\n    private Iterable<File> classpathDirectories() {\n        return classpathEntries(File::isDirectory);\n    }\n\n    private Iterable<File> classpathFiles() {\n        return classpathEntries(File::isFile);\n    }\n\n    private Iterable<File> classpathEntries(Spec<File> filter) {\n        return (this.classpath != null) ? this.classpath.filter(filter) : Collections.emptyList();\n    }\n\n    private void moveMetaInfToRoot(CopySpec spec) {\n        spec.eachFile((file) -> {\n            String path = file.getRelativeSourcePath().getPathString();\n            if (path.startsWith(\"META-INF/\") && !path.equals(\"META-INF/aop.xml\") && !path.endsWith(\".kotlin_module\")\n                && !path.startsWith(\"META-INF/services/\")) {\n                this.support.moveToRoot(file);\n            }\n        });\n    }\n\n    @Override\n    public void copy() {\n        this.support.configureBizManifest(getManifest(), getMainClass().get(), CLASSES_DIRECTORY, LIB_DIRECTORY,\n            CLASSPATH_INDEX);\n        super.copy();\n    }\n\n\n    @Override\n    protected CopyAction createCopyAction() {\n        try {\n            return this.support.createCopyAction(this);\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Override\n    public Property<String> getMainClass() {\n        return this.mainClass;\n    }\n\n    @Override\n    public void requiresUnpack(String... patterns) {\n        this.support.requiresUnpack(patterns);\n    }\n\n    @Override\n    public void requiresUnpack(Spec<FileTreeElement> spec) {\n        this.support.requiresUnpack(spec);\n    }\n\n    @Override\n    public FileCollection getClasspath() {\n        return this.classpath;\n    }\n\n    @Override\n    public void classpath(Object... classpath) {\n        FileCollection existingClasspath = this.classpath;\n        this.classpath = getProject().files((existingClasspath != null) ? existingClasspath : Collections.emptyList(),\n            classpath);\n    }\n\n    @Override\n    public void setClasspath(Object classpath) {\n        this.classpath = getProject().files(classpath);\n    }\n\n    @Override\n    public void setClasspath(FileCollection classpath) {\n        this.classpath = getProject().files(classpath);\n    }\n\n    /**\n     * Returns a {@code CopySpec} that can be used to add content to the {@code BOOT-INF}\n     * directory of the jar.\n     * @return a {@code CopySpec} for {@code BOOT-INF}\n     * @since 2.0.3\n     */\n    @Internal\n    public CopySpec getBootInf() {\n        CopySpec child = getProject().copySpec();\n        this.bootInfSpec.with(child);\n        return child;\n    }\n\n\n    /**\n     * Return the {@link ZipCompression} that should be used when adding the file\n     * represented by the given {@code details} to the jar. By default, any\n     * {@link #isLibrary(FileCopyDetails) library} is {@link ZipCompression#STORED stored}\n     * and all other files are {@link ZipCompression#DEFLATED deflated}.\n     * @param details the file copy details\n     * @return the compression to use\n     */\n    protected ZipCompression resolveZipCompression(FileCopyDetails details) {\n        return isLibrary(details) ? ZipCompression.STORED : ZipCompression.DEFLATED;\n    }\n\n    /**\n     * Return if the {@link FileCopyDetails} are for a library. By default any file in\n     * {@code BOOT-INF/lib} is considered to be a library.\n     * @param details the file copy details\n     * @return {@code true} if the details are for a library\n     * @since 2.3.0\n     */\n    protected boolean isLibrary(FileCopyDetails details) {\n        String path = details.getRelativePath().getPathString();\n        return path.startsWith(LIB_DIRECTORY);\n    }\n\n\n    /**\n     * Syntactic sugar that makes {@link CopySpec#into} calls a little easier to read.\n     * @param <T> the result type\n     * @param callable the callable\n     * @return an action to add the callable to the spec\n     */\n    private static <T> Action<CopySpec> fromCallTo(Callable<T> callable) {\n        return (spec) -> spec.from(callTo(callable));\n    }\n\n    /**\n     * Syntactic sugar that makes {@link CopySpec#from} calls a little easier to read.\n     * @param <T> the result type\n     * @param callable the callable\n     * @return the callable\n     */\n    private static <T> Callable<T> callTo(Callable<T> callable) {\n        return callable;\n    }\n\n    private final class LibrarySpec implements Spec<FileCopyDetails> {\n        @Override\n        public boolean isSatisfiedBy(FileCopyDetails details) {\n            return isLibrary(details);\n        }\n\n    }\n\n    private final class ZipCompressionResolver implements Function<FileCopyDetails, ZipCompression> {\n\n        @Override\n        public ZipCompression apply(FileCopyDetails details) {\n            return resolveZipCompression(details);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ArkPluginAction.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin;\n\nimport java.io.File;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.Callable;\nimport org.gradle.api.Action;\nimport org.gradle.api.Project;\nimport org.gradle.api.Task;\nimport org.gradle.api.artifacts.Configuration;\nimport org.gradle.api.artifacts.Dependency;\nimport org.gradle.api.artifacts.ModuleDependency;\nimport org.gradle.api.attributes.AttributeContainer;\nimport org.gradle.api.attributes.Bundling;\nimport org.gradle.api.attributes.LibraryElements;\nimport org.gradle.api.attributes.Usage;\nimport org.gradle.api.file.FileCollection;\nimport org.gradle.api.model.ObjectFactory;\nimport org.gradle.api.plugins.BasePlugin;\nimport org.gradle.api.plugins.JavaPlugin;\nimport org.gradle.api.plugins.JavaPluginExtension;\nimport org.gradle.api.provider.Provider;\nimport org.gradle.api.tasks.SourceSet;\nimport org.gradle.api.tasks.SourceSetContainer;\nimport org.gradle.api.tasks.TaskProvider;\nimport org.gradle.api.tasks.bundling.Jar;\nimport org.gradle.api.tasks.compile.JavaCompile;\nimport org.gradle.util.GradleVersion;\n\npublic class ArkPluginAction implements Action<Project> {\n\n    private static final String PARAMETERS_COMPILER_ARG  = \"-parameters\";\n\n    @Override\n    public void execute(Project project) {\n        classifyJarTask(project);\n        configureDevelopmentOnlyConfiguration(project);\n\n        project.afterEvaluate(this::configureArkAllArtifact);\n        project.afterEvaluate(this::configureBootJarTask);\n\n        configureBuildTask(project);\n\n        project.afterEvaluate(this::configureUtf8Encoding);\n        configureParametersCompilerArg(project);\n        configureAdditionalMetadataLocations(project);\n    }\n\n    private void classifyJarTask(Project project) {\n        project.getTasks()\n            .named(JavaPlugin.JAR_TASK_NAME, Jar.class)\n            .configure((task) -> task.getArchiveClassifier().convention(\"plain\"));\n    }\n\n\n    private void configureArkAllArtifact(Project project) {\n        SofaArkGradlePluginExtension arkConfig =  project.getExtensions().getByType(SofaArkGradlePluginExtension.class);\n\n        Configuration runtimeClasspath = project.getConfigurations().getByName(\"runtimeClasspath\");\n        Configuration sofaArkConfig = project.getConfigurations().maybeCreate(\"sofaArkConfig\");\n        // TODO: configure as the version of sofa-ark\n        Dependency arkDependency = project.getDependencies().create(SofaArkGradlePlugin.ARK_BOOTSTRAP+\"2.2.14\");\n        ((ModuleDependency) arkDependency).setTransitive(false);\n        sofaArkConfig.getDependencies().add(arkDependency);\n        runtimeClasspath.extendsFrom(sofaArkConfig);\n    }\n\n    private void configureBuildTask(Project project) {\n        project.getTasks()\n            .named(BasePlugin.ASSEMBLE_TASK_NAME)\n            .configure((task) -> task.dependsOn(project.getTasks().getByName(\"arkJar\")));\n    }\n\n    private void configureBootJarTask(Project project) {\n\n        SofaArkGradlePluginExtension arkExtension = project.getExtensions().findByType(SofaArkGradlePluginExtension.class);\n        Configuration configuration = project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);\n\n        applyExclusions(configuration, arkExtension);\n\n        SourceSet mainSourceSet = sourceSets(project).getByName(SourceSet.MAIN_SOURCE_SET_NAME);\n        Configuration developmentOnly = project.getConfigurations()\n            .getByName(SofaArkGradlePlugin.DEVELOPMENT_ONLY_CONFIGURATION_NAME);\n        Configuration productionRuntimeClasspath = project.getConfigurations()\n            .getByName(SofaArkGradlePlugin.PRODUCTION_RUNTIME_CLASSPATH_CONFIGURATION_NAME);\n        Callable<FileCollection> classpath = () -> mainSourceSet.getRuntimeClasspath()\n            .minus((developmentOnly.minus(productionRuntimeClasspath)))\n            .filter(new JarTypeFileSpec());\n\n\n        TaskProvider<ResolveMainClassName> resolveMainClassName = ResolveMainClassName\n            .registerForTask(SofaArkGradlePlugin.ARK_BIZ_TASK_NAME, project, classpath);\n\n\n        project.getTasks().register(SofaArkGradlePlugin.ARK_BIZ_TASK_NAME, ArkJar.class, (arkJar) -> {\n            arkJar.setDescription(\n                \"Assembles an executable jar archive containing the main classes and their dependencies.\");\n            arkJar.setGroup(BasePlugin.BUILD_GROUP);\n            arkJar.classpath(classpath);\n            Provider<String> manifestStartClass = project\n                .provider(() -> (String) arkJar.getManifest().getAttributes().get(\"Start-Class\"));\n            arkJar.getMainClass()\n                .convention(resolveMainClassName.flatMap((resolver) -> manifestStartClass.isPresent()\n                    ? manifestStartClass : resolveMainClassName.get().readMainClassName()));\n        });\n\n    }\n\n    private void applyExclusions(Configuration configuration, SofaArkGradlePluginExtension arkConfig) {\n        for (String exclude : arkConfig.getExcludes().get()) {\n            String[] parts = exclude.split(\":\");\n            // TODO: compatible with group:module:version\n            if (parts.length == 2) {\n                Map<String, String> excludeMap = new HashMap<>();\n                excludeMap.put(\"group\", parts[0]);\n                excludeMap.put(\"module\", parts[1]);\n                configuration.exclude(excludeMap);\n            }\n        }\n\n        arkConfig.getExcludeGroupIds().get().stream()\n            .map(groupId -> Collections.singletonMap(\"group\", groupId))\n            .forEach(configuration::exclude);\n\n        arkConfig.getExcludeArtifactIds().get().stream()\n            .map(artifactId -> Collections.singletonMap(\"module\", artifactId))\n            .forEach(configuration::exclude);\n    }\n\n\n    private SourceSetContainer sourceSets(Project project) {\n        if (GradleVersion.current().compareTo(GradleVersion.version(\"7.1\")) < 0) {\n            return project.getConvention().getPlugin(org.gradle.api.plugins.JavaPluginConvention.class).getSourceSets();\n        }\n        return project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets();\n    }\n\n    private void configureUtf8Encoding(Project evaluatedProject) {\n        evaluatedProject.getTasks().withType(JavaCompile.class).configureEach(this::configureUtf8Encoding);\n    }\n\n    private void configureUtf8Encoding(JavaCompile compile) {\n        if (compile.getOptions().getEncoding() == null) {\n            compile.getOptions().setEncoding(\"UTF-8\");\n        }\n    }\n\n    private void configureParametersCompilerArg(Project project) {\n        project.getTasks().withType(JavaCompile.class).configureEach((compile) -> {\n            List<String> compilerArgs = compile.getOptions().getCompilerArgs();\n            if (!compilerArgs.contains(PARAMETERS_COMPILER_ARG)) {\n                compilerArgs.add(PARAMETERS_COMPILER_ARG);\n            }\n        });\n    }\n\n    private void configureAdditionalMetadataLocations(Project project) {\n        project.afterEvaluate((evaluated) -> evaluated.getTasks()\n            .withType(JavaCompile.class)\n            .configureEach(this::configureAdditionalMetadataLocations));\n    }\n\n    private void configureAdditionalMetadataLocations(JavaCompile compile) {\n        sourceSets(compile.getProject()).stream()\n            .filter((candidate) -> candidate.getCompileJavaTaskName().equals(compile.getName()))\n            .map((match) -> match.getResources().getSrcDirs())\n            .findFirst()\n            .ifPresent((locations) -> compile.doFirst(new AdditionalMetadataLocationsConfigurer(locations)));\n    }\n\n    private void configureDevelopmentOnlyConfiguration(Project project) {\n        Configuration developmentOnly = project.getConfigurations()\n            .create(SofaArkGradlePlugin.DEVELOPMENT_ONLY_CONFIGURATION_NAME);\n        developmentOnly\n            .setDescription(\"Configuration for development-only dependencies such as Spring Boot's DevTools.\");\n        Configuration runtimeClasspath = project.getConfigurations()\n            .getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);\n        Configuration productionRuntimeClasspath = project.getConfigurations()\n            .create(SofaArkGradlePlugin.PRODUCTION_RUNTIME_CLASSPATH_CONFIGURATION_NAME);\n        AttributeContainer attributes = productionRuntimeClasspath.getAttributes();\n        ObjectFactory objectFactory = project.getObjects();\n        attributes.attribute(Usage.USAGE_ATTRIBUTE, objectFactory.named(Usage.class, Usage.JAVA_RUNTIME));\n        attributes.attribute(Bundling.BUNDLING_ATTRIBUTE, objectFactory.named(Bundling.class, Bundling.EXTERNAL));\n        attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,\n            objectFactory.named(LibraryElements.class, LibraryElements.JAR));\n        productionRuntimeClasspath.setVisible(false);\n        productionRuntimeClasspath.setExtendsFrom(runtimeClasspath.getExtendsFrom());\n        productionRuntimeClasspath.setCanBeResolved(runtimeClasspath.isCanBeResolved());\n        productionRuntimeClasspath.setCanBeConsumed(runtimeClasspath.isCanBeConsumed());\n        runtimeClasspath.extendsFrom(developmentOnly);\n    }\n\n\n    private static final class AdditionalMetadataLocationsConfigurer implements Action<Task> {\n\n        private final Set<File> locations;\n\n        private AdditionalMetadataLocationsConfigurer(Set<File> locations) {\n            this.locations = locations;\n        }\n\n        @Override\n        public void execute(Task task) {\n            if (!(task instanceof JavaCompile)) {\n                return;\n            }\n            JavaCompile compile = (JavaCompile) task;\n            if (hasConfigurationProcessorOnClasspath(compile)) {\n                configureAdditionalMetadataLocations(compile);\n            }\n        }\n\n        private boolean hasConfigurationProcessorOnClasspath(JavaCompile compile) {\n            Set<File> files = (compile.getOptions().getAnnotationProcessorPath() != null)\n                ? compile.getOptions().getAnnotationProcessorPath().getFiles() : compile.getClasspath().getFiles();\n            return files.stream()\n                .map(File::getName)\n                .anyMatch((name) -> name.startsWith(\"spring-boot-configuration-processor\"));\n        }\n\n        private void configureAdditionalMetadataLocations(JavaCompile compile) {\n            compile.getOptions()\n                .getCompilerArgs()\n                .add(\"-Aorg.springframework.boot.configurationprocessor.additionalMetadataLocations=\"\n                    + StringUtils.collectionToCommaDelimitedString(this.locations));\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/BootArchive.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin;\n\nimport org.gradle.api.Project;\nimport org.gradle.api.Task;\nimport org.gradle.api.file.FileCollection;\nimport org.gradle.api.file.FileTreeElement;\nimport org.gradle.api.provider.Property;\nimport org.gradle.api.specs.Spec;\nimport org.gradle.api.tasks.Classpath;\nimport org.gradle.api.tasks.Input;\nimport org.gradle.api.tasks.Optional;\n\npublic interface BootArchive extends Task {\n\n\t/**\n\t * Returns the fully-qualified name of the application's main class.\n\t * @return the fully-qualified name of the application's main class\n\t * @since 2.4.0\n\t */\n\t@Input\n\tProperty<String> getMainClass();\n\n\t/**\n\t * Adds Ant-style patterns that identify files that must be unpacked from the archive\n\t * when it is launched.\n\t * @param patterns the patterns\n\t */\n\tvoid requiresUnpack(String... patterns);\n\n\t/**\n\t * Adds a spec that identifies files that must be unpacked from the archive when it is\n\t * launched.\n\t * @param spec the spec\n\t */\n\tvoid requiresUnpack(Spec<FileTreeElement> spec);\n\n\n\t/**\n\t * Returns the classpath that will be included in the archive.\n\t * @return the classpath\n\t */\n\t@Optional\n\t@Classpath\n\tFileCollection getClasspath();\n\n\t/**\n\t * Adds files to the classpath to include in the archive. The given {@code classpath}\n\t * is evaluated as per {@link Project#files(Object...)}.\n\t * @param classpath the additions to the classpath\n\t */\n\tvoid classpath(Object... classpath);\n\n\t/**\n\t * Sets the classpath to include in the archive. The given {@code classpath} is\n\t * evaluated as per {@link Project#files(Object...)}.\n\t * @param classpath the classpath\n\t * @since 2.0.7\n\t */\n\tvoid setClasspath(Object classpath);\n\n\t/**\n\t * Sets the classpath to include in the archive.\n\t * @param classpath the classpath\n\t * @since 2.0.7\n\t */\n\tvoid setClasspath(FileCollection classpath);\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/DefaultTimeZoneOffset.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin;\n\nimport java.nio.file.attribute.FileTime;\nimport java.util.TimeZone;\n\nclass DefaultTimeZoneOffset {\n\n\tstatic final DefaultTimeZoneOffset INSTANCE = new DefaultTimeZoneOffset(TimeZone.getDefault());\n\n\tprivate final TimeZone defaultTimeZone;\n\n\tDefaultTimeZoneOffset(TimeZone defaultTimeZone) {\n\t\tthis.defaultTimeZone = defaultTimeZone;\n\t}\n\n\t/**\n\t * Remove the default offset from the given time.\n\t * @param time the time to remove the default offset from\n\t * @return the time with the default offset removed\n\t */\n\tFileTime removeFrom(FileTime time) {\n\t\treturn FileTime.fromMillis(removeFrom(time.toMillis()));\n\t}\n\n\t/**\n\t * Remove the default offset from the given time.\n\t * @param time the time to remove the default offset from\n\t * @return the time with the default offset removed\n\t */\n\tlong removeFrom(long time) {\n\t\treturn time - this.defaultTimeZone.getOffset(time);\n\t}\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/JarTypeFileSpec.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin;\n\nimport java.io.File;\nimport java.util.Collections;\nimport java.util.Set;\nimport java.util.jar.JarFile;\nimport org.gradle.api.specs.Spec;\n\nclass JarTypeFileSpec implements Spec<File> {\n\n\tprivate static final Set<String> EXCLUDED_JAR_TYPES = Collections.singleton(\"dependencies-starter\");\n\n\t@Override\n\tpublic boolean isSatisfiedBy(File file) {\n\t\ttry (JarFile jar = new JarFile(file)) {\n\t\t\tString jarType = jar.getManifest().getMainAttributes().getValue(\"Spring-Boot-Jar-Type\");\n\t\t\tif (jarType != null && EXCLUDED_JAR_TYPES.contains(jarType)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\t// Continue\n\t\t}\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/LoaderZipEntries.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.util.LinkedHashSet;\nimport java.util.Set;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipInputStream;\nimport org.apache.commons.compress.archivers.zip.ZipArchiveEntry;\nimport org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;\nimport org.gradle.api.file.FileTreeElement;\n\n\n/**\n * Internal utility used to copy entries from the {@code spring-boot-loader.jar}.\n *\n * @author Andy Wilkinson\n * @author Phillip Webb\n * @author Scott Frederick\n */\nclass LoaderZipEntries {\n\n\tprivate final Long entryTime;\n\n\tprivate final int dirMode;\n\n\tprivate final int fileMode;\n\n\tLoaderZipEntries(Long entryTime, int dirMode, int fileMode) {\n\t\tthis.entryTime = entryTime;\n\t\tthis.dirMode = dirMode;\n\t\tthis.fileMode = fileMode;\n\t}\n\n\tWrittenEntries writeTo(ZipArchiveOutputStream out) throws IOException {\n\t\tWrittenEntries written = new WrittenEntries();\n\t\ttry (ZipInputStream loaderJar = new ZipInputStream(\n\t\t\t\tgetClass().getResourceAsStream(\"/META-INF/loader/spring-boot-loader.jar\"))) {\n\t\t\tZipEntry entry = loaderJar.getNextEntry();\n\t\t\twhile (entry != null) {\n\t\t\t\tif (entry.isDirectory() && !entry.getName().equals(\"META-INF/\")) {\n\t\t\t\t\twriteDirectory(new ZipArchiveEntry(entry), out);\n\t\t\t\t\twritten.addDirectory(entry);\n\t\t\t\t}\n\t\t\t\telse if (entry.getName().endsWith(\".class\")) {\n\t\t\t\t\twriteClass(new ZipArchiveEntry(entry), loaderJar, out);\n\t\t\t\t\twritten.addFile(entry);\n\t\t\t\t}\n\t\t\t\tentry = loaderJar.getNextEntry();\n\t\t\t}\n\t\t}\n\t\treturn written;\n\t}\n\n\tprivate void writeDirectory(ZipArchiveEntry entry, ZipArchiveOutputStream out) throws IOException {\n\t\tprepareEntry(entry, this.dirMode);\n\t\tout.putArchiveEntry(entry);\n\t\tout.closeArchiveEntry();\n\t}\n\n\tprivate void writeClass(ZipArchiveEntry entry, ZipInputStream in, ZipArchiveOutputStream out) throws IOException {\n\t\tprepareEntry(entry, this.fileMode);\n\t\tout.putArchiveEntry(entry);\n\t\tcopy(in, out);\n\t\tout.closeArchiveEntry();\n\t}\n\n\tprivate void prepareEntry(ZipArchiveEntry entry, int unixMode) {\n\t\tif (this.entryTime != null) {\n\t\t\tentry.setTime(DefaultTimeZoneOffset.INSTANCE.removeFrom(this.entryTime));\n\t\t}\n\t\tentry.setUnixMode(unixMode);\n\t}\n\n\tprivate void copy(InputStream in, OutputStream out) throws IOException {\n\t\tStringUtils.copyTo(in, out);\n\t}\n\n\n\n\t/**\n\t * Tracks entries that have been written.\n\t */\n\tstatic class WrittenEntries {\n\n\t\tprivate final Set<String> directories = new LinkedHashSet<>();\n\n\t\tprivate final Set<String> files = new LinkedHashSet<>();\n\n\t\tprivate void addDirectory(ZipEntry entry) {\n\t\t\tthis.directories.add(entry.getName());\n\t\t}\n\n\t\tprivate void addFile(ZipEntry entry) {\n\t\t\tthis.files.add(entry.getName());\n\t\t}\n\n\t\tboolean isWrittenDirectory(FileTreeElement element) {\n\t\t\tString path = element.getRelativePath().getPathString();\n\t\t\tif (element.isDirectory() && !path.endsWith((\"/\"))) {\n\t\t\t\tpath += \"/\";\n\t\t\t}\n\t\t\treturn this.directories.contains(path);\n\t\t}\n\n\t\tSet<String> getFiles() {\n\t\t\treturn this.files;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/MainClassFinder.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin;\n\nimport java.io.BufferedInputStream;\nimport java.io.File;\nimport java.io.FileFilter;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayDeque;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.Deque;\nimport java.util.Enumeration;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarFile;\nimport org.objectweb.asm.AnnotationVisitor;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.Type;\n\n\n/**\n * Finds any class with a {@code public static main} method by performing a breadth first\n * search.\n *\n * @author Phillip Webb\n * @author Andy Wilkinson\n * @since 1.0.0\n */\npublic abstract class MainClassFinder {\n\n\tprivate static final String DOT_CLASS = \".class\";\n\n\tprivate static final Type STRING_ARRAY_TYPE = Type.getType(String[].class);\n\n\tprivate static final Type MAIN_METHOD_TYPE = Type.getMethodType(Type.VOID_TYPE, STRING_ARRAY_TYPE);\n\n\tprivate static final String MAIN_METHOD_NAME = \"main\";\n\n\tprivate static final FileFilter CLASS_FILE_FILTER = MainClassFinder::isClassFile;\n\n\tprivate static final FileFilter PACKAGE_DIRECTORY_FILTER = MainClassFinder::isPackageDirectory;\n\n\tprivate static boolean isClassFile(File file) {\n\t\treturn file.isFile() && file.getName().endsWith(DOT_CLASS);\n\t}\n\n\tprivate static boolean isPackageDirectory(File file) {\n\t\treturn file.isDirectory() && !file.getName().startsWith(\".\");\n\t}\n\n\t/**\n\t * Find the main class from a given directory.\n\t * @param rootDirectory the root directory to search\n\t * @return the main class or {@code null}\n\t * @throws IOException if the directory cannot be read\n\t */\n\tpublic static String findMainClass(File rootDirectory) throws IOException {\n\t\treturn doWithMainClasses(rootDirectory, MainClass::getName);\n\t}\n\n\t/**\n\t * Find a single main class from the given {@code rootDirectory}.\n\t * @param rootDirectory the root directory to search\n\t * @return the main class or {@code null}\n\t * @throws IOException if the directory cannot be read\n\t */\n\tpublic static String findSingleMainClass(File rootDirectory) throws IOException {\n\t\treturn findSingleMainClass(rootDirectory, null);\n\t}\n\n\t/**\n\t * Find a single main class from the given {@code rootDirectory}. A main class\n\t * annotated with an annotation with the given {@code annotationName} will be\n\t * preferred over a main class with no such annotation.\n\t * @param rootDirectory the root directory to search\n\t * @param annotationName the name of the annotation that may be present on the main\n\t * class\n\t * @return the main class or {@code null}\n\t * @throws IOException if the directory cannot be read\n\t */\n\tpublic static String findSingleMainClass(File rootDirectory, String annotationName) throws IOException {\n\t\tSingleMainClassCallback callback = new SingleMainClassCallback(annotationName);\n\t\tMainClassFinder.doWithMainClasses(rootDirectory, callback);\n\t\treturn callback.getMainClassName();\n\t}\n\n\t/**\n\t * Perform the given callback operation on all main classes from the given root\n\t * directory.\n\t * @param <T> the result type\n\t * @param rootDirectory the root directory\n\t * @param callback the callback\n\t * @return the first callback result or {@code null}\n\t * @throws IOException in case of I/O errors\n\t */\n\tstatic <T> T doWithMainClasses(File rootDirectory, MainClassCallback<T> callback) throws IOException {\n\t\tif (!rootDirectory.exists()) {\n\t\t\treturn null; // nothing to do\n\t\t}\n\t\tif (!rootDirectory.isDirectory()) {\n\t\t\tthrow new IllegalArgumentException(\"Invalid root directory '\" + rootDirectory + \"'\");\n\t\t}\n\t\tString prefix = rootDirectory.getAbsolutePath() + \"/\";\n\t\tDeque<File> stack = new ArrayDeque<>();\n\t\tstack.push(rootDirectory);\n\t\twhile (!stack.isEmpty()) {\n\t\t\tFile file = stack.pop();\n\t\t\tif (file.isFile()) {\n\t\t\t\ttry (InputStream inputStream = new FileInputStream(file)) {\n\t\t\t\t\tClassDescriptor classDescriptor = createClassDescriptor(inputStream);\n\t\t\t\t\tif (classDescriptor != null && classDescriptor.isMainMethodFound()) {\n\t\t\t\t\t\tString className = convertToClassName(file.getAbsolutePath(), prefix);\n\t\t\t\t\t\tT result = callback.doWith(new MainClass(className, classDescriptor.getAnnotationNames()));\n\t\t\t\t\t\tif (result != null) {\n\t\t\t\t\t\t\treturn result;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (file.isDirectory()) {\n\t\t\t\tpushAllSorted(stack, file.listFiles(PACKAGE_DIRECTORY_FILTER));\n\t\t\t\tpushAllSorted(stack, file.listFiles(CLASS_FILE_FILTER));\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static void pushAllSorted(Deque<File> stack, File[] files) {\n\t\tArrays.sort(files, Comparator.comparing(File::getName));\n\t\tfor (File file : files) {\n\t\t\tstack.push(file);\n\t\t}\n\t}\n\n\t/**\n\t * Find the main class in a given jar file.\n\t * @param jarFile the jar file to search\n\t * @param classesLocation the location within the jar containing classes\n\t * @return the main class or {@code null}\n\t * @throws IOException if the jar file cannot be read\n\t */\n\tpublic static String findMainClass(JarFile jarFile, String classesLocation) throws IOException {\n\t\treturn doWithMainClasses(jarFile, classesLocation, MainClass::getName);\n\t}\n\n\t/**\n\t * Find a single main class in a given jar file.\n\t * @param jarFile the jar file to search\n\t * @param classesLocation the location within the jar containing classes\n\t * @return the main class or {@code null}\n\t * @throws IOException if the jar file cannot be read\n\t */\n\tpublic static String findSingleMainClass(JarFile jarFile, String classesLocation) throws IOException {\n\t\treturn findSingleMainClass(jarFile, classesLocation, null);\n\t}\n\n\t/**\n\t * Find a single main class in a given jar file. A main class annotated with an\n\t * annotation with the given {@code annotationName} will be preferred over a main\n\t * class with no such annotation.\n\t * @param jarFile the jar file to search\n\t * @param classesLocation the location within the jar containing classes\n\t * @param annotationName the name of the annotation that may be present on the main\n\t * class\n\t * @return the main class or {@code null}\n\t * @throws IOException if the jar file cannot be read\n\t */\n\tpublic static String findSingleMainClass(JarFile jarFile, String classesLocation, String annotationName)\n\t\t\tthrows IOException {\n\t\tSingleMainClassCallback callback = new SingleMainClassCallback(annotationName);\n\t\tMainClassFinder.doWithMainClasses(jarFile, classesLocation, callback);\n\t\treturn callback.getMainClassName();\n\t}\n\n\t/**\n\t * Perform the given callback operation on all main classes from the given jar.\n\t * @param <T> the result type\n\t * @param jarFile the jar file to search\n\t * @param classesLocation the location within the jar containing classes\n\t * @param callback the callback\n\t * @return the first callback result or {@code null}\n\t * @throws IOException in case of I/O errors\n\t */\n\tstatic <T> T doWithMainClasses(JarFile jarFile, String classesLocation, MainClassCallback<T> callback)\n\t\t\tthrows IOException {\n\t\tList<JarEntry> classEntries = getClassEntries(jarFile, classesLocation);\n\t\tclassEntries.sort(new ClassEntryComparator());\n\t\tfor (JarEntry entry : classEntries) {\n\t\t\ttry (InputStream inputStream = new BufferedInputStream(jarFile.getInputStream(entry))) {\n\t\t\t\tClassDescriptor classDescriptor = createClassDescriptor(inputStream);\n\t\t\t\tif (classDescriptor != null && classDescriptor.isMainMethodFound()) {\n\t\t\t\t\tString className = convertToClassName(entry.getName(), classesLocation);\n\t\t\t\t\tT result = callback.doWith(new MainClass(className, classDescriptor.getAnnotationNames()));\n\t\t\t\t\tif (result != null) {\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static String convertToClassName(String name, String prefix) {\n\t\tname = name.replace('/', '.');\n\t\tname = name.replace('\\\\', '.');\n\t\tname = name.substring(0, name.length() - DOT_CLASS.length());\n\t\tif (prefix != null) {\n\t\t\tname = name.substring(prefix.length());\n\t\t}\n\t\treturn name;\n\t}\n\n\tprivate static List<JarEntry> getClassEntries(JarFile source, String classesLocation) {\n\t\tclassesLocation = (classesLocation != null) ? classesLocation : \"\";\n\t\tEnumeration<JarEntry> sourceEntries = source.entries();\n\t\tList<JarEntry> classEntries = new ArrayList<>();\n\t\twhile (sourceEntries.hasMoreElements()) {\n\t\t\tJarEntry entry = sourceEntries.nextElement();\n\t\t\tif (entry.getName().startsWith(classesLocation) && entry.getName().endsWith(DOT_CLASS)) {\n\t\t\t\tclassEntries.add(entry);\n\t\t\t}\n\t\t}\n\t\treturn classEntries;\n\t}\n\n\tprivate static ClassDescriptor createClassDescriptor(InputStream inputStream) {\n\t\ttry {\n\t\t\tClassReader classReader = new ClassReader(inputStream);\n\t\t\tClassDescriptor classDescriptor = new ClassDescriptor();\n\t\t\tclassReader.accept(classDescriptor, ClassReader.SKIP_CODE);\n\t\t\treturn classDescriptor;\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate static class ClassEntryComparator implements Comparator<JarEntry> {\n\n\t\t@Override\n\t\tpublic int compare(JarEntry o1, JarEntry o2) {\n\t\t\tInteger d1 = getDepth(o1);\n\t\t\tInteger d2 = getDepth(o2);\n\t\t\tint depthCompare = d1.compareTo(d2);\n\t\t\tif (depthCompare != 0) {\n\t\t\t\treturn depthCompare;\n\t\t\t}\n\t\t\treturn o1.getName().compareTo(o2.getName());\n\t\t}\n\n\t\tprivate int getDepth(JarEntry entry) {\n\t\t\treturn entry.getName().split(\"/\").length;\n\t\t}\n\n\t}\n\n\tprivate static class ClassDescriptor extends ClassVisitor {\n\n\t\tprivate final Set<String> annotationNames = new LinkedHashSet<>();\n\n\t\tprivate boolean mainMethodFound;\n\n\t\tClassDescriptor() {\n\t\t\tsuper(Opcodes.ASM7);\n\t\t}\n\n\t\t@Override\n\t\tpublic AnnotationVisitor visitAnnotation(String desc, boolean visible) {\n\t\t\tthis.annotationNames.add(Type.getType(desc).getClassName());\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {\n\t\t\tif (isAccess(access, Opcodes.ACC_PUBLIC, Opcodes.ACC_STATIC) && MAIN_METHOD_NAME.equals(name)\n\t\t\t\t\t&& MAIN_METHOD_TYPE.getDescriptor().equals(desc)) {\n\t\t\t\tthis.mainMethodFound = true;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tprivate boolean isAccess(int access, int... requiredOpsCodes) {\n\t\t\tfor (int requiredOpsCode : requiredOpsCodes) {\n\t\t\t\tif ((access & requiredOpsCode) == 0) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tboolean isMainMethodFound() {\n\t\t\treturn this.mainMethodFound;\n\t\t}\n\n\t\tSet<String> getAnnotationNames() {\n\t\t\treturn this.annotationNames;\n\t\t}\n\n\t}\n\n\t/**\n\t * Callback for handling {@link MainClass MainClasses}.\n\t *\n\t * @param <T> the callback's return type\n\t */\n\tinterface MainClassCallback<T> {\n\n\t\t/**\n\t\t * Handle the specified main class.\n\t\t * @param mainClass the main class\n\t\t * @return a non-null value if processing should end or {@code null} to continue\n\t\t */\n\t\tT doWith(MainClass mainClass);\n\n\t}\n\n\t/**\n\t * A class with a {@code main} method.\n\t */\n\tstatic final class MainClass {\n\n\t\tprivate final String name;\n\n\t\tprivate final Set<String> annotationNames;\n\n\t\t/**\n\t\t * Creates a new {@code MainClass} rather represents the main class with the given\n\t\t * {@code name}. The class is annotated with the annotations with the given\n\t\t * {@code annotationNames}.\n\t\t * @param name the name of the class\n\t\t * @param annotationNames the names of the annotations on the class\n\t\t */\n\t\tMainClass(String name, Set<String> annotationNames) {\n\t\t\tthis.name = name;\n\t\t\tthis.annotationNames = Collections.unmodifiableSet(new HashSet<>(annotationNames));\n\t\t}\n\n\t\tString getName() {\n\t\t\treturn this.name;\n\t\t}\n\n\t\tSet<String> getAnnotationNames() {\n\t\t\treturn this.annotationNames;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\tif (this == obj) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (obj == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (getClass() != obj.getClass()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tMainClass other = (MainClass) obj;\n\t\t\treturn this.name.equals(other.name);\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn this.name.hashCode();\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn this.name;\n\t\t}\n\n\t}\n\n\t/**\n\t * Find a single main class, throwing an {@link IllegalStateException} if multiple\n\t * candidates exist.\n\t */\n\tprivate static final class SingleMainClassCallback implements MainClassCallback<Object> {\n\n\t\tprivate final Set<MainClass> mainClasses = new LinkedHashSet<>();\n\n\t\tprivate final String annotationName;\n\n\t\tprivate SingleMainClassCallback(String annotationName) {\n\t\t\tthis.annotationName = annotationName;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object doWith(MainClass mainClass) {\n\t\t\tthis.mainClasses.add(mainClass);\n\t\t\treturn null;\n\t\t}\n\n\t\tprivate String getMainClassName() {\n\t\t\tSet<MainClass> matchingMainClasses = new LinkedHashSet<>();\n\t\t\tif (this.annotationName != null) {\n\t\t\t\tfor (MainClass mainClass : this.mainClasses) {\n\t\t\t\t\tif (mainClass.getAnnotationNames().contains(this.annotationName)) {\n\t\t\t\t\t\tmatchingMainClasses.add(mainClass);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (matchingMainClasses.isEmpty()) {\n\t\t\t\tmatchingMainClasses.addAll(this.mainClasses);\n\t\t\t}\n\t\t\tif (matchingMainClasses.size() > 1) {\n\t\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\t\"Unable to find a single main class from the following candidates \" + matchingMainClasses);\n\t\t\t}\n\t\t\treturn (matchingMainClasses.isEmpty() ? null : matchingMainClasses.iterator().next().getName());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/Nullable.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport javax.annotation.Nonnull;\nimport javax.annotation.meta.TypeQualifierNickname;\nimport javax.annotation.meta.When;\n\n@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Nonnull(when = When.MAYBE)\n@TypeQualifierNickname\npublic @interface Nullable {\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ResolveMainClassName.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.StandardOpenOption;\nimport java.util.Objects;\nimport java.util.concurrent.Callable;\nimport org.gradle.api.DefaultTask;\nimport org.gradle.api.InvalidUserDataException;\nimport org.gradle.api.Project;\nimport org.gradle.api.Task;\nimport org.gradle.api.Transformer;\nimport org.gradle.api.file.FileCollection;\nimport org.gradle.api.file.RegularFile;\nimport org.gradle.api.file.RegularFileProperty;\nimport org.gradle.api.plugins.BasePlugin;\nimport org.gradle.api.plugins.JavaApplication;\nimport org.gradle.api.provider.Property;\nimport org.gradle.api.provider.Provider;\nimport org.gradle.api.tasks.Classpath;\nimport org.gradle.api.tasks.Input;\nimport org.gradle.api.tasks.Optional;\nimport org.gradle.api.tasks.OutputFile;\nimport org.gradle.api.tasks.TaskAction;\nimport org.gradle.api.tasks.TaskProvider;\nimport org.gradle.work.DisableCachingByDefault;\n\n\n@DisableCachingByDefault(because = \"Not worth caching\")\npublic class ResolveMainClassName extends DefaultTask {\n\n\tprivate static final String SPRING_BOOT_APPLICATION_CLASS_NAME = \"org.springframework.boot.autoconfigure.SpringBootApplication\";\n\n\tprivate final RegularFileProperty outputFile;\n\n\tprivate final Property<String> configuredMainClass;\n\n\tprivate FileCollection classpath;\n\n\t/**\n\t * Creates a new instance of the {@code ResolveMainClassName} task.\n\t */\n\tpublic ResolveMainClassName() {\n\t\tthis.outputFile = getProject().getObjects().fileProperty();\n\t\tthis.configuredMainClass = getProject().getObjects().property(String.class);\n\t}\n\n\t/**\n\t * Returns the classpath that the task will examine when resolving the main class\n\t * name.\n\t * @return the classpath\n\t */\n\t@Classpath\n\tpublic FileCollection getClasspath() {\n\t\treturn this.classpath;\n\t}\n\n\t/**\n\t * Sets the classpath that the task will examine when resolving the main class name.\n\t * @param classpath the classpath\n\t */\n\tpublic void setClasspath(FileCollection classpath) {\n\t\tsetClasspath((Object) classpath);\n\t}\n\n\t/**\n\t * Sets the classpath that the task will examine when resolving the main class name.\n\t * The given {@code classpath} is evaluated as per {@link Project#files(Object...)}.\n\t * @param classpath the classpath\n\t * @since 2.5.10\n\t */\n\tpublic void setClasspath(Object classpath) {\n\t\tthis.classpath = getProject().files(classpath);\n\t}\n\n\t/**\n\t * Returns the property for the task's output file that will contain the name of the\n\t * main class.\n\t * @return the output file\n\t */\n\t@OutputFile\n\tpublic RegularFileProperty getOutputFile() {\n\t\treturn this.outputFile;\n\t}\n\n\t/**\n\t * Returns the property for the explicitly configured main class name that should be\n\t * used in favor of resolving the main class name from the classpath.\n\t * @return the configured main class name property\n\t */\n\t@Input\n\t@Optional\n\tpublic Property<String> getConfiguredMainClassName() {\n\t\treturn this.configuredMainClass;\n\t}\n\n\t@TaskAction\n\tvoid resolveAndStoreMainClassName() throws IOException {\n\t\tFile outputFile = this.outputFile.getAsFile().get();\n\t\toutputFile.getParentFile().mkdirs();\n\t\tString mainClassName = resolveMainClassName();\n\t\tFiles.write(outputFile.toPath(), mainClassName.getBytes(StandardCharsets.UTF_8), StandardOpenOption.WRITE,\n\t\t\t\tStandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);\n\t}\n\n\tprivate String resolveMainClassName() {\n\t\tString configuredMainClass = this.configuredMainClass.getOrNull();\n\t\tif (configuredMainClass != null) {\n\t\t\treturn configuredMainClass;\n\t\t}\n\t\treturn getClasspath().filter(File::isDirectory)\n\t\t\t.getFiles()\n\t\t\t.stream()\n\t\t\t.map(this::findMainClass)\n\t\t\t.filter(Objects::nonNull)\n\t\t\t.findFirst()\n\t\t\t.orElse(\"\");\n\t}\n\n\tprivate String findMainClass(File file) {\n\t\ttry {\n\t\t\t// TODO: compatible with non-spring-boot-project\n\t\t\treturn MainClassFinder.findSingleMainClass(file, SPRING_BOOT_APPLICATION_CLASS_NAME);\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tProvider<String> readMainClassName() {\n\t\treturn this.outputFile.map(new ClassNameReader());\n\t}\n\n\tstatic TaskProvider<ResolveMainClassName> registerForTask(String taskName, Project project,\n\t\t\tCallable<FileCollection> classpath) {\n\t\tTaskProvider<ResolveMainClassName> resolveMainClassNameProvider = project.getTasks()\n\t\t\t.register(taskName + \"MainClassName\", ResolveMainClassName.class, (resolveMainClassName) -> {\n\t\t\t\tresolveMainClassName\n\t\t\t\t\t.setDescription(\"Resolves the name of the application's main class for the \" + taskName + \" task.\");\n\t\t\t\tresolveMainClassName.setGroup(BasePlugin.BUILD_GROUP);\n\t\t\t\tresolveMainClassName.setClasspath(classpath);\n\t\t\t\tresolveMainClassName.getConfiguredMainClassName().convention(project.provider(() -> {\n\t\t\t\t\tString javaApplicationMainClass = getJavaApplicationMainClass(project);\n\t\t\t\t\tif (javaApplicationMainClass != null) {\n\t\t\t\t\t\treturn javaApplicationMainClass;\n\t\t\t\t\t}\n\t\t\t\t\tSofaArkGradlePluginExtension springBootExtension = project.getExtensions()\n\t\t\t\t\t\t.findByType(SofaArkGradlePluginExtension.class);\n\t\t\t\t\treturn springBootExtension.getMainClass().getOrNull();\n\t\t\t\t}));\n\t\t\t\tresolveMainClassName.getOutputFile()\n\t\t\t\t\t.set(project.getLayout().getBuildDirectory().file(taskName + \"MainClassName\"));\n\t\t\t});\n\t\treturn resolveMainClassNameProvider;\n\t}\n\n\tprivate static String getJavaApplicationMainClass(Project project) {\n\t\tJavaApplication javaApplication = project.getExtensions().findByType(JavaApplication.class);\n\t\tif (javaApplication == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn javaApplication.getMainClass().getOrNull();\n\t}\n\n\tprivate static final class ClassNameReader implements Transformer<String, RegularFile> {\n\n\t\t@Override\n\t\tpublic String transform(RegularFile file) {\n\t\t\tif (file.getAsFile().length() == 0) {\n\t\t\t\tthrow new InvalidUserDataException(\n\t\t\t\t\t\t\"Main class name has not been configured and it could not be resolved\");\n\t\t\t}\n\t\t\tPath output = file.getAsFile().toPath();\n\t\t\ttry {\n\t\t\t\treturn new String(Files.readAllBytes(output), StandardCharsets.UTF_8);\n\t\t\t}\n\t\t\tcatch (IOException ex) {\n\t\t\t\tthrow new RuntimeException(\"Failed to read main class name from '\" + output + \"'\");\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePlugin.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin;\n\nimport org.gradle.api.GradleException;\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\nimport org.gradle.internal.impldep.org.bouncycastle.pqc.crypto.newhope.NHSecretKeyProcessor.PartyUBuilder;\nimport org.gradle.util.GradleVersion;\n\npublic class SofaArkGradlePlugin implements Plugin<Project> {\n\n    public static final String ARK_VERSION = \"2.2.14\";\n    public static final String ARK_BIZ_TASK_NAME = \"arkJar\";\n    public static final String DEVELOPMENT_ONLY_CONFIGURATION_NAME = \"developmentOnly\";\n    public static final String PRODUCTION_RUNTIME_CLASSPATH_CONFIGURATION_NAME = \"productionRuntimeClasspath\";\n    public static final String ARK_BOOTSTRAP = \"com.alipay.sofa:sofa-ark-all:\";\n\n    @Override\n    public void apply(Project project) {\n        verifyGradleVersion();\n        createAndSetExtension(project);\n        registerPluginActions(project);\n    }\n\n    private void verifyGradleVersion() {\n        GradleVersion currentVersion = GradleVersion.current();\n        if (currentVersion.compareTo(GradleVersion.version(\"6.8\")) < 0) {\n            throw new GradleException(\"Spring Boot plugin requires Gradle 6.8.+ \"\n                + \"The current version is \" + currentVersion);\n        }\n    }\n\n    private void createAndSetExtension(Project project) {\n        project.getExtensions().create(\"arkConfig\", SofaArkGradlePluginExtension.class, project);\n    }\n\n    private void  registerPluginActions(Project project) {\n        ArkPluginAction arkAction =  new ArkPluginAction();\n        arkAction.execute(project);\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/SofaArkGradlePluginExtension.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin;\n\nimport org.gradle.api.Project;\nimport org.gradle.api.file.DirectoryProperty;\nimport org.gradle.api.provider.Property;\nimport org.gradle.api.provider.SetProperty;\nimport org.gradle.api.tasks.Optional;\nimport org.gradle.api.tasks.OutputDirectory;\n\nabstract public class SofaArkGradlePluginExtension {\n\n    private final String ARK_JAR_PLUGIN_VERSION = \"2.2.14\";\n\n    private final Integer PRIORITY = 100;\n\n    private final String ARK_CLASSIFIER = \"ark-executable\";\n\n    private final String FINAL_NAME = \"\";\n    private final String BIZ_NAME = \"\";\n    private final String BIZ_CLASSIFIER = \"ark-biz\";\n\n    private final String WEB_CONTEXT_PATH = \"/\";\n\n    private final Property<String> mainClass;\n\n    public SofaArkGradlePluginExtension(Project project){\n\n        this.mainClass = project.getObjects().property(String.class);\n\n        getPriority().convention(project.provider(() -> PRIORITY));\n        getArkClassifier().convention(project.provider(() -> ARK_CLASSIFIER));\n        getFinalName().convention(project.provider(() -> FINAL_NAME));\n        getBizName().convention(project.provider(() -> BIZ_NAME));\n        getBizClassifier().convention(project.provider(() -> BIZ_CLASSIFIER));\n\n        getBizVersion().convention(project.provider(() -> project.getVersion().toString()));\n        getWebContextPath().convention(project.provider(()-> WEB_CONTEXT_PATH));\n\n        getOutputDirectory().convention(project.getLayout().getBuildDirectory().dir(\"libs\"));\n    }\n\n    public Property<String> getMainClass() {\n        return this.mainClass;\n    }\n\n    @OutputDirectory\n    abstract public DirectoryProperty getOutputDirectory();\n\n    abstract public Property<String> getFinalName();\n\n    abstract public Property<String> getArkClassifier();\n\n    abstract public Property<String> getWebContextPath();\n\n\n    abstract public Property<String> getBizName();\n    abstract public Property<String> getBizClassifier();\n    abstract public Property<String> getBizVersion();\n\n    abstract public Property<Integer> getPriority();\n\n    @Optional\n    abstract public SetProperty<String> getExcludes();\n    @Optional\n    abstract public SetProperty<String> getExcludeArtifactIds();\n    @Optional\n    abstract public SetProperty<String> getExcludeGroupIds();\n    @Optional\n    abstract public SetProperty<String> getDenyImportPackages();\n    @Optional\n    abstract public SetProperty<String> getDenyImportClasses();\n    @Optional\n    abstract public SetProperty<String> getDenyImportResources();\n    @Optional\n    abstract public SetProperty<String> getInjectPluginDependencies();\n    @Optional\n    abstract public SetProperty<String> getInjectPluginExportPackages();\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/StringUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.util.Collection;\nimport java.util.Iterator;\n\npublic class StringUtils {\n\n    static int copyTo(InputStream in, OutputStream out) throws IOException {\n        int byteCount = 0;\n\n        int bytesRead;\n        for(byte[] buffer = new byte[4096]; (bytesRead = in.read(buffer)) != -1; byteCount += bytesRead) {\n            out.write(buffer, 0, bytesRead);\n        }\n\n        out.flush();\n        return byteCount;\n    }\n\n    public static String collectionToCommaDelimitedString(@Nullable Collection<?> coll) {\n        return collectionToDelimitedString(coll, \",\");\n    }\n\n    public static String collectionToDelimitedString(@Nullable Collection<?> coll, String delim) {\n        return collectionToDelimitedString(coll, delim, \"\", \"\");\n    }\n\n    public static String collectionToDelimitedString(\n        @Nullable Collection<?> coll, String delim, String prefix, String suffix) {\n\n        if (CollectionUtils.isEmpty(coll)) {\n            return \"\";\n        }\n\n        int totalLength = coll.size() * (prefix.length() + suffix.length()) + (coll.size() - 1) * delim.length();\n        for (Object element : coll) {\n            totalLength += String.valueOf(element).length();\n        }\n\n        StringBuilder sb = new StringBuilder(totalLength);\n        Iterator<?> it = coll.iterator();\n        while (it.hasNext()) {\n            sb.append(prefix).append(it.next()).append(suffix);\n            if (it.hasNext()) {\n                sb.append(delim);\n            }\n        }\n        return sb.toString();\n    }\n\n\n\n    static class CollectionUtils {\n        public static boolean isEmpty(@Nullable Collection<?> collection) {\n            return (collection == null || collection.isEmpty());\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-gradle-plugin/src/main/java/com/alipay/sofa/ark/plugin/ZipCompression.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin;\n\nimport java.util.zip.ZipEntry;\n\npublic enum ZipCompression {\n\n\t/**\n\t * The entry should be {@link ZipEntry#STORED} in the archive.\n\t */\n\tSTORED,\n\n\t/**\n\t * The entry should be {@link ZipEntry#DEFLATED} in the archive.\n\t */\n\tDEFLATED\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-maven-plugin/CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Module Overview\n\n**Artifact ID**: `sofa-ark-maven-plugin`\n**Packaging**: `maven-plugin`\n**Goal**: `repackage`\n\nThis Maven plugin packages applications into Ark executable JARs and Biz modules.\n\n## Purpose\n\n- Transform a Spring Boot or plain Java application into an Ark executable JAR\n- Create Biz module JARs for dynamic deployment\n- Configure dependency filtering and classloader isolation\n\n## Key Classes\n\n### `RepackageMojo`\nMain Maven Mojo bound to `package` phase:\n- Input: Compiled application JAR\n- Output: `*-ark-executable.jar` (Fat Jar) and/or `*-ark-biz.jar` (Biz module)\n\nKey configuration parameters:\n- `bizName` / `bizVersion` - Module identity\n- `priority` - Startup priority (default: 100)\n- `mainClass` - Entry point class\n- `excludes` / `excludeGroupIds` / `excludeArtifactIds` - Dependencies to exclude\n- `includes` / `includeGroupIds` / `includeArtifactIds` - Dependencies to include\n- `denyImportPackages/classes/Resources` - Classloader filtering\n- `skipArkExecutable` - Skip creating Fat Jar\n- `keepArkBizJar` - Keep Biz JAR after packaging\n- `declaredMode` - Enable declared dependency mode\n- `webContextPath` - Web context path for web apps\n\n### `ModuleSlimExecutor`\nHandles dependency slimming - removing unnecessary dependencies from the package.\n\n### `ModuleSlimConfig`\nConfiguration for dependency slimming:\n- `packExcludesConfig` - Exclude rules file\n- `baseDependencyParentIdentity` - Base dependency filtering\n\n### `ArtifactsLibraries`\nHandles library resolution and unpacking.\n\n### `model.ArkConfigHolder`\nLoads Ark configuration from `conf/ark/bootstrap.yml` or `bootstrap.properties`.\n\n## Usage\n\n```xml\n<plugin>\n    <groupId>com.alipay.sofa</groupId>\n    <artifactId>sofa-ark-maven-plugin</artifactId>\n    <executions>\n        <execution>\n            <goals>\n                <goal>repackage</goal>\n            </goals>\n        </execution>\n    </executions>\n    <configuration>\n        <bizName>my-app</bizName>\n        <bizVersion>1.0.0</bizVersion>\n    </configuration>\n</plugin>\n```\n\n## Dependencies\n\n- `sofa-ark-common` - Utilities\n- `sofa-ark-tools` - Repackaging logic\n- Maven Core/Plugin APIs"
  },
  {
    "path": "sofa-ark-parent/support/ark-maven-plugin/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>sofa-ark-support</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <packaging>maven-plugin</packaging>\n\n    <artifactId>sofa-ark-maven-plugin</artifactId>\n    <name>${project.groupId}:${project.artifactId}</name>\n\n    <dependencies>\n\n        <!--SOFAArk modules-->\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-common</artifactId>\n            <exclusions>\n                <exclusion>\n                    <groupId>com.alipay.sofa</groupId>\n                    <artifactId>sofa-ark-spi</artifactId>\n                </exclusion>\n                <exclusion>\n                    <groupId>com.google.inject</groupId>\n                    <artifactId>guice</artifactId>\n                </exclusion>\n                <exclusion>\n                    <groupId>com.alipay.sofa</groupId>\n                    <artifactId>log-sofa-boot-starter</artifactId>\n                </exclusion>\n                <exclusion>\n                    <groupId>org.slf4j</groupId>\n                    <artifactId>slf4j-log4j12</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-tools</artifactId>\n        </dependency>\n\n        <!--maven-->\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-archiver</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-artifact</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-plugin-api</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-core</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven.shared</groupId>\n            <artifactId>maven-invoker</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.maven.plugin-tools</groupId>\n            <artifactId>maven-plugin-annotations</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.maven.plugins</groupId>\n            <artifactId>maven-dependency-plugin</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.maven.shared</groupId>\n            <artifactId>maven-common-artifact-filters</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.sonatype.plexus</groupId>\n            <artifactId>plexus-build-api</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-inline</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.fasterxml.jackson.core</groupId>\n            <artifactId>jackson-databind</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.yaml</groupId>\n            <artifactId>snakeyaml</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-plugin-plugin</artifactId>\n                <configuration>\n                    <!--add this config when use maven2-->\n                    <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>\n                </configuration>\n                <executions>\n                    <execution>\n                        <id>mojo-descriptor</id>\n                        <goals>\n                            <goal>descriptor</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-maven-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ArtifactsLibraries.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.boot.mojo;\n\nimport java.io.IOException;\nimport java.util.*;\n\nimport org.apache.maven.artifact.Artifact;\nimport org.apache.maven.model.Dependency;\nimport org.apache.maven.plugin.logging.Log;\n\nimport com.alipay.sofa.ark.tools.Libraries;\nimport com.alipay.sofa.ark.tools.Library;\nimport com.alipay.sofa.ark.tools.LibraryCallback;\nimport com.alipay.sofa.ark.tools.LibraryScope;\n\n/**\n * {@link Libraries} backed by Maven {@link Artifact}s.\n *\n * @author Phillip Webb\n * @author Andy Wilkinson\n * @author Stephane Nicoll\n */\npublic class ArtifactsLibraries implements Libraries {\n\n    private static final Map<String, LibraryScope> SCOPES;\n\n    static {\n        Map<String, LibraryScope> libraryScopes = new HashMap<>();\n        libraryScopes.put(Artifact.SCOPE_COMPILE, LibraryScope.COMPILE);\n        libraryScopes.put(Artifact.SCOPE_RUNTIME, LibraryScope.RUNTIME);\n        libraryScopes.put(Artifact.SCOPE_PROVIDED, LibraryScope.PROVIDED);\n        libraryScopes.put(Artifact.SCOPE_SYSTEM, LibraryScope.COMPILE);\n        SCOPES = Collections.unmodifiableMap(libraryScopes);\n    }\n\n    private final Set<Artifact>                    artifacts;\n\n    private final Collection<Dependency>           unpacks;\n\n    private final Log                              log;\n\n    public ArtifactsLibraries(Set<Artifact> artifacts, Collection<Dependency> unpacks, Log log) {\n        this.artifacts = artifacts;\n        this.unpacks = unpacks;\n        this.log = log;\n    }\n\n    @Override\n    public void doWithLibraries(LibraryCallback callback) throws IOException {\n        Set<String> duplicates = getDuplicates(artifacts);\n        for (Artifact artifact : this.artifacts) {\n            LibraryScope scope = SCOPES.get(artifact.getScope());\n            if (scope != null && artifact.getFile() != null) {\n                String name = getFileName(artifact);\n                if (duplicates.contains(name)) {\n                    this.log.debug(String.format(\"Duplicate found: %s\", name));\n                    name = artifact.getGroupId() + \"-\" + name;\n                    this.log.debug(String.format(\"Renamed to: %s\", name));\n                }\n                Library library = new Library(name, artifact.getFile(), scope,\n                    isUnpackRequired(artifact));\n                library.setArtifactId(artifact.getArtifactId());\n                callback.library(library);\n            }\n        }\n    }\n\n    private Set<String> getDuplicates(Set<Artifact> artifacts) {\n        Set<String> duplicates = new HashSet<>();\n        Set<String> seen = new HashSet<>();\n        for (Artifact artifact : artifacts) {\n            String fileName = getFileName(artifact);\n            if (artifact.getFile() != null && !seen.add(fileName)) {\n                duplicates.add(fileName);\n            }\n        }\n        return duplicates;\n    }\n\n    private boolean isUnpackRequired(Artifact artifact) {\n        if (this.unpacks != null) {\n            for (Dependency unpack : this.unpacks) {\n                if (artifact.getGroupId().equals(unpack.getGroupId())\n                    && artifact.getArtifactId().equals(unpack.getArtifactId())) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    private String getFileName(Artifact artifact) {\n        StringBuilder sb = new StringBuilder();\n        sb.append(artifact.getArtifactId()).append(\"-\").append(artifact.getBaseVersion());\n        String classifier = artifact.getClassifier();\n        if (classifier != null) {\n            sb.append(\"-\").append(classifier);\n        }\n        sb.append(\".\").append(artifact.getArtifactHandler().getExtension());\n        return sb.toString();\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-maven-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/MavenUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.boot.mojo;\n\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.tools.ArtifactItem;\nimport org.apache.maven.artifact.Artifact;\nimport org.apache.maven.model.Dependency;\nimport org.apache.maven.model.Parent;\nimport org.apache.maven.project.MavenProject;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.STRING_COLON;\nimport static java.util.Arrays.asList;\n\npublic class MavenUtils {\n    public static boolean isRootProject(MavenProject project) {\n        if (project == null) {\n            return true;\n        }\n\n        if (project.hasParent() && project.getParent().getBasedir() != null) {\n            return false;\n        }\n        return true;\n    }\n\n    public static MavenProject getRootProject(MavenProject project) {\n        if (project == null) {\n            return null;\n        }\n        MavenProject parent = project;\n        while (parent.hasParent() && parent.getParent().getBasedir() != null) {\n            parent = parent.getParent();\n        }\n        return parent;\n    }\n\n    /**\n     * @param depTreeContent\n     * @return\n     */\n    public static Set<ArtifactItem> convert(String depTreeContent) {\n        Set<ArtifactItem> artifactItems = new HashSet<>();\n        String[] contents = depTreeContent.split(\"\\n\");\n\n        for (String content : contents) {\n            ArtifactItem artifactItem = getArtifactItem(content);\n            if (artifactItem != null && !\"test\".equals(artifactItem.getScope())) {\n                artifactItems.add(artifactItem);\n            }\n        }\n\n        return artifactItems;\n    }\n\n    private static ArtifactItem getArtifactItem(String lineContent) {\n        if (StringUtils.isEmpty(lineContent)) {\n            return null;\n        }\n        lineContent = StringUtils.removeCR(lineContent);\n        String[] contentInfos = lineContent.split(\" \");\n        if (contentInfos.length == 0) {\n            return null;\n        }\n        Optional<String> artifactStrOp = Arrays.stream(contentInfos).filter(c -> c.contains(\":\")).findFirst();\n        if (!artifactStrOp.isPresent()) {\n            return null;\n        }\n        String[] artifactInfos = artifactStrOp.get().split(\":\");\n\n        ArtifactItem artifactItem = new ArtifactItem();\n        if (artifactInfos.length == 5) {\n            // like \"com.alipay.sofa:healthcheck-sofa-boot-starter:jar:3.11.1:provided\"\n\n            artifactItem.setGroupId(artifactInfos[0]);\n            artifactItem.setArtifactId(artifactInfos[1]);\n            artifactItem.setType(artifactInfos[2]);\n            artifactItem.setVersion(artifactInfos[3]);\n            artifactItem.setScope(artifactInfos[4]);\n        } else if (artifactInfos.length == 6) {\n            // like \"io.sofastack:dynamic-stock-mng:jar:ark-biz:1.0.0:compile\"\n\n            artifactItem.setGroupId(artifactInfos[0]);\n            artifactItem.setArtifactId(artifactInfos[1]);\n            artifactItem.setType(artifactInfos[2]);\n            artifactItem.setClassifier(artifactInfos[3]);\n            artifactItem.setVersion(artifactInfos[4]);\n            artifactItem.setScope(artifactInfos[5]);\n        } else {\n            return null;\n        }\n        return artifactItem;\n    }\n\n    private static List<String> UN_LOG_SCOPES = asList(\"provided\", \"test\", \"import\", \"system\");\n\n    public static boolean inUnLogScopes(String scope) {\n        return UN_LOG_SCOPES.contains(scope);\n    }\n\n    public static String getGAVIdentity(Artifact artifact) {\n        return artifact.getGroupId() + STRING_COLON + artifact.getArtifactId() + STRING_COLON\n               + artifact.getBaseVersion();\n    }\n\n    public static String getArtifactIdentity(Artifact artifact) {\n        if (artifact.hasClassifier()) {\n            return artifact.getGroupId() + STRING_COLON + artifact.getArtifactId() + STRING_COLON\n                   + artifact.getBaseVersion() + STRING_COLON + artifact.getClassifier()\n                   + STRING_COLON + artifact.getType();\n        } else {\n            return artifact.getGroupId() + STRING_COLON + artifact.getArtifactId() + STRING_COLON\n                   + artifact.getBaseVersion() + STRING_COLON + artifact.getType();\n        }\n    }\n\n    public static String getArtifactIdentityWithoutVersion(Artifact artifact) {\n        if (artifact.hasClassifier()) {\n            return artifact.getGroupId() + STRING_COLON + artifact.getArtifactId() + STRING_COLON\n                   + artifact.getClassifier() + STRING_COLON + artifact.getType();\n        } else {\n            return artifact.getGroupId() + STRING_COLON + artifact.getArtifactId() + STRING_COLON\n                   + artifact.getType();\n        }\n\n    }\n\n    public static String getDependencyIdentity(Dependency dependency) {\n        if (org.apache.commons.lang3.StringUtils.isNotEmpty(dependency.getClassifier())) {\n            return dependency.getGroupId() + STRING_COLON + dependency.getArtifactId()\n                   + STRING_COLON + dependency.getVersion() + STRING_COLON\n                   + dependency.getClassifier() + STRING_COLON + dependency.getType();\n        } else {\n            return dependency.getGroupId() + STRING_COLON + dependency.getArtifactId()\n                   + STRING_COLON + dependency.getVersion() + STRING_COLON + dependency.getType();\n        }\n    }\n\n    public static String getDependencyIdentityWithoutVersion(Dependency dependency) {\n        if (org.apache.commons.lang3.StringUtils.isNotEmpty(dependency.getClassifier())) {\n            return dependency.getGroupId() + STRING_COLON + dependency.getArtifactId()\n                   + STRING_COLON + dependency.getClassifier() + STRING_COLON\n                   + dependency.getType();\n        } else {\n            return dependency.getGroupId() + STRING_COLON + dependency.getArtifactId()\n                   + STRING_COLON + dependency.getType();\n        }\n    }\n\n    public static String getGAIdentity(Artifact artifact) {\n        return artifact.getGroupId() + STRING_COLON + artifact.getArtifactId();\n    }\n\n    public static String getGAIdentity(Parent parent) {\n        return parent.getGroupId() + STRING_COLON + parent.getArtifactId();\n    }\n\n    public static String getGAVIdentity(Parent parent) {\n        return parent.getGroupId() + STRING_COLON + parent.getArtifactId() + STRING_COLON\n               + parent.getVersion();\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-maven-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ModuleSlimConfig.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.boot.mojo;\n\nimport java.util.LinkedHashSet;\n\n/**\n * @author lianglipeng.llp@alibaba-inc.com\n * @version $Id: ModuleSlimConfig.java, v 0.1 2024年07月12日 16:28 立蓬 Exp $\n */\npublic class ModuleSlimConfig {\n\n    private String                packExcludesConfig;\n\n    private String                packExcludesUrl;\n\n    /**\n     * Colon separated groupId, artifactId [and classifier] to exclude (exact match). e.g:\n     * group-a:tracer-core:3.0.10\n     * group-b:tracer-core:3.0.10:jdk17\n     */\n    private LinkedHashSet<String> excludes                                          = new LinkedHashSet<>();\n\n    /**\n     * list of groupId names to exclude (exact match).\n     */\n    private LinkedHashSet<String> excludeGroupIds                                   = new LinkedHashSet<>();\n\n    /**\n     * list of artifact names to exclude (exact match).\n     */\n    private LinkedHashSet<String> excludeArtifactIds                                = new LinkedHashSet<>();\n\n    /**\n     * Colon separated groupId, artifactId [and classifier] to exclude (exact match). e.g:\n     * group-a:tracer-core:3.0.10\n     * group-b:tracer-core:3.0.10:jdk17\n     */\n    private LinkedHashSet<String> includes                                          = new LinkedHashSet<>();\n\n    /**\n     * list of groupId names to exclude (exact match).\n     */\n    private LinkedHashSet<String> includeGroupIds                                   = new LinkedHashSet<>();\n\n    /**\n     * list of artifact names to exclude (exact match).\n     */\n    private LinkedHashSet<String> includeArtifactIds                                = new LinkedHashSet<>();\n\n    /**\n     * 基座依赖标识，以 ${groupId}:${artifactId}:${version} 标识\n     */\n    private String                baseDependencyParentIdentity;\n\n    /**\n     * 在排除依赖时，是否同时排除依赖及间接依赖。如：A依赖B，B依赖C，当 excludes 只配置了 A 时，B 和 C 都会被排除\n     */\n    private boolean               excludeWithIndirectDependencies                   = true;\n\n    /**\n     * 在排除依赖时，如果排除的依赖与基座不一致，是否构建失败\n     */\n    private boolean               buildFailWhenExcludeBaseDependencyWithDiffVersion = false;\n\n    public LinkedHashSet<String> getExcludeArtifactIds() {\n        return excludeArtifactIds;\n    }\n\n    public LinkedHashSet<String> getExcludeGroupIds() {\n        return excludeGroupIds;\n    }\n\n    public LinkedHashSet<String> getExcludes() {\n        return excludes;\n    }\n\n    public String getPackExcludesConfig() {\n        return packExcludesConfig;\n    }\n\n    public ModuleSlimConfig setPackExcludesConfig(String packExcludesConfig) {\n        this.packExcludesConfig = packExcludesConfig;\n        return this;\n    }\n\n    public String getPackExcludesUrl() {\n        return packExcludesUrl;\n    }\n\n    public ModuleSlimConfig setPackExcludesUrl(String packExcludesUrl) {\n        this.packExcludesUrl = packExcludesUrl;\n        return this;\n    }\n\n    public String getBaseDependencyParentIdentity() {\n        return baseDependencyParentIdentity;\n    }\n\n    public ModuleSlimConfig setBaseDependencyParentIdentity(String baseDependencyParentIdentity) {\n        this.baseDependencyParentIdentity = baseDependencyParentIdentity;\n        return this;\n    }\n\n    public ModuleSlimConfig setExcludes(LinkedHashSet<String> excludes) {\n        this.excludes = excludes;\n        return this;\n    }\n\n    public ModuleSlimConfig setExcludeGroupIds(LinkedHashSet<String> excludeGroupIds) {\n        this.excludeGroupIds = excludeGroupIds;\n        return this;\n    }\n\n    public ModuleSlimConfig setExcludeArtifactIds(LinkedHashSet<String> excludeArtifactIds) {\n        this.excludeArtifactIds = excludeArtifactIds;\n        return this;\n    }\n\n    public LinkedHashSet<String> getIncludes() {\n        return includes;\n    }\n\n    public ModuleSlimConfig setIncludes(LinkedHashSet<String> includes) {\n        this.includes = includes;\n        return this;\n    }\n\n    public LinkedHashSet<String> getIncludeGroupIds() {\n        return includeGroupIds;\n    }\n\n    public ModuleSlimConfig setIncludeGroupIds(LinkedHashSet<String> includeGroupIds) {\n        this.includeGroupIds = includeGroupIds;\n        return this;\n    }\n\n    public LinkedHashSet<String> getIncludeArtifactIds() {\n        return includeArtifactIds;\n    }\n\n    public ModuleSlimConfig setIncludeArtifactIds(LinkedHashSet<String> includeArtifactIds) {\n        this.includeArtifactIds = includeArtifactIds;\n        return this;\n    }\n\n    public boolean isExcludeWithIndirectDependencies() {\n        return excludeWithIndirectDependencies;\n    }\n\n    public void setExcludeWithIndirectDependencies(boolean excludeWithIndirectDependencies) {\n        this.excludeWithIndirectDependencies = excludeWithIndirectDependencies;\n    }\n\n    public boolean isBuildFailWhenExcludeBaseDependencyWithDiffVersion() {\n        return buildFailWhenExcludeBaseDependencyWithDiffVersion;\n    }\n\n    public void setBuildFailWhenExcludeBaseDependencyWithDiffVersion(boolean buildFailWhenExcludeBaseDependencyWithDiffVersion) {\n        this.buildFailWhenExcludeBaseDependencyWithDiffVersion = buildFailWhenExcludeBaseDependencyWithDiffVersion;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-maven-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ModuleSlimExecutor.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.boot.mojo;\n\nimport com.alipay.sofa.ark.boot.mojo.model.ArkConfigHolder;\nimport com.alipay.sofa.ark.common.util.ParseUtils;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.tools.ArtifactItem;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.http.client.methods.CloseableHttpResponse;\nimport org.apache.http.client.methods.HttpGet;\nimport org.apache.http.impl.client.CloseableHttpClient;\nimport org.apache.http.impl.client.HttpClients;\nimport org.apache.http.util.EntityUtils;\nimport org.apache.maven.artifact.Artifact;\nimport org.apache.maven.model.Dependency;\nimport org.apache.maven.model.DependencyManagement;\nimport org.apache.maven.model.Model;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugin.logging.Log;\nimport org.apache.maven.project.MavenProject;\nimport org.apache.maven.project.ProjectBuilder;\nimport org.apache.maven.project.ProjectBuildingException;\nimport org.apache.maven.repository.RepositorySystem;\nimport org.apache.maven.shared.dependency.graph.DependencyNode;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport static com.alipay.sofa.ark.boot.mojo.MavenUtils.getArtifactIdentityWithoutVersion;\nimport static com.alipay.sofa.ark.boot.mojo.MavenUtils.inUnLogScopes;\nimport static com.alipay.sofa.ark.boot.mojo.utils.ParseUtils.getBooleanWithDefault;\nimport static com.alipay.sofa.ark.boot.mojo.utils.ParseUtils.getStringSet;\nimport static com.alipay.sofa.ark.spi.constant.Constants.ARK_CONF_BASE_DIR;\nimport static com.alipay.sofa.ark.spi.constant.Constants.EXTENSION_EXCLUDES;\nimport static com.alipay.sofa.ark.spi.constant.Constants.EXTENSION_EXCLUDES_ARTIFACTIDS;\nimport static com.alipay.sofa.ark.spi.constant.Constants.EXTENSION_EXCLUDES_GROUPIDS;\nimport static com.alipay.sofa.ark.spi.constant.Constants.EXTENSION_INCLUDES;\nimport static com.alipay.sofa.ark.spi.constant.Constants.EXTENSION_INCLUDES_ARTIFACTIDS;\nimport static com.alipay.sofa.ark.spi.constant.Constants.EXTENSION_INCLUDES_GROUPIDS;\n\n/**\n * @author lianglipeng.llp@alibaba-inc.com\n * @version $Id: ModuleSlimStrategy.java, v 0.1 2024年07月12日 16:21 立蓬 Exp $\n */\n\npublic class ModuleSlimExecutor {\n    private MavenProject        project;\n\n    private RepositorySystem    repositorySystem;\n\n    private ProjectBuilder      projectBuilder;\n\n    private DependencyNode      projDependencyGraph;\n    private ModuleSlimConfig    config;\n\n    private Log                 log;\n\n    private File                baseDir;\n\n    private File                sofaArkLogDirectory;\n\n    private static final String EXTENSION_EXCLUDE_WITH_INDIRECT_DEPENDENCIES           = \"excludeWithIndirectDependencies\";\n\n    private static final String EXTENSION_BUILD_FAIL_WHEN_EXCLUDE_DIFF_BASE_DEPENDENCY = \"buildFailWhenExcludeDiffBaseDependency\";\n\n    private static final String DEFAULT_EXCLUDE_RULES                                  = \"rules.txt\";\n\n    ModuleSlimExecutor(MavenProject project, RepositorySystem repositorySystem,\n                       ProjectBuilder projectBuilder, DependencyNode projDependencyGraph,\n                       ModuleSlimConfig config, File baseDir, File sofaArkLogDirectory, Log log) {\n        this.project = project;\n        this.repositorySystem = repositorySystem;\n        this.projectBuilder = projectBuilder;\n        this.projDependencyGraph = projDependencyGraph;\n        this.config = config;\n        this.baseDir = baseDir;\n        this.sofaArkLogDirectory = sofaArkLogDirectory;\n        this.log = log;\n    }\n\n    public Set<Artifact> getSlimmedArtifacts() throws MojoExecutionException, IOException {\n        initSlimStrategyConfig();\n\n        Set<Artifact> toFilterByBase = getArtifactsToFilterByParentIdentity(project.getArtifacts());\n        Set<Artifact> toFilterByBasePlugin = getArtifactsToFilterByBasePlugin(project\n            .getArtifacts());\n        Set<Artifact> toFilterByExclude = getArtifactsToFilterByExcludeConfig(project\n            .getArtifacts());\n        Set<Artifact> toAddByInclude = getArtifactsToAddByIncludeConfig(project.getArtifacts());\n\n        Set<Artifact> toFilter = new HashSet<>();\n        toFilter.addAll(toFilterByBase);\n        toFilter.addAll(toFilterByBasePlugin);\n        toFilter.addAll(toFilterByExclude);\n        toFilter.removeAll(toAddByInclude);\n\n        checkExcludeByParentIdentity(toFilter);\n\n        Set<Artifact> filteredArtifacts = new HashSet<>(project.getArtifacts());\n        \n        Set<String> excludedArtifacts = new LinkedHashSet<>();\n        filteredArtifacts.stream().filter(toFilter::contains).forEach(\n                artifact -> {\n                    excludedArtifacts.add(getArtifactIdentityWithoutVersion(artifact));\n                }\n        );\n        filteredArtifacts.removeAll(toFilter);\n        \n        Set<String> compiledArtifacts = new LinkedHashSet<>();\n        toAddByInclude.stream()\n                .filter(artifact -> Artifact.SCOPE_PROVIDED.equals(artifact.getScope()))\n                .forEach(artifact -> {\n                    compiledArtifacts.add(getArtifactIdentityWithoutVersion(artifact));\n                    artifact.setScope(Artifact.SCOPE_COMPILE);\n                });\n\n        saveModuleSlimResult(excludedArtifacts, compiledArtifacts);\n\n        return filteredArtifacts;\n    }\n\n    protected Model resolvePomAsOriginalModel(String groupId, String artifactId, String version) {\n        try {\n            Artifact artifact = repositorySystem\n                .createProjectArtifact(groupId, artifactId, version);\n            return projectBuilder.build(artifact, project.getProjectBuildingRequest()).getProject()\n                .getOriginalModel();\n        } catch (ProjectBuildingException e) {\n            log.warn(\"resolve pom as project error: with \" + groupId + \":\" + artifactId + \":\"\n                     + version);\n            return null;\n        }\n    }\n\n    protected Set<Artifact> getArtifactsToFilterByBasePlugin(Set<Artifact> artifacts) {\n        if (!excludeWithBaseDependencyParentIdentity()) {\n            return Collections.emptySet();\n        }\n\n        return getSameArtifactsInBasePlugin(artifacts);\n    }\n\n    private Set<Artifact> getSameArtifactsInBasePlugin(Set<Artifact> artifacts) {\n        List<Model> basePluginModels = getBasePluginModel();\n        List<Dependency> dependenciesInBasePlugin = basePluginModels.stream().flatMap(it->Optional.ofNullable(it.getDependencyManagement()).orElseGet(\n                DependencyManagement::new).getDependencies().stream()).collect(Collectors.toList());\n\n        // 如果 artifacts 中含有 base Plugin 里配置的 dependencyManagement 的 dependencies，那么需要过滤\n        Set<String> dependencyIdentities = dependenciesInBasePlugin.stream().map(MavenUtils::getDependencyIdentityWithoutVersion).collect(Collectors.toSet());\n        return artifacts.stream().filter(it -> dependencyIdentities.contains(getArtifactIdentityWithoutVersion(it))).collect(Collectors.toSet());\n    }\n\n    protected List<Model> getBasePluginModel(){\n        // 先通过 project.getOriginalModel().getDependencyManagement().getDependencies() 获取所有 type 为 pom 的 dependencies\n        List<Dependency> pomDependenciesInDependencyManagement = Optional.ofNullable(project)\n                .map(MavenProject::getOriginalModel)\n                .map(Model::getDependencyManagement)\n                .map(DependencyManagement::getDependencies)\n                .orElse(Collections.emptyList())\n                .stream()\n                .filter(it -> StringUtils.equals(\"pom\",it.getType()) && StringUtils.equals(\"import\",it.getScope()))\n                .collect(Collectors.toList());\n\n        // 然后解析这些 dependencies 为 artifacts，并转为 mavenProject, 获取其 model\n        List<Model> basePluginModels = new ArrayList<>();\n        for (Dependency dependency : pomDependenciesInDependencyManagement) {\n            // 解析 dependency 为 model\n            Model model = resolvePomAsOriginalModel(dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion());\n\n            // 如果 model 的 parent 和我们的 baseVersion 一致，那么这个 model 就是 basePlugin\n            if (null != model && null != model.getParent() &&\n                    (StringUtils.equals(config.getBaseDependencyParentIdentity(),MavenUtils.getGAIdentity(model.getParent())) || StringUtils.equals(config.getBaseDependencyParentIdentity(),MavenUtils.getGAVIdentity(model.getParent())))\n            ){\n                basePluginModels.add(model);\n            }\n        }\n        return basePluginModels;\n    }\n\n    protected void checkExcludeByParentIdentity(Set<Artifact> toFilter) throws MojoExecutionException {\n        if (StringUtils.isEmpty(config.getBaseDependencyParentIdentity())) {\n            return;\n        }\n\n        Set<Artifact> excludedButNoDependencyInBase = getExcludedButNoDependencyInBase(toFilter);\n        Set<Artifact> excludedButDifferentVersionDependencyInBase = getExcludedButDifferentVersionDependencyInBase(toFilter);\n\n        if(excludedButNoDependencyInBase.isEmpty() && excludedButDifferentVersionDependencyInBase.isEmpty()){\n            getLog().info(String.format(\"check excludeWithBaseDependencyParentIdentity success with base: %s\",config.getBaseDependencyParentIdentity()));\n            return;\n        }\n\n        // Dependency not found in base; please add it to the base or do not exclude it in the module\n        excludedButNoDependencyInBase.forEach(artifact -> {\n            getLog().error(\n                    String.format(\n                            \"error to exclude package jar: %s because no such jar in base, please keep the jar or add it to base\",\n                            MavenUtils.getArtifactIdentity(artifact)));\n        });\n\n        // The base contains this dependency, but the version and module are inconsistent; Please use the same dependency version as the base in the module.\n        List<Dependency> baseDependencies = getAllBaseDependencies();\n        Map<String,Dependency> baseDependencyIdentityWithoutVersion = baseDependencies.stream().collect(Collectors.toMap(MavenUtils::getDependencyIdentityWithoutVersion, it -> it));\n        excludedButDifferentVersionDependencyInBase.forEach(artifact -> {\n            Dependency baseDependency = baseDependencyIdentityWithoutVersion.get(getArtifactIdentityWithoutVersion(artifact));\n            getLog().error(\n                    String.format(\n                            \"error to exclude package jar: %s because it has different version with: %s in base, please keep the jar or set same version with base\",\n                            MavenUtils.getArtifactIdentity(artifact), MavenUtils.getDependencyIdentity(baseDependency)));\n        });\n\n        if(config.isBuildFailWhenExcludeBaseDependencyWithDiffVersion()){\n            throw new MojoExecutionException(String.format(\"check excludeWithBaseDependencyParentIdentity failed with base: %s\",config.getBaseDependencyParentIdentity()));\n        }\n    }\n\n    protected Set<Artifact> getArtifactsToFilterByParentIdentity(Set<Artifact> artifacts)\n                                                                                         throws MojoExecutionException {\n        if (!excludeWithBaseDependencyParentIdentity()) {\n            return Collections.emptySet();\n        }\n\n        // 过滤出模块和基座版本一致的依赖，即需要瘦身的依赖\n        return getSameArtifactsWithBase(artifacts);\n    }\n\n    private boolean excludeWithBaseDependencyParentIdentity() {\n        return StringUtils.isNotEmpty(config.getBaseDependencyParentIdentity());\n    }\n\n    private Set<Artifact> getSameArtifactsWithBase(Set<Artifact> artifacts) throws MojoExecutionException {\n        List<Dependency> baseDependencies = getAllBaseDependencies();\n\n        Set<String> dependencyIdentities = baseDependencies.stream().map(MavenUtils::getDependencyIdentityWithoutVersion).collect(Collectors.toSet());\n\n        return artifacts.stream().filter(it -> dependencyIdentities.contains(getArtifactIdentityWithoutVersion(it))).collect(Collectors.toSet());\n    }\n\n    private Set<Artifact> getExcludedButNoDependencyInBase(Set<Artifact> toFilter) throws MojoExecutionException {\n        List<Dependency> baseDependencies = getAllBaseDependencies();\n\n        Map<String,Dependency> baseDependencyIdentityWithoutVersion = baseDependencies.stream().collect(Collectors.toMap(MavenUtils::getDependencyIdentityWithoutVersion, it -> it));\n\n        return toFilter.stream().filter(it -> !baseDependencyIdentityWithoutVersion.containsKey(getArtifactIdentityWithoutVersion(it))).collect(Collectors.toSet());\n    }\n\n    private Set<Artifact> getExcludedButDifferentVersionDependencyInBase(Set<Artifact> toFilter) throws MojoExecutionException {\n        List<Dependency> baseDependencies = getAllBaseDependencies();\n        Map<String,Dependency> baseDependencyIdentityWithoutVersion = baseDependencies.stream().collect(Collectors.toMap(MavenUtils::getDependencyIdentityWithoutVersion, it -> it));\n        return toFilter.stream().filter(artifact ->\n                {\n                    String identityWithoutVersion = getArtifactIdentityWithoutVersion(artifact);\n                    return baseDependencyIdentityWithoutVersion.containsKey(identityWithoutVersion)\n                            && (!artifact.getBaseVersion().equals(baseDependencyIdentityWithoutVersion.get(identityWithoutVersion).getVersion()));\n                }\n        ).collect(Collectors.toSet());\n    }\n\n    private List<Dependency> getAllBaseDependencies() throws MojoExecutionException {\n        // 获取基座DependencyParent的原始Model\n        Model baseDependencyPom = getBaseDependencyParentOriginalModel();\n        if (null == baseDependencyPom) {\n            throw new MojoExecutionException(\n                String.format(\"can not find base dependency parent: %s\",\n                    config.getBaseDependencyParentIdentity()));\n        }\n\n        if (null == baseDependencyPom.getDependencyManagement()) {\n            return Collections.emptyList();\n        }\n\n        return baseDependencyPom.getDependencyManagement().getDependencies();\n    }\n\n    protected Model getBaseDependencyParentOriginalModel() {\n        MavenProject proj = project;\n        while (null != proj) {\n            if (MavenUtils.getGAIdentity(proj.getArtifact()).equals(\n                config.getBaseDependencyParentIdentity())\n                || MavenUtils.getGAVIdentity(proj.getArtifact()).equals(\n                    config.getBaseDependencyParentIdentity())) {\n                return proj.getOriginalModel();\n            }\n            proj = proj.getParent();\n        }\n        return null;\n    }\n\n    protected void initSlimStrategyConfig() throws IOException {\n        Map<String, Object> arkYaml = ArkConfigHolder.getArkYaml(baseDir.getAbsolutePath());\n        Properties prop = ArkConfigHolder.getArkProperties(baseDir.getAbsolutePath());\n\n        config.setExcludeWithIndirectDependencies(getBooleanWithDefault(prop, arkYaml,\n            EXTENSION_EXCLUDE_WITH_INDIRECT_DEPENDENCIES, true));\n\n        config.setBuildFailWhenExcludeBaseDependencyWithDiffVersion(getBooleanWithDefault(prop,\n            arkYaml, EXTENSION_BUILD_FAIL_WHEN_EXCLUDE_DIFF_BASE_DEPENDENCY, false));\n\n        initExcludeAndIncludeConfig();\n    }\n\n    /**\n     * exclude and include config comes from 3 parts:\n     * 1. extension from config file that configured by user in sofa-ark-maven-plugin\n     * 2. extension from default bootstrap.properties or bootstrap.yml\n     * 3. extension from url\n     * @throws IOException\n     */\n    protected void initExcludeAndIncludeConfig() throws IOException {\n        // extension from other resource\n        if (!StringUtils.isEmpty(config.getPackExcludesConfig())) {\n            extensionExcludeAndIncludeArtifacts(baseDir + File.separator + ARK_CONF_BASE_DIR\n                                                + File.separator + config.getPackExcludesConfig());\n        } else {\n            extensionExcludeAndIncludeArtifacts(baseDir + File.separator + ARK_CONF_BASE_DIR\n                                                + File.separator + DEFAULT_EXCLUDE_RULES);\n        }\n\n        // extension from default bootstrap.properties or bootstrap.yml\n        configExcludeArtifactsByDefault();\n\n        // extension from url\n        if (StringUtils.isNotBlank(config.getPackExcludesUrl())) {\n            extensionExcludeArtifactsFromUrl(config.getPackExcludesUrl(), project.getArtifacts());\n        }\n    }\n\n    protected Set<Artifact> getArtifactsToFilterByExcludeConfig(Set<Artifact> artifacts) {\n        Set<Artifact> literalArtifactsToExclude = getLiteralArtifactsToFilterByExcludeConfig(artifacts);\n        if (config.isExcludeWithIndirectDependencies()) {\n            return excludeWithIndirectDependencies(literalArtifactsToExclude, artifacts);\n        }\n        return literalArtifactsToExclude;\n    }\n\n    private Set<Artifact> excludeWithIndirectDependencies(Set<Artifact> literalArtifactsToExclude, Set<Artifact> artifacts) {\n        Set<String> excludeArtifactIdentities = literalArtifactsToExclude.stream().map(MavenUtils::getArtifactIdentity).collect(Collectors.toSet());\n        Map<String,Artifact> artifactMap = artifacts.stream().collect(Collectors.toMap(MavenUtils::getArtifactIdentity,it->it));\n        return getExcludeWithIndirectDependencies(projDependencyGraph,excludeArtifactIdentities,artifactMap);\n    }\n\n    private Set<Artifact> getExcludeWithIndirectDependencies(DependencyNode node,\n                                                             Set<String> literalArtifactsToExclude,\n                                                             Map<String, Artifact> artifacts) {\n        if (null == node) {\n            return Collections.emptySet();\n        }\n\n        Set<Artifact> result = new LinkedHashSet<>();\n\n        String artifactIdentity = MavenUtils.getArtifactIdentity(node.getArtifact());\n        if (literalArtifactsToExclude.contains(artifactIdentity)) {\n            // 排除当前依赖\n            result.add(artifacts.get(artifactIdentity));\n\n            // 排除当前依赖的所有依赖\n            result.addAll(getAllDependencies(node, artifacts));\n            return result;\n        }\n\n        for (DependencyNode child : node.getChildren()) {\n            result.addAll(getExcludeWithIndirectDependencies(child, literalArtifactsToExclude,\n                artifacts));\n        }\n\n        return result;\n    }\n\n    private Set<Artifact> getAllDependencies(DependencyNode node, Map<String, Artifact> artifacts) {\n        if (null == node) {\n            return Collections.emptySet();\n        }\n\n        Set<Artifact> result = new HashSet<>();\n        for (DependencyNode child : node.getChildren()) {\n            String artifactId = MavenUtils.getArtifactIdentity(child.getArtifact());\n            if (artifacts.containsKey(artifactId)) {\n                result.add(artifacts.get(artifactId));\n                result.addAll(getAllDependencies(child, artifacts));\n            }\n        }\n        return result;\n    }\n\n    protected Set<Artifact> getLiteralArtifactsToFilterByExcludeConfig(Set<Artifact> artifacts) {\n        List<ArtifactItem> excludeList = new ArrayList<>();\n        if (config != null\n            && (config.getExcludes().contains(\"*\") || config.getExcludes().contains(\".*\"))) {\n            return artifacts;\n        }\n        for (String exclude : config.getExcludes()) {\n            ArtifactItem item = ArtifactItem.parseArtifactItem(exclude);\n            excludeList.add(item);\n        }\n\n        Set<Artifact> result = new LinkedHashSet<>();\n        for (Artifact e : artifacts) {\n            if (checkMatchExclude(excludeList, e)) {\n                result.add(e);\n            }\n        }\n\n        return result;\n    }\n\n    protected Set<Artifact> getArtifactsToAddByIncludeConfig(Set<Artifact> artifacts) {\n        List<ArtifactItem> includeList = new ArrayList<>();\n        if (config != null\n            && (config.getIncludes().contains(\"*\") || config.getIncludes().contains(\".*\"))) {\n            return artifacts;\n        }\n        for (String include : config.getIncludes()) {\n            ArtifactItem item = ArtifactItem.parseArtifactItem(include);\n            includeList.add(item);\n        }\n\n        Set<Artifact> result = new LinkedHashSet<>();\n        for (Artifact e : artifacts) {\n            if (checkMatchInclude(includeList, e)) {\n                result.add(e);\n            }\n        }\n\n        return result;\n    }\n\n    /**\n     * This method is core method for excluding artifacts in sofa-ark-maven-plugin &lt;excludeGroupIds&gt;\n     * and &lt;excludeArtifactIds&gt; config.\n     *\n     * @param excludeList\n     * @param artifact\n     * @return\n     */\n    private boolean checkMatchExclude(List<ArtifactItem> excludeList, Artifact artifact) {\n        for (ArtifactItem exclude : excludeList) {\n            if (exclude.isSameWithVersion(ArtifactItem.parseArtifactItem(artifact))) {\n                return true;\n            }\n        }\n\n        if (checkMatchGroupId(config.getExcludeGroupIds(), artifact)) {\n            return true;\n        }\n\n        return checkMatchArtifactId(config.getExcludeArtifactIds(), artifact);\n    }\n\n    /**\n     * This method is core method for including artifacts in sofa-ark-maven-plugin &lt;includeGroupIds&gt;\n     * and &lt;includeArtifactIds&gt; config.\n     *\n     * @param includeList\n     * @param artifact\n     * @return\n     */\n    private boolean checkMatchInclude(List<ArtifactItem> includeList, Artifact artifact) {\n        for (ArtifactItem include : includeList) {\n            if (include.isSameWithVersion(ArtifactItem.parseArtifactItem(artifact))) {\n                return true;\n            }\n        }\n\n        if (checkMatchGroupId(config.getIncludeGroupIds(), artifact)) {\n            return true;\n        }\n\n        return checkMatchArtifactId(config.getIncludeArtifactIds(), artifact);\n    }\n\n    private boolean checkMatchGroupId(Set<String> groupIds, Artifact artifact) {\n        if (groupIds != null) {\n            // 支持通配符\n            for (String groupId : groupIds) {\n                if (groupId.endsWith(Constants.PACKAGE_PREFIX_MARK)\n                    || groupId.endsWith(Constants.PACKAGE_PREFIX_MARK_2)) {\n                    if (groupId.endsWith(Constants.PACKAGE_PREFIX_MARK_2)) {\n                        groupId = StringUtils.removeEnd(groupId, Constants.PACKAGE_PREFIX_MARK_2);\n                    } else if (groupId.endsWith(Constants.PACKAGE_PREFIX_MARK)) {\n                        groupId = StringUtils.removeEnd(groupId, Constants.PACKAGE_PREFIX_MARK);\n                    }\n\n                    if (artifact.getGroupId().startsWith(groupId)) {\n                        return true;\n                    }\n                } else {\n                    if (artifact.getGroupId().equals(groupId)) {\n                        return true;\n                    }\n                }\n            }\n        }\n        return false;\n    }\n\n    private boolean checkMatchArtifactId(Set<String> artifactIds, Artifact artifact) {\n        if (artifactIds != null) {\n            // 支持通配符\n            for (String artifactId : artifactIds) {\n                if (artifactId.endsWith(Constants.PACKAGE_PREFIX_MARK)\n                    || artifactId.endsWith(Constants.PACKAGE_PREFIX_MARK_2)) {\n                    if (artifactId.endsWith(Constants.PACKAGE_PREFIX_MARK_2)) {\n                        artifactId = StringUtils.removeEnd(artifactId,\n                            Constants.PACKAGE_PREFIX_MARK_2);\n                    } else if (artifactId.endsWith(Constants.PACKAGE_PREFIX_MARK)) {\n                        artifactId = StringUtils.removeEnd(artifactId,\n                            Constants.PACKAGE_PREFIX_MARK);\n                    }\n                    if (artifact.getArtifactId().startsWith(artifactId)) {\n                        return true;\n                    }\n                } else {\n                    if (artifact.getArtifactId().equals(artifactId)) {\n                        return true;\n                    }\n                }\n            }\n        }\n        return false;\n    }\n\n    protected void extensionExcludeAndIncludeArtifacts(String extraResources) {\n        try {\n            File configFile = com.alipay.sofa.ark.common.util.FileUtils.file(extraResources);\n            if (configFile.exists()) {\n                BufferedReader bufferedReader = new BufferedReader(new FileReader(configFile));\n                String dataLine;\n                while ((dataLine = bufferedReader.readLine()) != null) {\n                    if (dataLine.startsWith(EXTENSION_EXCLUDES)) {\n                        ParseUtils.parseExcludeConf(config.getExcludes(), dataLine,\n                            EXTENSION_EXCLUDES);\n                    } else if (dataLine.startsWith(EXTENSION_EXCLUDES_GROUPIDS)) {\n                        ParseUtils.parseExcludeConf(config.getExcludeGroupIds(), dataLine,\n                            EXTENSION_EXCLUDES_GROUPIDS);\n                    } else if (dataLine.startsWith(EXTENSION_EXCLUDES_ARTIFACTIDS)) {\n                        ParseUtils.parseExcludeConf(config.getExcludeArtifactIds(), dataLine,\n                            EXTENSION_EXCLUDES_ARTIFACTIDS);\n                    } else if (dataLine.startsWith(EXTENSION_INCLUDES)) {\n                        ParseUtils.parseExcludeConf(config.getIncludes(), dataLine,\n                            EXTENSION_INCLUDES);\n                    } else if (dataLine.startsWith(EXTENSION_INCLUDES_GROUPIDS)) {\n                        ParseUtils.parseExcludeConf(config.getIncludeGroupIds(), dataLine,\n                            EXTENSION_INCLUDES_GROUPIDS);\n                    } else if (dataLine.startsWith(EXTENSION_INCLUDES_ARTIFACTIDS)) {\n                        ParseUtils.parseExcludeConf(config.getIncludeArtifactIds(), dataLine,\n                            EXTENSION_INCLUDES_ARTIFACTIDS);\n                    }\n                }\n            }\n        } catch (IOException ex) {\n            getLog().error(\"failed to extension excludes artifacts.\", ex);\n        }\n    }\n\n    protected void configExcludeArtifactsByDefault() throws IOException {\n        // extension from default ark.properties and ark.yml\n        Map<String, Object> arkYaml = ArkConfigHolder.getArkYaml(baseDir.getAbsolutePath());\n        Properties prop = ArkConfigHolder.getArkProperties(baseDir.getAbsolutePath());\n\n        config.getExcludes().addAll(getStringSet(prop, EXTENSION_EXCLUDES));\n        config.getExcludeGroupIds().addAll(getStringSet(prop, EXTENSION_EXCLUDES_GROUPIDS));\n        config.getExcludeArtifactIds().addAll(getStringSet(prop, EXTENSION_EXCLUDES_ARTIFACTIDS));\n        config.getIncludes().addAll(getStringSet(prop, EXTENSION_INCLUDES));\n        config.getIncludeGroupIds().addAll(getStringSet(prop, EXTENSION_INCLUDES_GROUPIDS));\n        config.getIncludeArtifactIds().addAll(getStringSet(prop, EXTENSION_INCLUDES_ARTIFACTIDS));\n\n        config.getExcludes().addAll(getStringSet(arkYaml, EXTENSION_EXCLUDES));\n        config.getExcludeGroupIds().addAll(getStringSet(arkYaml, EXTENSION_EXCLUDES_GROUPIDS));\n        config.getExcludeArtifactIds()\n            .addAll(getStringSet(arkYaml, EXTENSION_EXCLUDES_ARTIFACTIDS));\n        config.getIncludes().addAll(getStringSet(arkYaml, EXTENSION_INCLUDES));\n        config.getIncludeGroupIds().addAll(getStringSet(arkYaml, EXTENSION_INCLUDES_GROUPIDS));\n        config.getIncludeArtifactIds()\n            .addAll(getStringSet(arkYaml, EXTENSION_INCLUDES_ARTIFACTIDS));\n    }\n\n    protected void extensionExcludeArtifactsFromUrl(String packExcludesUrl, Set<Artifact> artifacts) {\n        try {\n            CloseableHttpClient client = HttpClients.createDefault();\n            HttpGet request = new HttpGet(packExcludesUrl);\n            CloseableHttpResponse response = client.execute(request);\n            int statusCode = response.getStatusLine().getStatusCode();\n            if (statusCode == 200 && response.getEntity() != null) {\n                String result = EntityUtils.toString(response.getEntity());\n                getLog().info(\n                    String.format(\"success to get excludes config from url: %s, response: %s\",\n                        packExcludesUrl, result));\n                ObjectMapper objectMapper = new ObjectMapper();\n                ExcludeConfigResponse excludeConfigResponse = objectMapper.readValue(result,\n                    ExcludeConfigResponse.class);\n                if (excludeConfigResponse.isSuccess() && excludeConfigResponse.getResult() != null) {\n                    ExcludeConfig excludeConfig = excludeConfigResponse.getResult();\n                    List<String> jarBlackGroupIds = excludeConfig.getJarBlackGroupIds();\n                    List<String> jarBlackArtifactIds = excludeConfig.getJarBlackArtifactIds();\n                    List<String> jarBlackList = excludeConfig.getJarBlackList();\n                    List<String> jarWhiteGroupIds = excludeConfig.getJarWhiteGroupIds();\n                    List<String> jarWhiteArtifactIds = excludeConfig.getJarWhiteArtifactIds();\n                    List<String> jarWhiteList = excludeConfig.getJarWhiteList();\n                    if (CollectionUtils.isNotEmpty(jarBlackGroupIds)) {\n                        config.getExcludeGroupIds().addAll(jarBlackGroupIds);\n                    }\n                    if (CollectionUtils.isNotEmpty(jarBlackArtifactIds)) {\n                        config.getExcludeArtifactIds().addAll(jarBlackArtifactIds);\n                    }\n                    if (CollectionUtils.isNotEmpty(jarBlackList)) {\n                        config.getExcludes().addAll(jarBlackList);\n                    }\n                    if (CollectionUtils.isNotEmpty(jarWhiteGroupIds)) {\n                        config.getIncludeGroupIds().addAll(jarWhiteGroupIds);\n                    }\n                    if (CollectionUtils.isNotEmpty(jarWhiteArtifactIds)) {\n                        config.getIncludeArtifactIds().addAll(jarWhiteArtifactIds);\n                    }\n                    if (CollectionUtils.isNotEmpty(jarWhiteList)) {\n                        config.getIncludes().addAll(jarWhiteList);\n                    }\n                    logExcludeMessage(jarBlackGroupIds, jarBlackArtifactIds, jarBlackList,\n                        artifacts, true);\n\n                    List<String> jarWarnGroupIds = excludeConfig.getJarWarnGroupIds();\n                    List<String> jarWarnArtifactIds = excludeConfig.getJarWarnArtifactIds();\n                    List<String> jarWarnList = excludeConfig.getJarWarnList();\n                    logExcludeMessage(jarWarnGroupIds, jarWarnArtifactIds, jarWarnList, artifacts,\n                        false);\n                }\n            }\n            response.close();\n            client.close();\n        } catch (Exception e) {\n            getLog().error(\n                String.format(\"failed to get excludes config from url: %s\", packExcludesUrl), e);\n        }\n    }\n\n    protected void logExcludeMessage(List<String> jarGroupIds, List<String> jarArtifactIds,\n                                     List<String> jarList, Set<Artifact> artifacts, boolean error) {\n        if (CollectionUtils.isNotEmpty(jarGroupIds)) {\n            for (Artifact artifact : artifacts) {\n                if (inUnLogScopes(artifact.getScope())) {\n                    continue;\n                }\n                for (String jarBlackGroupId : jarGroupIds) {\n                    if (jarBlackGroupId.endsWith(Constants.PACKAGE_PREFIX_MARK)\n                        || jarBlackGroupId.endsWith(Constants.PACKAGE_PREFIX_MARK_2)) {\n                        if (jarBlackGroupId.endsWith(Constants.PACKAGE_PREFIX_MARK_2)) {\n                            jarBlackGroupId = StringUtils.remove(jarBlackGroupId,\n                                Constants.PACKAGE_PREFIX_MARK_2);\n                        } else if (jarBlackGroupId.endsWith(Constants.PACKAGE_PREFIX_MARK)) {\n                            jarBlackGroupId = StringUtils.removeEnd(jarBlackGroupId,\n                                Constants.PACKAGE_PREFIX_MARK);\n                        }\n\n                        if (artifact.getGroupId().startsWith(jarBlackGroupId)) {\n                            if (error) {\n                                getLog()\n                                    .error(\n                                        String\n                                            .format(\n                                                \"Error to package jar: %s due to match groupId: %s, automatically exclude it.\",\n                                                artifact, jarBlackGroupId));\n                            } else {\n                                getLog().warn(\n                                    String.format(\n                                        \"Warn to package jar: %s due to match groupId: %s\",\n                                        artifact, jarBlackGroupId));\n                            }\n\n                        }\n                    } else {\n                        if (artifact.getGroupId().equals(jarBlackGroupId)) {\n                            if (error) {\n                                getLog()\n                                    .error(\n                                        String\n                                            .format(\n                                                \"Error to package jar: %s due to match groupId: %s, automatically exclude it.\",\n                                                artifact, jarBlackGroupId));\n                            } else {\n                                getLog().warn(\n                                    String.format(\n                                        \"Warn to package jar: %s due to match groupId: %s\",\n                                        artifact, jarBlackGroupId));\n                            }\n                        }\n                    }\n                }\n            }\n        }\n        if (CollectionUtils.isNotEmpty(jarArtifactIds)) {\n            for (Artifact artifact : artifacts) {\n                if (inUnLogScopes(artifact.getScope())) {\n                    continue;\n                }\n                for (String jarBlackArtifactId : jarArtifactIds) {\n                    if (jarBlackArtifactId.endsWith(Constants.PACKAGE_PREFIX_MARK)\n                        || jarBlackArtifactId.endsWith(Constants.PACKAGE_PREFIX_MARK_2)) {\n                        if (jarBlackArtifactId.endsWith(Constants.PACKAGE_PREFIX_MARK_2)) {\n                            jarBlackArtifactId = StringUtils.removeEnd(jarBlackArtifactId,\n                                Constants.PACKAGE_PREFIX_MARK_2);\n                        } else if (jarBlackArtifactId.endsWith(Constants.PACKAGE_PREFIX_MARK)) {\n                            jarBlackArtifactId = StringUtils.removeEnd(jarBlackArtifactId,\n                                Constants.PACKAGE_PREFIX_MARK);\n                        }\n                        if (artifact.getArtifactId().startsWith(jarBlackArtifactId)) {\n                            if (error) {\n                                getLog()\n                                    .error(\n                                        String\n                                            .format(\n                                                \"Error to package jar: %s due to match artifactId: %s, automatically exclude it.\",\n                                                artifact, jarBlackArtifactId));\n                            } else {\n                                getLog().warn(\n                                    String.format(\n                                        \"Warn to package jar: %s due to match artifactId: %s\",\n                                        artifact, jarBlackArtifactId));\n                            }\n                        }\n                    } else {\n                        if (artifact.getArtifactId().equals(jarBlackArtifactId)) {\n                            if (error) {\n                                getLog()\n                                    .error(\n                                        String\n                                            .format(\n                                                \"Error to package jar: %s due to match artifactId: %s, automatically exclude it.\",\n                                                artifact, jarBlackArtifactId));\n                            } else {\n                                getLog().warn(\n                                    String.format(\n                                        \"Warn to package jar: %s due to match artifactId: %s\",\n                                        artifact, jarBlackArtifactId));\n                            }\n                        }\n                    }\n                }\n            }\n        }\n        if (CollectionUtils.isNotEmpty(jarList)) {\n            for (Artifact artifact : artifacts) {\n                if (inUnLogScopes(artifact.getScope())) {\n                    continue;\n                }\n                for (String jarBlack : jarList) {\n                    if (jarBlack.equals(String.join(\":\", artifact.getGroupId(),\n                        artifact.getArtifactId(), artifact.getVersion()))) {\n                        if (error) {\n                            getLog()\n                                .error(\n                                    String\n                                        .format(\n                                            \"Error to package jar: %s due to match groupId:artifactId:version: %s, automatically exclude it.\",\n                                            artifact, jarBlack));\n                        } else {\n                            getLog()\n                                .warn(\n                                    String\n                                        .format(\n                                            \"Warn to package jar: %s due to match groupId:artifactId:version: %s\",\n                                            artifact, jarBlack));\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    private Log getLog() {\n        return log;\n    }\n\n    private void saveModuleSlimResult(Set<String> excludedArtifacts, Set<String> compiledArtifacts) {\n        if (sofaArkLogDirectory == null) {\n            log.warn(\"outputDirectory is null, skip saving module slim result\");\n            return;\n        }\n\n        try {\n\n            ModuleSlimResult result = new ModuleSlimResult();\n            result.setExcluded(new ArrayList<>(excludedArtifacts));\n            result.setCompiled(new ArrayList<>(compiledArtifacts));\n\n            File resultFile = new File(sofaArkLogDirectory, \"module-slim-results.json\");\n            ObjectMapper objectMapper = new ObjectMapper();\n            objectMapper.writeValue(resultFile, result);\n\n        } catch (Exception e) {\n            log.error(\"Failed to save module slim result: \" + e.getMessage(), e);\n        }\n    }\n\n    public static class ExcludeConfigResponse {\n\n        private boolean       success;\n\n        private ExcludeConfig result;\n\n        public boolean isSuccess() {\n            return success;\n        }\n\n        public void setSuccess(boolean success) {\n            this.success = success;\n        }\n\n        public ExcludeConfig getResult() {\n            return result;\n        }\n\n        public void setResult(ExcludeConfig result) {\n            this.result = result;\n        }\n    }\n\n    public static class ExcludeConfig {\n\n        private String       app;\n\n        private List<String> jarBlackGroupIds;\n\n        private List<String> jarBlackArtifactIds;\n\n        private List<String> jarBlackList;\n\n        private List<String> jarWhiteGroupIds;\n\n        private List<String> jarWhiteArtifactIds;\n\n        private List<String> jarWhiteList;\n\n        private List<String> jarWarnGroupIds;\n\n        private List<String> jarWarnArtifactIds;\n\n        private List<String> jarWarnList;\n\n        public String getApp() {\n            return app;\n        }\n\n        public void setApp(String app) {\n            this.app = app;\n        }\n\n        public List<String> getJarBlackGroupIds() {\n            return jarBlackGroupIds;\n        }\n\n        public void setJarBlackGroupIds(List<String> jarBlackGroupIds) {\n            this.jarBlackGroupIds = jarBlackGroupIds;\n        }\n\n        public List<String> getJarBlackArtifactIds() {\n            return jarBlackArtifactIds;\n        }\n\n        public void setJarBlackArtifactIds(List<String> jarBlackArtifactIds) {\n            this.jarBlackArtifactIds = jarBlackArtifactIds;\n        }\n\n        public List<String> getJarBlackList() {\n            return jarBlackList;\n        }\n\n        public void setJarBlackList(List<String> jarBlackList) {\n            this.jarBlackList = jarBlackList;\n        }\n\n        public List<String> getJarWhiteGroupIds() {\n            return jarWhiteGroupIds;\n        }\n\n        public void setJarWhiteGroupIds(List<String> jarWhiteGroupIds) {\n            this.jarWhiteGroupIds = jarWhiteGroupIds;\n        }\n\n        public List<String> getJarWhiteArtifactIds() {\n            return jarWhiteArtifactIds;\n        }\n\n        public void setJarWhiteArtifactIds(List<String> jarWhiteArtifactIds) {\n            this.jarWhiteArtifactIds = jarWhiteArtifactIds;\n        }\n\n        public List<String> getJarWhiteList() {\n            return jarWhiteList;\n        }\n\n        public void setJarWhiteList(List<String> jarWhiteList) {\n            this.jarWhiteList = jarWhiteList;\n        }\n\n        public List<String> getJarWarnGroupIds() {\n            return jarWarnGroupIds;\n        }\n\n        public void setJarWarnGroupIds(List<String> jarWarnGroupIds) {\n            this.jarWarnGroupIds = jarWarnGroupIds;\n        }\n\n        public List<String> getJarWarnArtifactIds() {\n            return jarWarnArtifactIds;\n        }\n\n        public void setJarWarnArtifactIds(List<String> jarWarnArtifactIds) {\n            this.jarWarnArtifactIds = jarWarnArtifactIds;\n        }\n\n        public List<String> getJarWarnList() {\n            return jarWarnList;\n        }\n\n        public void setJarWarnList(List<String> jarWarnList) {\n            this.jarWarnList = jarWarnList;\n        }\n    }\n\n    public static class ModuleSlimResult {\n        private List<String> excluded;\n        private List<String> compiled;\n\n        public List<String> getExcluded() {\n            return excluded;\n        }\n\n        public void setExcluded(List<String> excluded) {\n            this.excluded = excluded;\n        }\n\n        public List<String> getCompiled() {\n            return compiled;\n        }\n\n        public void setCompiled(List<String> compiled) {\n            this.compiled = compiled;\n        }\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-maven-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/RepackageMojo.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.boot.mojo;\n\nimport com.alipay.sofa.ark.boot.mojo.model.ArkConfigHolder;\nimport com.alipay.sofa.ark.tools.ArtifactItem;\nimport com.alipay.sofa.ark.tools.Libraries;\nimport com.alipay.sofa.ark.tools.Repackager;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.maven.artifact.Artifact;\nimport org.apache.maven.artifact.factory.ArtifactFactory;\nimport org.apache.maven.artifact.resolver.ArtifactResolutionRequest;\nimport org.apache.maven.execution.MavenSession;\nimport org.apache.maven.model.Dependency;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugin.MojoFailureException;\nimport org.apache.maven.plugin.descriptor.PluginDescriptor;\nimport org.apache.maven.plugins.annotations.Component;\nimport org.apache.maven.plugins.annotations.LifecyclePhase;\nimport org.apache.maven.plugins.annotations.Mojo;\nimport org.apache.maven.plugins.annotations.Parameter;\nimport org.apache.maven.plugins.annotations.ResolutionScope;\nimport org.apache.maven.plugins.dependency.tree.TreeMojo;\nimport org.apache.maven.project.MavenProject;\nimport org.apache.maven.project.MavenProjectHelper;\nimport org.apache.maven.project.ProjectBuilder;\nimport org.apache.maven.project.ProjectBuildingRequest;\nimport org.apache.maven.repository.RepositorySystem;\nimport org.apache.maven.shared.dependency.graph.DependencyNode;\nimport org.apache.maven.shared.invoker.DefaultInvocationRequest;\nimport org.apache.maven.shared.invoker.DefaultInvoker;\nimport org.apache.maven.shared.invoker.InvocationRequest;\nimport org.apache.maven.shared.invoker.InvocationResult;\nimport org.apache.maven.shared.invoker.Invoker;\nimport org.apache.maven.shared.invoker.MavenInvocationException;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.charset.Charset;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport static com.alipay.sofa.ark.boot.mojo.utils.ParseUtils.getStringSet;\nimport static com.alipay.sofa.ark.spi.constant.Constants.DECLARED_LIBRARIES_WHITELIST;\nimport static com.alipay.sofa.ark.tools.ArtifactItem.parseArtifactItem;\n\n/**\n * Repackages existing JAR archives so that they can be executed from the command\n * line using {@literal java -jar}.\n *\n * @author qilong.zql\n * @since 0.1.0\n */\n@Mojo(name = \"repackage\", defaultPhase = LifecyclePhase.PACKAGE, requiresProject = true, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME)\npublic class RepackageMojo extends TreeMojo {\n\n    private static final String    BIZ_NAME                   = \"com.alipay.sofa.ark.bizName\";\n\n    private static final String    DEFAULT_EXCLUDE_RULES      = \"rules.txt\";\n\n    @Parameter(defaultValue = \"${project}\", readonly = true, required = true)\n    private MavenProject           mavenProject;\n\n    @Component\n    private MavenProjectHelper     projectHelper;\n\n    @Component\n    private MavenSession           mavenSession;\n\n    @Component\n    private ArtifactFactory        artifactFactory;\n\n    @Component\n    private RepositorySystem       repositorySystem;\n\n    @Component\n    private ProjectBuilder         projectBuilder;\n\n    /**\n     * Directory containing the generated archive\n     * @since 0.1.0\n     */\n    @Parameter(defaultValue = \"${project.build.directory}\", required = true)\n    private File                   outputDirectory;\n\n    @Parameter(defaultValue = \"${project.basedir}\", required = true)\n    private File                   baseDir;\n\n    /**\n     * Deprecated by the default conf/ark/bootstrap.properties or conf/ark/bootstrap.yml\n     */\n    @Deprecated\n    @Parameter(defaultValue = \"\", required = false)\n    private String                 packExcludesConfig;\n\n    @Parameter(defaultValue = \"\", required = false)\n    private String                 packExcludesUrl;\n\n    /**\n     * Name of the generated archive\n     * @since 0.1.0\n     */\n    @Parameter(defaultValue = \"${project.build.finalName}\", required = true)\n    private String                 finalName;\n\n    /**\n     * Skip the repackage goal.\n     * @since 0.1.0\n     */\n    @Parameter(property = \"sofa.ark.repackage.skip\", defaultValue = \"false\")\n    private boolean                skip;\n\n    /**\n     * Classifier to add to the artifact generated. If attach is set 'true', the\n     * artifact will be attached with that classifier. Attaching the artifact\n     * allows to deploy it alongside to the main artifact.\n     * @since 0.1.0\n     */\n    @Parameter(defaultValue = \"ark-biz\", readonly = true)\n    private String                 bizClassifier;\n\n    /**\n     *\n     */\n    @Parameter(defaultValue = \"${project.artifactId}\")\n    private String                 bizName;\n\n    /**\n     * ark biz version\n     * @since 0.4.0\n     */\n    @Parameter(defaultValue = \"${project.version}\")\n    private String                 bizVersion;\n\n    /**\n     * ark biz version\n     * @since 0.4.0\n     */\n    @Parameter(defaultValue = \"100\", property = \"sofa.ark.biz.priority\")\n    protected Integer              priority;\n\n    /**\n     * Classifier to add to the executable artifact generated, if needed,\n     * 'sofa-ark' is recommended.\n     *\n     * @since 0.1.0\n     */\n    @Parameter(defaultValue = \"ark-executable\", readonly = true)\n    private String                 arkClassifier;\n\n    /**\n     * Attach the module archive to be installed and deployed.\n     * @since 0.1.0\n     */\n    @Parameter(defaultValue = \"false\")\n    private boolean                attach;\n\n    /**\n     * The name of the main class. If not specified the first compiled class found that\n     * contains a 'main' method will be used.\n     * @since 0.1.0\n     */\n    @Parameter\n    private String                 mainClass;\n\n    /**\n     * A list of the libraries that must be unpacked from fat jars in order to run.\n     * Specify each library as a <code>&lt;dependency&gt;</code> with a\n     * <code>&lt;groupId&gt;</code> and a <code>&lt;artifactId&gt;</code> and they will be\n     * unpacked at runtime.\n     * @since 0.1.0\n     */\n    @Parameter\n    private List<Dependency>       requiresUnpack;\n\n    /**\n     * The version of SOFAArk, same with plugin version. when developer\n     * want develop a application running on the SOFA-Ark. Just configure\n     * sofa-ark-maven-plugin.\n     */\n    private String                 arkVersion;\n\n    /**\n     * mvn command user properties\n     */\n    private ProjectBuildingRequest projectBuildingRequest;\n\n    /**\n     * Colon separated groupId, artifactId [and classifier] to exclude (exact match). e.g:\n     * group-a:tracer-core:3.0.10\n     * group-b:tracer-core:3.0.10:jdk17\n     */\n    @Parameter(defaultValue = \"\")\n    private LinkedHashSet<String>  excludes                   = new LinkedHashSet<>();\n\n    /**\n     * list of groupId names to exclude (exact match).\n     */\n    @Parameter(defaultValue = \"\")\n    private LinkedHashSet<String>  excludeGroupIds            = new LinkedHashSet<>();\n\n    /**\n     * list of artifact names to exclude (exact match).\n     */\n    @Parameter(defaultValue = \"\")\n    private LinkedHashSet<String>  excludeArtifactIds         = new LinkedHashSet<>();\n\n    /**\n     * Colon separated groupId, artifactId [and classifier] to include (exact match). e.g:\n     * group-a:tracer-core:3.0.10\n     * group-b:tracer-core:3.0.10:jdk17\n     */\n    @Parameter(defaultValue = \"\")\n    private LinkedHashSet<String>  includes                   = new LinkedHashSet<>();\n\n    /**\n     * list of groupId names to include (exact match).\n     */\n    @Parameter(defaultValue = \"\")\n    private LinkedHashSet<String>  includeGroupIds            = new LinkedHashSet<>();\n\n    /**\n     * list of artifact names to include (exact match).\n     */\n    @Parameter(defaultValue = \"\")\n    private LinkedHashSet<String>  includeArtifactIds         = new LinkedHashSet<>();\n\n    /**\n     * list of packages denied to be imported\n     */\n    @Parameter(defaultValue = \"\")\n    private LinkedHashSet<String>  denyImportPackages;\n\n    /**\n     * list of classes denied to be imported\n     */\n    @Parameter(defaultValue = \"\")\n    private LinkedHashSet<String>  denyImportClasses;\n\n    /**\n     * list of resources denied to be imported\n     */\n    @Parameter(defaultValue = \"\")\n    private LinkedHashSet<String>  denyImportResources;\n\n    /**\n     * list of inject plugin dependencies\n     */\n    @Parameter(defaultValue = \"\")\n    private LinkedHashSet<String>  injectPluginDependencies   = new LinkedHashSet<>();\n\n    /**\n     * list of inject plugin export packages\n     */\n    @Parameter(defaultValue = \"\")\n    private LinkedHashSet<String>  injectPluginExportPackages = new LinkedHashSet<>();\n\n    /**\n     * whether package provided dependencies into ark\n     */\n    @Parameter(defaultValue = \"false\")\n    private boolean                packageProvided;\n\n    /**\n     * whether to skip package ark-executable jar\n     */\n    @Parameter(defaultValue = \"false\")\n    private boolean                skipArkExecutable;\n\n    /**\n     * whether to keep ark biz jar after package finish, default value is true\n     */\n    @Parameter(defaultValue = \"true\")\n    private boolean                keepArkBizJar;\n\n    /**\n     * web context path when biz is web app. it must start with \"/\", default value is \"/\"\n     */\n    @Parameter(defaultValue = \"/\", required = true)\n    private String                 webContextPath;\n\n    /**\n     * the biz jar will record the declared libraries if true,\n     * and will filter out only declared libraries when delegate classes and resources to ark-base\n     */\n    @Parameter(defaultValue = \"false\")\n    private boolean                declaredMode;\n\n    @Parameter(defaultValue = \"false\")\n    private boolean                disableGitInfo;\n\n    /*----------------Git 相关参数---------------------*/\n    /**\n     * The root directory of the repository we want to check.\n     */\n    @Parameter(defaultValue = \"\")\n    private File                   gitDirectory;\n\n    /**\n     * 基座依赖标识，以 ${groupId}:${artifactId}:${version} 标识，或以 ${groupId}:${artifactId} 标识\n     */\n    @Parameter(defaultValue = \"\")\n    private String                 baseDependencyParentIdentity;\n\n    private File                   sofaArkLogDirectory;\n\n    @Override\n    public void execute() throws MojoExecutionException, MojoFailureException {\n        if (\"war\".equals(this.mavenProject.getPackaging())) {\n            getLog().debug(\"repackage goal could not be applied to war project.\");\n            return;\n        }\n        if (\"pom\".equals(this.mavenProject.getPackaging())) {\n            getLog().debug(\"repackage goal could not be applied to pom project.\");\n            return;\n        }\n        if (StringUtils.equals(this.arkClassifier, this.bizClassifier)) {\n            getLog().debug(\"Executable fat jar should be different from 'plug-in' module jar.\");\n            return;\n        }\n        if (this.skip) {\n            getLog().debug(\"skipping repackaging as configuration.\");\n            return;\n        }\n\n        projectBuildingRequest = this.mavenProject.getProjectBuildingRequest();\n        sofaArkLogDirectory = new File(this.outputDirectory, \"sofa-ark\");\n        if (!sofaArkLogDirectory.exists()) {\n            sofaArkLogDirectory.mkdirs();\n        }\n\n        /* version of ark container packaged into fat jar follows the plugin version */\n        PluginDescriptor pluginDescriptor = (PluginDescriptor) getPluginContext().get(\n            \"pluginDescriptor\");\n        arkVersion = pluginDescriptor.getVersion();\n\n        repackage();\n    }\n\n    /**\n     *\n     *\n     * @throws MojoExecutionException\n     * @throws MojoFailureException\n     */\n    private void repackage() throws MojoExecutionException, MojoFailureException {\n        File source = this.mavenProject.getArtifact().getFile();\n        File appTarget = getAppTargetFile();\n        File moduleTarget = getModuleTargetFile();\n\n        Repackager repackager = getRepackager(source);\n        Libraries libraries = new ArtifactsLibraries(getAdditionalArtifact(), this.requiresUnpack,\n            getLog());\n        try {\n            if (repackager.isDeclaredMode()) {\n                Set<ArtifactItem> artifactItems;\n                if (MavenUtils.isRootProject(this.mavenProject)) {\n                    artifactItems = getAllArtifact();\n                } else {\n                    artifactItems = getAllArtifactByMavenTree();\n                }\n                artifactItems.addAll(getDeclaredLibrariesWhitelist());\n                repackager.prepareDeclaredLibraries(artifactItems);\n            }\n            MavenProject rootProject = MavenUtils.getRootProject(this.mavenProject);\n            repackager.setGitDirectory(getGitDirectory(rootProject));\n            repackager.repackage(appTarget, moduleTarget, libraries);\n        } catch (IOException ex) {\n            throw new MojoExecutionException(ex.getMessage(), ex);\n        }\n        updateArtifact(appTarget, repackager.getModuleTargetFile());\n    }\n\n    protected Set<ArtifactItem> getDeclaredLibrariesWhitelist() throws IOException {\n        Set<ArtifactItem> res = new HashSet<>();\n        Properties prop = ArkConfigHolder.getArkProperties(baseDir.getAbsolutePath());\n        Map<String, Object> arkYaml = ArkConfigHolder.getArkYaml(baseDir.getAbsolutePath());\n\n        Set<String> declaredLibrariesWhitelist = new HashSet<>();\n        declaredLibrariesWhitelist.addAll(getStringSet(prop, DECLARED_LIBRARIES_WHITELIST));\n        declaredLibrariesWhitelist.addAll(getStringSet(arkYaml, DECLARED_LIBRARIES_WHITELIST));\n\n        for (String declaredLibrary : declaredLibrariesWhitelist) {\n            res.add(parseArtifactItem(declaredLibrary));\n        }\n        return res;\n    }\n\n    private File getGitDirectory(MavenProject rootProject) {\n        if (disableGitInfo) {\n            return null;\n        }\n        if (gitDirectory != null && gitDirectory.exists()) {\n            return gitDirectory;\n        }\n        return com.alipay.sofa.ark.common.util.FileUtils.file(rootProject.getBasedir()\n            .getAbsolutePath() + \"/.git\");\n    }\n\n    private void parseArtifactItems(DependencyNode rootNode, Set<ArtifactItem> result) {\n        if (rootNode != null) {\n            if (!StringUtils.equalsIgnoreCase(rootNode.getArtifact().getScope(), \"test\")) {\n                result.add(parseArtifactItem(rootNode.getArtifact()));\n            }\n\n            if (CollectionUtils.isNotEmpty(rootNode.getChildren())) {\n                for (DependencyNode node : rootNode.getChildren()) {\n                    parseArtifactItems(node, result);\n                }\n            }\n        }\n    }\n\n    private DependencyNode parseDependencyGraph() throws MojoExecutionException,\n                                                 MojoFailureException {\n        if (null == super.getDependencyGraph()) {\n            super.execute();\n        }\n        return super.getDependencyGraph();\n    }\n\n    private Set<ArtifactItem> getAllArtifact() throws MojoExecutionException, MojoFailureException {\n        DependencyNode dependencyNode = parseDependencyGraph();\n        Set<ArtifactItem> results = new HashSet<>();\n        parseArtifactItems(dependencyNode, results);\n        return results;\n    }\n\n    private Set<ArtifactItem> getAllArtifactByMavenTree() throws MojoExecutionException,\n                                                         MojoFailureException {\n        MavenProject rootProject = MavenUtils.getRootProject(this.mavenProject);\n        getLog().info(\"root project path: \" + rootProject.getBasedir().getAbsolutePath());\n\n        //  run  maven dependency:tree\n        try {\n            if (this.mavenProject.getBasedir() != null) {\n                doGetAllArtifactByMavenTree(this.mavenProject);\n                return getAllArtifact();\n            }\n        } catch (MojoExecutionException e) {\n            getLog().warn(\n                \"execute dependency:tree failed, try to execute dependency:tree in root project\");\n        }\n        return doGetAllArtifactByMavenTree(MavenUtils.getRootProject(this.mavenProject));\n    }\n\n    private Set<ArtifactItem> doGetAllArtifactByMavenTree(MavenProject project) throws MojoExecutionException {\n        File baseDir = project.getBasedir();\n        getLog().info(\"project path: \" + baseDir.getAbsolutePath());\n\n        // dependency:tree\n        InvocationRequest request = new DefaultInvocationRequest();\n        request.setPomFile(com.alipay.sofa.ark.common.util.FileUtils.file(baseDir.getAbsolutePath() + \"/pom.xml\"));\n\n        Properties userProperties = projectBuildingRequest.getUserProperties();\n        String outputPath = baseDir.getAbsolutePath() + \"/deps.log.\" + System.currentTimeMillis();\n        List<String> goals = Stream.of(\"dependency:tree\", \"-DappendOutput=true\", \"-DoutputFile=\\\"\" + outputPath + \"\\\"\").collect(Collectors.toList());\n        if (userProperties != null) {\n            userProperties.forEach((key, value) -> {\n                if (key instanceof String && StringUtils.equals(\"outputFile\", (String) key)) {\n                    return;\n                }\n                goals.add(String.format(\"-D%s=%s\", key, \"\\\"\" + value + \"\\\"\"));\n            });\n        }\n\n        getLog().info(\n                \"execute 'mvn dependency:tree' with command 'mvn \" + String.join(\" \", goals) + \"'\");\n        request.setGoals(goals);\n        request.setBatchMode(mavenSession.getSettings().getInteractiveMode());\n        request.setProfiles(mavenSession.getSettings().getActiveProfiles());\n        setSettingsLocation(request);\n        Invoker invoker = new DefaultInvoker();\n        try {\n            InvocationResult result = invoker.execute(request);\n            if (result.getExitCode() != 0) {\n                throw new MojoExecutionException(\"execute dependency:tree failed\",\n                        result.getExecutionException());\n            }\n\n            String depTreeStr = FileUtils.readFileToString(FileUtils.getFile(outputPath),\n                    Charset.defaultCharset());\n            return MavenUtils.convert(depTreeStr);\n        } catch (MavenInvocationException | IOException e) {\n            throw new MojoExecutionException(\"execute dependency:tree failed\", e);\n        } finally {\n            File outputFile = com.alipay.sofa.ark.common.util.FileUtils.file(outputPath);\n            if (outputFile.exists()) {\n                outputFile.delete();\n            }\n        }\n    }\n\n    private void setSettingsLocation(InvocationRequest request) {\n        File userSettingsFile = mavenSession.getRequest().getUserSettingsFile();\n        if (userSettingsFile != null && userSettingsFile.exists()) {\n            request.setUserSettingsFile(userSettingsFile);\n        }\n        File globalSettingsFile = mavenSession.getRequest().getGlobalSettingsFile();\n        if (globalSettingsFile != null && globalSettingsFile.exists()) {\n            request.setGlobalSettingsFile(globalSettingsFile);\n        }\n    }\n\n    /**\n     * @return sofa-ark-all and all maven project's non-excluded artifacts\n     * @throws MojoExecutionException\n     */\n    @SuppressWarnings(\"unchecked\")\n    private Set<Artifact> getAdditionalArtifact() throws MojoExecutionException {\n        Artifact arkArtifact = repositorySystem.createArtifact(ArkConstants.getGroupId(),\n            ArkConstants.getArtifactId(), arkVersion, ArkConstants.getScope(),\n            ArkConstants.getType());\n\n        try {\n            ArtifactResolutionRequest artifactResolutionRequest = new ArtifactResolutionRequest();\n            artifactResolutionRequest.setArtifact(arkArtifact);\n            artifactResolutionRequest.setLocalRepository(projectBuildingRequest\n                .getLocalRepository());\n            artifactResolutionRequest.setRemoteRepositories(this.mavenProject\n                .getRemoteArtifactRepositories());\n            repositorySystem.resolve(artifactResolutionRequest);\n            Set<Artifact> artifacts = new HashSet<>(Collections.singleton(arkArtifact));\n\n            // 读取需要打包的依赖\n            artifacts.addAll(getSlimmedArtifacts());\n            return artifacts;\n        } catch (Exception ex) {\n            throw new MojoExecutionException(ex.getMessage(), ex);\n        }\n    }\n\n    private Set<Artifact> getSlimmedArtifacts() throws MojoExecutionException, IOException,\n                                               MojoFailureException {\n        if (StringUtils.endsWithAny(packExcludesConfig, \".yml\", \".properties\")) {\n            throw new MojoExecutionException(\n                \"not support packExcludesConfig of .yml or .properties in sofa-ark-maven-plugin, please using default conf/ark/bootstrap.yml or conf/ark/bootstrap.properties\");\n        }\n        ModuleSlimConfig moduleSlimConfig = (new ModuleSlimConfig())\n            .setPackExcludesConfig(packExcludesConfig).setPackExcludesUrl(packExcludesUrl)\n            .setExcludes(excludes).setExcludeGroupIds(excludeGroupIds)\n            .setExcludeArtifactIds(excludeArtifactIds).setIncludes(includes)\n            .setIncludeGroupIds(includeGroupIds).setIncludeArtifactIds(includeArtifactIds)\n            .setBaseDependencyParentIdentity(baseDependencyParentIdentity);\n        ModuleSlimExecutor slimStrategyExecutor = new ModuleSlimExecutor(this.mavenProject,\n            this.repositorySystem, projectBuilder, parseDependencyGraph(), moduleSlimConfig,\n            this.baseDir, this.sofaArkLogDirectory, this.getLog());\n        return slimStrategyExecutor.getSlimmedArtifacts();\n    }\n\n    private File getAppTargetFile() {\n        String classifier = (this.arkClassifier == null ? \"\" : this.arkClassifier.trim());\n        if (classifier.length() > 0 && !classifier.startsWith(\"-\")) {\n            classifier = \"-\" + classifier;\n        }\n        if (!this.outputDirectory.exists()) {\n            this.outputDirectory.mkdirs();\n        }\n        return new File(this.outputDirectory, this.finalName\n                                              + classifier\n                                              + \".\"\n                                              + this.mavenProject.getArtifact()\n                                                  .getArtifactHandler().getExtension());\n    }\n\n    private File getModuleTargetFile() {\n        String classifier = (this.bizClassifier == null ? \"\" : this.bizClassifier.trim());\n        if (classifier.length() > 0 && !classifier.startsWith(\"-\")) {\n            classifier = \"-\" + classifier;\n        }\n        if (!this.outputDirectory.exists()) {\n            this.outputDirectory.mkdirs();\n        }\n        return new File(this.outputDirectory, this.finalName\n                                              + classifier\n                                              + \".\"\n                                              + this.mavenProject.getArtifact()\n                                                  .getArtifactHandler().getExtension());\n    }\n\n    private Repackager getRepackager(File source) {\n        Repackager repackager = new Repackager(source);\n        repackager.addMainClassTimeoutWarningListener(new LoggingMainClassTimeoutWarningListener());\n        repackager.setMainClass(this.mainClass);\n        repackager.setBizName(bizName);\n        if (!StringUtils.isEmpty(System.getProperty(BIZ_NAME))) {\n            repackager.setBizName(System.getProperty(BIZ_NAME));\n        }\n        repackager.setBizVersion(bizVersion);\n        repackager.setPriority(String.valueOf(priority));\n        repackager.setArkVersion(arkVersion);\n        repackager.setDenyImportClasses(denyImportClasses);\n        repackager.setDenyImportPackages(denyImportPackages);\n        repackager.setDenyImportResources(denyImportResources);\n        repackager.setInjectPluginDependencies(injectPluginDependencies);\n        repackager.setInjectPluginExportPackages(injectPluginExportPackages);\n        repackager.setPackageProvided(packageProvided);\n        repackager.setSkipArkExecutable(skipArkExecutable);\n        repackager.setKeepArkBizJar(keepArkBizJar);\n        repackager.setBaseDir(baseDir);\n        repackager.setWebContextPath(webContextPath);\n        repackager.setDeclaredMode(declaredMode);\n        return repackager;\n    }\n\n    private void updateArtifact(File repackaged, File modulePackaged) {\n        if (this.attach) {\n            if (!this.skipArkExecutable) {\n                attachArtifact(repackaged, arkClassifier);\n            }\n            if (this.keepArkBizJar) {\n                attachArtifact(modulePackaged, bizClassifier);\n            }\n        }\n    }\n\n    private void attachArtifact(File jarFile, String classifier) {\n        getLog().info(\"Attaching archive:\" + jarFile + \", with classifier: \" + classifier);\n        this.projectHelper.attachArtifact(this.mavenProject, this.mavenProject.getPackaging(),\n            classifier, jarFile);\n    }\n\n    private class LoggingMainClassTimeoutWarningListener implements\n                                                        Repackager.MainClassTimeoutWarningListener {\n\n        @Override\n        public void handleTimeoutWarning(long duration, String mainMethod) {\n            getLog()\n                .warn(\n                    String\n                        .format(\n                            \"Searching for the main-class is taking some time: %dms, consider using the mainClass configuration parameter\",\n                            duration));\n        }\n\n    }\n\n    public void setExcludes(String str) {\n        this.excludes = parseToSet(str);\n    }\n\n    public void setExcludeGroupIds(String str) {\n        this.excludeGroupIds = parseToSet(str);\n    }\n\n    public void setExcludeArtifactIds(String str) {\n        this.excludeArtifactIds = parseToSet(str);\n    }\n\n    public void setDenyImportPackages(String str) {\n        this.denyImportPackages = parseToSet(str);\n    }\n\n    public void setDenyImportClasses(String str) {\n        this.denyImportClasses = parseToSet(str);\n    }\n\n    public void setDenyImportResources(String str) {\n        this.denyImportResources = parseToSet(str);\n    }\n\n    public void setInjectPluginDependencies(String str) {\n        this.injectPluginDependencies = parseToSet(str);\n    }\n\n    public void setInjectPluginExportPackages(String str) {\n        this.injectPluginExportPackages = parseToSet(str);\n    }\n\n    private LinkedHashSet<String> parseToSet(String str) {\n        LinkedHashSet<String> set = new LinkedHashSet<>();\n        if (StringUtils.isBlank(str)) {\n            return set;\n        }\n        Arrays.stream(str.split(\",\"))\n                .map(String::trim)\n                .filter(StringUtils::isNotBlank)\n                .forEach(set::add);\n        return set;\n    }\n\n    public static class ArkConstants {\n\n        private static String groupId    = \"com.alipay.sofa\";\n\n        private static String artifactId = \"sofa-ark-all\";\n\n        private static String classifier = \"\";\n\n        private static String scope      = \"compile\";\n\n        private static String type       = \"jar\";\n\n        public static String getGroupId() {\n            return groupId;\n        }\n\n        public static String getArtifactId() {\n            return artifactId;\n        }\n\n        public static String getClassifier() {\n            return classifier;\n        }\n\n        public static String getScope() {\n            return scope;\n        }\n\n        public static String getType() {\n            return type;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-maven-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/model/ArkConfigHolder.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.boot.mojo.model;\n\nimport org.apache.maven.plugin.logging.SystemStreamLog;\nimport org.yaml.snakeyaml.Yaml;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.ARK_CONF_BASE_DIR;\nimport static com.alipay.sofa.ark.spi.constant.Constants.ARK_CONF_FILE;\nimport static com.alipay.sofa.ark.spi.constant.Constants.ARK_CONF_YAML_FILE;\nimport static com.google.common.collect.Maps.newHashMap;\n\n/**\n * @author lianglipeng.llp@alibaba-inc.com\n * @version $Id: ArkConfigHolder.java, v 0.1 2024年06月26日 13:45 立蓬 Exp $\n */\npublic class ArkConfigHolder {\n    private static Properties          arkProperties;\n\n    private static Map<String, Object> arkYaml;\n\n    private static SystemStreamLog     log = new SystemStreamLog();\n\n    public static Properties getArkProperties(String baseDir) throws IOException {\n        return arkProperties == null ? initArkProperties(baseDir) : arkProperties;\n    }\n\n    public static Map<String, Object> getArkYaml(String baseDir) throws IOException {\n        return arkYaml == null ? initArkYaml(baseDir) : arkYaml;\n    }\n\n    private static Map<String, Object> initArkYaml(String baseDir) throws IOException {\n        String configPath = baseDir + File.separator + ARK_CONF_BASE_DIR + File.separator\n                            + ARK_CONF_YAML_FILE;\n        File configFile = new File(configPath);\n        if (!configFile.exists()) {\n            log.info(String.format(\n                \"sofa-ark-maven-plugin: extension-config %s not found, will not config it\",\n                configPath));\n            return newHashMap();\n        }\n\n        log.info(String.format(\n            \"sofa-ark-maven-plugin: find extension-config %s and will config it\", configPath));\n\n        try (FileInputStream fis = new FileInputStream(configPath)) {\n            Yaml yaml = new Yaml();\n            arkYaml = yaml.load(fis);\n            return arkYaml;\n        } catch (IOException ex) {\n            log.error(String.format(\"failed to parse excludes artifacts from %s.\", configPath), ex);\n            throw ex;\n        }\n    }\n\n    private static Properties initArkProperties(String baseDir) throws IOException {\n        String configPath = baseDir + File.separator + ARK_CONF_BASE_DIR + File.separator\n                            + ARK_CONF_FILE;\n        File configFile = new File(configPath);\n        if (!configFile.exists()) {\n            log.info(String.format(\n                \"sofa-ark-maven-plugin: extension-config %s not found, will not config it\",\n                configPath));\n            return new Properties();\n        }\n\n        log.info(String.format(\n            \"sofa-ark-maven-plugin: find extension-config %s and will config it\", configPath));\n\n        Properties prop = new Properties();\n        try (FileInputStream fis = new FileInputStream(configPath)) {\n            prop.load(fis);\n            arkProperties = prop;\n            return prop;\n        } catch (IOException ex) {\n            log.error(String.format(\n                \"sofa-ark-maven-plugin: failed to read extension-config from %s.\", configPath), ex);\n            throw ex;\n        }\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-maven-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/utils/ParseUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.boot.mojo.utils;\n\nimport org.apache.commons.collections.MapUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.Set;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.COMMA_SPLIT;\nimport static com.google.common.collect.Sets.newHashSet;\n\n/**\n * @author lianglipeng.llp@alibaba-inc.com\n * @version $Id: ParseUtils.java, v 0.1 2024年07月14日 01:24 立蓬 Exp $\n */\npublic class ParseUtils {\n    public static Set<String> getStringSet(Properties prop, String confKey) {\n        if (null == prop) {\n            return newHashSet();\n        }\n        String[] values = StringUtils.split(prop.getProperty(confKey), COMMA_SPLIT);\n        if (values == null) {\n            return newHashSet();\n        }\n        values = Arrays.stream(values).map(String::trim).filter(s -> !s.isEmpty()).toArray(String[]::new);\n        return newHashSet(values);\n    }\n\n    public static Set<String> getStringSet(Map<String, Object> yaml, String confKey) {\n        Object value = getValue(yaml, confKey);\n        if (null == value) {\n            return newHashSet();\n        }\n        return newHashSet((List<String>) value);\n    }\n\n    public static boolean getBooleanWithDefault(Map<String, Object> yaml, String confKey,\n                                                boolean defaultValue) {\n        Object value = getValue(yaml, confKey);\n\n        if (null == value) {\n            return defaultValue;\n        }\n        return Boolean.parseBoolean(value.toString());\n    }\n\n    public static boolean getBooleanWithDefault(Properties prop, String confKey,\n                                                boolean defaultValue) {\n        Object value = prop.getProperty(confKey);\n        if (null == value) {\n            return defaultValue;\n        }\n        return Boolean.parseBoolean(value.toString());\n    }\n\n    public static boolean getBooleanWithDefault(Properties prop, Map<String, Object> yaml,\n                                                String confKey, boolean defaultValue) {\n        Object valueFromProp = prop.getProperty(confKey);\n        Object valueFromYaml = getValue(yaml, confKey);\n\n        if (null == valueFromProp && null == valueFromYaml) {\n            return defaultValue;\n        }\n        return null != valueFromProp ? Boolean.parseBoolean(valueFromProp.toString()) : Boolean\n            .parseBoolean(valueFromYaml.toString());\n    }\n\n    private static Object getValue(Map<String, Object> yaml, String confKey) {\n        if (MapUtils.isEmpty(yaml) || StringUtils.isEmpty(confKey)) {\n            return null;\n        }\n\n        List<String> keys = Arrays.asList(StringUtils.split(confKey, \".\"));\n        String currentKey = keys.get(0);\n        if (yaml.containsKey(currentKey) && null != yaml.get(currentKey) && keys.size() == 1) {\n            return yaml.get(currentKey);\n        }\n\n        if (yaml.containsKey(currentKey) && null != yaml.get(currentKey) && keys.size() > 1\n            && yaml.get(currentKey) instanceof Map) {\n            return getValue((Map<String, Object>) yaml.get(currentKey),\n                StringUtils.join(keys.subList(1, keys.size()), \".\"));\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-maven-plugin/src/test/java/com/alipay/sofa/ark/boot/mojo/ArtifactsLibrariesTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.boot.mojo;\n\nimport org.apache.maven.artifact.Artifact;\nimport org.apache.maven.artifact.DefaultArtifact;\nimport org.apache.maven.artifact.handler.DefaultArtifactHandler;\nimport org.apache.maven.model.Dependency;\nimport org.apache.maven.monitor.logging.DefaultLog;\nimport org.codehaus.plexus.logging.console.ConsoleLogger;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport static org.junit.Assert.assertEquals;\n\npublic class ArtifactsLibrariesTest {\n\n    public ArtifactsLibrariesTest() {\n    }\n\n    @Before\n    public void setUp() {\n    }\n\n    @After\n    public void tearDown() {\n    }\n\n    @Test\n    public void testDoWithLibraries() throws IOException {\n\n        Set<Artifact> artifacts = new HashSet<>();\n        DefaultArtifact artifact = new DefaultArtifact(\"group1\", \"artifact1\", \"1.0\", \"compile\", \"\", null, new DefaultArtifactHandler());\n        artifact.setFile(new File(\"./\"));\n        artifacts.add(artifact);\n\n        artifact = new DefaultArtifact(\"group1\", \"artifact1\", \"2.0\", \"provided\", \"\", null, new DefaultArtifactHandler());\n        artifact.setFile(new File(\"./\"));\n        artifacts.add(artifact);\n\n        artifact = new DefaultArtifact(\"group1\", \"artifact2\", \"2.0\", \"runtime\", \"\", null, new DefaultArtifactHandler());\n        artifact.setFile(new File(\"./\"));\n        artifacts.add(artifact);\n\n        artifact = new DefaultArtifact(\"group2\", \"artifact2\", \"2.0\", \"compile\", \"\", null, new DefaultArtifactHandler());\n        artifact.setFile(new File(\"./\"));\n        artifacts.add(artifact);\n\n        artifact = new DefaultArtifact(\"group2\", \"artifact3\", \"2.0\", \"provided\", \"\", \"clsf\", new DefaultArtifactHandler());\n        artifact.setFile(new File(\"./\"));\n        artifacts.add(artifact);\n\n        artifact = new DefaultArtifact(\"group3\", \"artifact3\", \"2.0\", \"runtime\", \"\", \"clsf\", new DefaultArtifactHandler());\n        artifact.setFile(new File(\"./\"));\n        artifacts.add(artifact);\n\n        artifact = new DefaultArtifact(\"group3\", \"artifact4\", \"2.0\", null, \"\", \"clsf\", new DefaultArtifactHandler());\n        artifact.setFile(new File(\"./\"));\n        artifacts.add(artifact);\n\n        List<Dependency> dependencies = new ArrayList<>();\n        Dependency dependency = new Dependency();\n        dependency.setGroupId(\"group3\");\n        dependency.setArtifactId(\"artifact3\");\n        dependencies.add(dependency);\n\n        AtomicInteger atomicInteger = new AtomicInteger(0);\n        new ArtifactsLibraries(artifacts, dependencies, new DefaultLog(new ConsoleLogger())).doWithLibraries(\n                library -> atomicInteger.incrementAndGet());\n\n        assertEquals(artifacts.size() - 1, atomicInteger.get());\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-maven-plugin/src/test/java/com/alipay/sofa/ark/boot/mojo/CommonUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.boot.mojo;\n\nimport java.io.File;\nimport java.net.URISyntaxException;\nimport java.net.URL;\n\n/**\n * @author lianglipeng.llp@alibaba-inc.com\n * @version $Id: CommonUtils.java, v 0.1 2024年08月22日 21:56 立蓬 Exp $\n */\npublic class CommonUtils {\n\n    public static File getResourceFile(String resourceName) throws URISyntaxException {\n        URL url = CommonUtils.class.getClassLoader().getResource(resourceName);\n        return new File(url.toURI());\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-maven-plugin/src/test/java/com/alipay/sofa/ark/boot/mojo/MavenUtilsTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.boot.mojo;\n\nimport com.alipay.sofa.ark.tools.ArtifactItem;\nimport org.apache.maven.project.MavenProject;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport static com.alipay.sofa.ark.boot.mojo.MavenUtils.*;\nimport static org.junit.Assert.assertEquals;\n\npublic class MavenUtilsTest {\n\n    @Before\n    public void setUp() {\n    }\n\n    @After\n    public void tearDown() {\n    }\n\n    @Test\n    public void testIsRootProject() {\n\n        assertEquals(true, isRootProject(null));\n        MavenProject parentMavenProject = new MavenProject();\n        parentMavenProject.setFile(new File(\"./a\"));\n        MavenProject mavenProject = new MavenProject();\n        mavenProject.setParent(parentMavenProject);\n        assertEquals(false, isRootProject(mavenProject));\n        parentMavenProject.setFile(null);\n        assertEquals(true, isRootProject(mavenProject));\n        assertEquals(null, getRootProject(null));\n    }\n\n    @Test\n    public void testConvert() {\n\n        assertEquals(new HashSet<>(), convert(\"\"));\n        assertEquals(new HashSet<>(), convert(\"\\n\"));\n        assertEquals(new HashSet<>(), convert(\"\\n\\r\"));\n        assertEquals(new HashSet<>(), convert(\"\\n\\r\"));\n        assertEquals(new HashSet<>(), convert(\"a\\na\"));\n\n        Set<ArtifactItem> artifactItems = new HashSet<>();\n        ArtifactItem artifactItem = new ArtifactItem();\n        artifactItem.setGroupId(\"org.springframework.boot\");\n        artifactItem.setArtifactId(\"spring-boot\");\n        artifactItem.setType(\"jar\");\n        artifactItem.setVersion(\"2.7.14\");\n        artifactItem.setScope(\"provided\");\n        artifactItems.add(artifactItem);\n        artifactItem = new ArtifactItem();\n        artifactItem.setGroupId(\"org.springframework\");\n        artifactItem.setArtifactId(\"spring-jcl\");\n        artifactItem.setType(\"jar\");\n        artifactItem.setVersion(\"5.3.29\");\n        artifactItem.setScope(\"provided\");\n        artifactItem.setClassifier(\"ark-biz\");\n        artifactItems.add(artifactItem);\n\n        assertEquals(\n            artifactItems,\n            convert(\"[INFO] com.alipay.sofa:sofa-ark-springboot-starter:jar:2.2.4-SNAPSHOT\\n\"\n                    + \"[INFO] +- org.springframework.boot:spring-boot:jar:2.7.14:provided\\n\"\n                    + \"[INFO] |  |  \\\\- org.springframework:spring-jcl:jar:ark-biz:5.3.29:provided\\n\"\n                    + \"[INFO] |  \\\\- org.springframework:spring-context:jar:5.3.29\"));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-maven-plugin/src/test/java/com/alipay/sofa/ark/boot/mojo/ModuleSlimExecutorTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.boot.mojo;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Sets;\nimport org.apache.maven.artifact.Artifact;\nimport org.apache.maven.artifact.DefaultArtifact;\nimport org.apache.maven.artifact.handler.DefaultArtifactHandler;\nimport org.apache.maven.model.Dependency;\nimport org.apache.maven.model.DependencyManagement;\nimport org.apache.maven.model.Model;\nimport org.apache.maven.model.Parent;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugin.logging.Log;\nimport org.apache.maven.project.MavenProject;\nimport org.apache.maven.shared.dependency.graph.DependencyNode;\nimport org.junit.Test;\nimport org.mockito.Mockito;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport static com.alipay.sofa.ark.boot.mojo.ReflectionUtils.setField;\nimport static java.util.Arrays.asList;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.ArgumentMatchers.anySet;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.doNothing;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\n/**\n * @author lianglipeng.llp@alibaba-inc.com\n * @version $Id: ModuleSlimStrategyTest.java, v 0.1 2024年07月24日 16:33 立蓬 Exp $\n */\npublic class ModuleSlimExecutorTest {\n\n    @Test\n    public void testGetSlimmedArtifacts() throws MojoExecutionException, IOException,\n                                         URISyntaxException {\n        MavenProject proj = mock(MavenProject.class);\n        Artifact a1 = new DefaultArtifact(\"com.alipay.sofa\", \"a1\", \"version\", \"compile\", \"jar\",\n            null, new DefaultArtifactHandler());\n        Artifact a2 = new DefaultArtifact(\"com.alipay.sofa\", \"a2\", \"version\", \"compile\", \"jar\",\n            null, new DefaultArtifactHandler());\n        Artifact a3 = new DefaultArtifact(\"com.alipay.sofa\", \"a3\", \"version\", \"compile\", \"jar\",\n            null, new DefaultArtifactHandler());\n        Artifact a4 = new DefaultArtifact(\"com.alipay.sofa\", \"a4\", \"version\", \"provided\", \"jar\",\n            null, new DefaultArtifactHandler());\n        Artifact a5 = new DefaultArtifact(\"com.alipay.sofa\", \"a5\", \"version\", \"compile\", \"jar\",\n            null, new DefaultArtifactHandler());\n\n        Set<Artifact> artifacts = Sets.newHashSet(a1, a2, a3, a4, a5);\n        when(proj.getArtifacts()).thenReturn(artifacts);\n\n        ModuleSlimConfig config = new ModuleSlimConfig();\n\n        ModuleSlimExecutor strategy = spy(new ModuleSlimExecutor(proj, null, null, null, config,\n            mockBaseDir(), null, mockLog()));\n\n        doNothing().when(strategy).checkExcludeByParentIdentity(anySet());\n        doReturn(Sets.newHashSet(a1)).when(strategy).getArtifactsToFilterByParentIdentity(anySet());\n        doReturn(Sets.newHashSet(a2)).when(strategy).getArtifactsToFilterByExcludeConfig(anySet());\n        doReturn(Sets.newHashSet(a3, a4)).when(strategy).getArtifactsToFilterByBasePlugin(anySet());\n        doReturn(Sets.newHashSet(a4)).when(strategy).getArtifactsToAddByIncludeConfig(anySet());\n\n        assertEquals(2, strategy.getSlimmedArtifacts().size());\n        assertEquals(Artifact.SCOPE_COMPILE, a4.getScope());\n    }\n\n    @Test\n    public void testGetArtifactsToFilterByParentIdentity() throws URISyntaxException,\n                                                          MojoExecutionException {\n        ModuleSlimConfig config = (new ModuleSlimConfig())\n            .setBaseDependencyParentIdentity(\"com.mock:base-dependencies-starter:1.0\");\n        ModuleSlimExecutor strategy = new ModuleSlimExecutor(getMockBootstrapProject(), null, null,\n            null, config, mockBaseDir(), null, mockLog());\n\n        Artifact sameArtifact = mock(Artifact.class);\n        when(sameArtifact.getGroupId()).thenReturn(\"com.mock\");\n        when(sameArtifact.getArtifactId()).thenReturn(\"same-dependency-artifact\");\n        when(sameArtifact.getVersion()).thenReturn(\"1.0\");\n        when(sameArtifact.getBaseVersion()).thenReturn(\"1.0-SNAPSHOT\");\n        when(sameArtifact.getType()).thenReturn(\"jar\");\n\n        Artifact differenceArtifact = mock(Artifact.class);\n        when(differenceArtifact.getGroupId()).thenReturn(\"com.mock\");\n        when(differenceArtifact.getArtifactId()).thenReturn(\"difference-dependency-artifact\");\n        when(differenceArtifact.getVersion()).thenReturn(\"2.0\");\n        when(differenceArtifact.getBaseVersion()).thenReturn(\"2.0-SNAPSHOT\");\n        when(sameArtifact.getType()).thenReturn(\"jar\");\n\n        // case1: with BaseDependencyParentIdentity\n        Set<Artifact> res = strategy.getArtifactsToFilterByParentIdentity(Sets.newHashSet(\n            sameArtifact, differenceArtifact));\n        assertTrue(res.contains(sameArtifact));\n        assertFalse(res.contains(differenceArtifact));\n\n        // case2: without BaseDependencyParentIdentity\n        config.setBaseDependencyParentIdentity(\"\");\n        res = strategy.getArtifactsToFilterByParentIdentity(Sets.newHashSet(sameArtifact,\n            differenceArtifact));\n        assertTrue(res.isEmpty());\n    }\n\n    @Test\n    public void testGetArtifactsToFilterByBasePlugin() throws URISyntaxException {\n        ModuleSlimConfig config = new ModuleSlimConfig();\n        ModuleSlimExecutor strategy = spy(new ModuleSlimExecutor(getMockBootstrapProject(), null,\n            null, null, config, mockBaseDir(), null, mockLog()));\n\n        Artifact sameArtifact = mock(Artifact.class);\n        when(sameArtifact.getGroupId()).thenReturn(\"com.mock\");\n        when(sameArtifact.getArtifactId()).thenReturn(\"same-dependency-artifact\");\n        when(sameArtifact.getVersion()).thenReturn(\"1.0\");\n        when(sameArtifact.getBaseVersion()).thenReturn(\"1.0-SNAPSHOT\");\n        when(sameArtifact.getType()).thenReturn(\"jar\");\n\n        Artifact differenceArtifact = mock(Artifact.class);\n        when(differenceArtifact.getGroupId()).thenReturn(\"com.mock\");\n        when(differenceArtifact.getArtifactId()).thenReturn(\"difference-dependency-artifact\");\n        when(differenceArtifact.getVersion()).thenReturn(\"2.0\");\n        when(differenceArtifact.getBaseVersion()).thenReturn(\"2.0-SNAPSHOT\");\n        when(sameArtifact.getType()).thenReturn(\"jar\");\n\n        doReturn(mockBasePluginBomModel(Lists.newArrayList(sameArtifact))).when(strategy)\n            .resolvePomAsOriginalModel(anyString(), anyString(), anyString());\n\n        // case1: without BaseDependencyParentIdentity\n        config.setBaseDependencyParentIdentity(\"\");\n        Set<Artifact> res = strategy.getArtifactsToFilterByBasePlugin(Sets.newHashSet(sameArtifact,\n            differenceArtifact));\n        assertTrue(res.isEmpty());\n\n        // case2: with BaseDependencyParentIdentity\n        config.setBaseDependencyParentIdentity(\"com.mock:base-dependencies-starter:1.0\");\n        res = strategy.getArtifactsToFilterByBasePlugin(Sets.newHashSet(sameArtifact,\n            differenceArtifact));\n        assertTrue(res.contains(sameArtifact));\n        assertFalse(res.contains(differenceArtifact));\n    }\n\n    private Model mockBasePluginBomModel(List<Artifact> artifacts){\n        Model model = new Model();\n        model.setGroupId(\"com.mock\");\n        model.setArtifactId(\"base-plugin-bom\");\n        model.setVersion(\"1.0\");\n        model.setPackaging(\"pom\");\n\n        Parent parent = new Parent();\n        parent.setGroupId(\"com.mock\");\n        parent.setArtifactId(\"base-dependencies-starter\");\n        parent.setVersion(\"1.0\");\n        model.setParent(parent);\n\n        DependencyManagement dependencyManagement = new DependencyManagement();\n        dependencyManagement.setDependencies(artifacts.stream().map(artifact -> {\n            Dependency d = new Dependency();\n            d.setGroupId(artifact.getGroupId());\n            d.setArtifactId(artifact.getArtifactId());\n            d.setVersion(artifact.getBaseVersion());\n            return d;\n        }).collect(Collectors.toList()));\n        model.setDependencyManagement(dependencyManagement);\n        return model;\n    }\n\n    @Test\n    public void testCheckExcludeByParentIdentity() throws URISyntaxException,\n                                                  MojoExecutionException {\n        ModuleSlimConfig config = (new ModuleSlimConfig())\n            .setBaseDependencyParentIdentity(\"com.mock:base-dependencies-starter:1.0\");\n\n        Log log = Mockito.mock(Log.class);\n        doNothing().when(log).info(anyString());\n        doNothing().when(log).error(anyString());\n\n        ModuleSlimExecutor strategy = new ModuleSlimExecutor(getMockBootstrapProject(), null, null,\n            null, config, mockBaseDir(), null, log);\n\n        // 基座和模块都有该依赖，且版本一致\n        Artifact sameArtifact = mock(Artifact.class);\n        when(sameArtifact.getGroupId()).thenReturn(\"com.mock\");\n        when(sameArtifact.getArtifactId()).thenReturn(\"same-dependency-artifact\");\n        when(sameArtifact.getVersion()).thenReturn(\"1.0\");\n        when(sameArtifact.getBaseVersion()).thenReturn(\"1.0-SNAPSHOT\");\n        when(sameArtifact.getType()).thenReturn(\"jar\");\n\n        // 模块有，但基座没有的依赖\n        Artifact differentArtifact = mock(Artifact.class);\n        when(differentArtifact.getGroupId()).thenReturn(\"com.mock\");\n        when(differentArtifact.getArtifactId()).thenReturn(\"different-artifact\");\n        when(differentArtifact.getVersion()).thenReturn(\"1.0\");\n        when(differentArtifact.getBaseVersion()).thenReturn(\"1.0-SNAPSHOT\");\n        when(differentArtifact.getType()).thenReturn(\"jar\");\n\n        // 模块和基座都有该依赖，但版本不一致\n        Artifact differentVersionArtifact = mock(Artifact.class);\n        when(differentVersionArtifact.getGroupId()).thenReturn(\"com.mock\");\n        when(differentVersionArtifact.getArtifactId()).thenReturn(\"difference-dependency-artifact\");\n        when(differentVersionArtifact.getVersion()).thenReturn(\"2.0\");\n        when(differentVersionArtifact.getBaseVersion()).thenReturn(\"2.0-SNAPSHOT\");\n        when(differentVersionArtifact.getType()).thenReturn(\"jar\");\n\n        Dependency differenceVersionDependency = new Dependency();\n        differenceVersionDependency.setArtifactId(\"difference-dependency-artifact\");\n        differenceVersionDependency.setGroupId(\"com.mock\");\n        differenceVersionDependency.setVersion(\"1.0-SNAPSHOT\");\n\n        // case1: 排除相同的依赖\n        Set<Artifact> toFilterByExclude = Sets.newHashSet(sameArtifact);\n        strategy.checkExcludeByParentIdentity(toFilterByExclude);\n        verify(log)\n            .info(\n                eq(\"check excludeWithBaseDependencyParentIdentity success with base: com.mock:base-dependencies-starter:1.0\"));\n\n        // case2: 排除了基座没有的依赖\n        toFilterByExclude = Sets.newHashSet(differentArtifact);\n        try {\n            strategy.checkExcludeByParentIdentity(toFilterByExclude);\n        } catch (MojoExecutionException e) {\n            // 验证构建失败\n            verify(log)\n                .error(\n                    eq(String\n                        .format(\n                            \"error to exclude package jar: %s because no such jar in base, please keep the jar or add it to base\",\n                            MavenUtils.getArtifactIdentity(differentArtifact))));\n\n            assertEquals(String.format(\n                \"check excludeWithBaseDependencyParentIdentity failed with base: %s\",\n                config.getBaseDependencyParentIdentity()), e.getMessage());\n        }\n\n        // case3: 排除了不同版本的依赖\n        toFilterByExclude = Sets.newHashSet(differentVersionArtifact);\n        strategy.checkExcludeByParentIdentity(toFilterByExclude);\n        verify(log)\n            .error(\n                eq(String\n                    .format(\n                        \"error to exclude package jar: %s because it has different version with: %s in base, please keep the jar or set same version with base\",\n                        MavenUtils.getArtifactIdentity(differentVersionArtifact),\n                        MavenUtils.getDependencyIdentity(differenceVersionDependency))));\n\n        // case4: 配置开关：如果排除的依赖有问题，那么构建报错\n        config.setBuildFailWhenExcludeBaseDependencyWithDiffVersion(true);\n        try {\n            strategy.checkExcludeByParentIdentity(toFilterByExclude);\n        } catch (MojoExecutionException e) {\n            // 验证构建失败\n            assertEquals(String.format(\n                \"check excludeWithBaseDependencyParentIdentity failed with base: %s\",\n                config.getBaseDependencyParentIdentity()), e.getMessage());\n        }\n    }\n\n    @Test\n    public void testGetBaseDependencyParentOriginalModel() throws URISyntaxException {\n        // find base-dependency-parent by gav identity\n        ModuleSlimConfig config = (new ModuleSlimConfig())\n            .setBaseDependencyParentIdentity(\"com.mock:base-dependencies-starter:1.0\");\n        ModuleSlimExecutor strategy = new ModuleSlimExecutor(getMockBootstrapProject(), null, null,\n            null, config, mockBaseDir(), null, null);\n        assertNotNull(strategy.getBaseDependencyParentOriginalModel());\n\n        // find base-dependency-parent by ga identity\n        config.setBaseDependencyParentIdentity(\"com.mock:base-dependencies-starter\");\n        assertNotNull(strategy.getBaseDependencyParentOriginalModel());\n    }\n\n    @Test\n    public void testExtensionExcludeAndIncludeArtifactsByDefault() throws URISyntaxException,\n                                                                  IOException {\n        ModuleSlimConfig config = new ModuleSlimConfig();\n        ModuleSlimExecutor strategy = new ModuleSlimExecutor(getMockBootstrapProject(), null, null,\n            null, config, mockBaseDir(), null, mockLog());\n\n        strategy.configExcludeArtifactsByDefault();\n\n        // 验证 ark.properties\n        assertTrue(config.getExcludes().contains(\"commons-beanutils:commons-beanutils\"));\n        assertTrue(config.getExcludeGroupIds().contains(\"org.springframework\"));\n        assertTrue(config.getExcludeArtifactIds().contains(\"sofa-ark-spi\"));\n        assertTrue(config.getIncludes().contains(\"com.alipay.sofa:sofa-ark-all\"));\n        assertTrue(config.getIncludeGroupIds().contains(\"com.alipay.sofa\"));\n        assertTrue(config.getIncludeArtifactIds().contains(\"sofa-ark-all\"));\n\n        // 验证 ark.yml\n        assertTrue(config.getExcludes().contains(\"commons-beanutils:commons-beanutils-yml\"));\n        assertTrue(config.getExcludeGroupIds().contains(\"org.springframework-yml\"));\n        assertTrue(config.getExcludeArtifactIds().contains(\"sofa-ark-spi-yml\"));\n        assertTrue(config.getIncludes().contains(\"com.alipay.sofa:sofa-ark-all-yml\"));\n        assertTrue(config.getIncludeGroupIds().contains(\"com.alipay.sofa\"));\n        assertTrue(config.getIncludeArtifactIds().contains(\"sofa-ark-all-yml\"));\n    }\n\n    @Test\n    public void testExtensionExcludeAndIncludeArtifacts() throws URISyntaxException {\n        ModuleSlimConfig config = new ModuleSlimConfig();\n        ModuleSlimExecutor strategy = new ModuleSlimExecutor(null, null, null, null, config,\n            mockBaseDir(), null, mockLog());\n        URL resource = this.getClass().getClassLoader().getResource(\"excludes.txt\");\n        strategy.extensionExcludeAndIncludeArtifacts(resource.getPath());\n\n        assertTrue(config.getExcludes().contains(\"tracer-core:3.0.10\")\n                   && config.getExcludes().contains(\"tracer-core:3.0.11\"));\n    }\n\n    @Test\n    public void testLogExcludeMessage() throws URISyntaxException {\n        List<String> jarGroupIds = asList(\"com.alipay.sofa\", \"org.springframework\");\n        List<String> jarArtifactIds = asList(\"netty\");\n        List<String> jarList = asList(\"commons-io:commons-io:2.7\");\n\n        DefaultArtifact defaultArtifact = new DefaultArtifact(\"com.alipay.sofa\", \"artifactId\",\n            \"version\", \"compile\", \"jar\", null, new DefaultArtifactHandler());\n        DefaultArtifact defaultArtifact1 = new DefaultArtifact(\"io.netty\", \"netty\", \"version\",\n            \"compile\", \"jar\", null, new DefaultArtifactHandler());\n        DefaultArtifact defaultArtifact2 = new DefaultArtifact(\"commons-io\", \"commons-io\", \"2.7\",\n            \"compile\", \"jar\", null, new DefaultArtifactHandler());\n        Set<Artifact> artifacts = new HashSet<>();\n        artifacts.add(defaultArtifact);\n        artifacts.add(defaultArtifact1);\n        artifacts.add(defaultArtifact2);\n\n        ModuleSlimExecutor strategy = new ModuleSlimExecutor(null, null, null, null, null,\n            mockBaseDir(), null, mockLog());\n        strategy.logExcludeMessage(jarGroupIds, jarArtifactIds, jarList, artifacts, true);\n        strategy.logExcludeMessage(jarGroupIds, jarArtifactIds, jarList, artifacts, false);\n    }\n\n    @Test\n    public void testExtensionExcludeAndIncludeArtifactsFromUrl() throws URISyntaxException, Exception {\n        // 准备测试数据\n        DefaultArtifact defaultArtifact = new DefaultArtifact(\"groupId\", \"artifactId\", \"version\",\n            \"provided\", \"jar\", null, new DefaultArtifactHandler());\n        DefaultArtifact defaultArtifact1 = new DefaultArtifact(\"groupId\", \"artifactId\", \"version\",\n            \"provided\", \"jar\", null, new DefaultArtifactHandler());\n        Set<Artifact> artifacts = new HashSet<>();\n        artifacts.add(defaultArtifact);\n        artifacts.add(defaultArtifact1);\n\n        String packExcludesUrl = \"http://mock-server.com/excludes\";\n\n        // Mock HTTP响应\n        org.apache.http.client.methods.CloseableHttpResponse mockResponse = mock(org.apache.http.client.methods.CloseableHttpResponse.class);\n        org.apache.http.StatusLine mockStatusLine = mock(org.apache.http.StatusLine.class);\n        org.apache.http.HttpEntity mockEntity = mock(org.apache.http.HttpEntity.class);\n        \n        // 设置状态码为200\n        when(mockStatusLine.getStatusCode()).thenReturn(200);\n        when(mockResponse.getStatusLine()).thenReturn(mockStatusLine);\n        when(mockResponse.getEntity()).thenReturn(mockEntity);\n        \n        // 模拟返回的JSON数据\n        ModuleSlimExecutor.ExcludeConfig excludeConfig = new ModuleSlimExecutor.ExcludeConfig();\n        excludeConfig.setJarBlackGroupIds(Lists.newArrayList(\"com.test.black\"));\n        excludeConfig.setJarBlackArtifactIds(Lists.newArrayList(\"test-artifact-black\"));\n        excludeConfig.setJarBlackList(Lists.newArrayList(\"com.test.black:test-artifact-black:1.0\"));\n\n        excludeConfig.setJarWhiteGroupIds(Lists.newArrayList(\"com.test.white\"));\n        excludeConfig.setJarWhiteArtifactIds(Lists.newArrayList(\"test-artifact-white\"));\n        excludeConfig.setJarWhiteList(Lists.newArrayList(\"com.test.white:test-artifact-white:1.0\"));\n\n        excludeConfig.setJarWarnList(Lists.newArrayList(\"com.test.warn:test-artifact-warn:1.0\"));\n        excludeConfig.setJarWarnArtifactIds(Lists.newArrayList(\"test-artifact-warn\"));\n        excludeConfig.setJarWarnGroupIds(Lists.newArrayList(\"com.test.warn\"));\n\n        ModuleSlimExecutor.ExcludeConfigResponse excludeConfigResponse = new ModuleSlimExecutor.ExcludeConfigResponse();\n        excludeConfigResponse.setSuccess(true);\n        excludeConfigResponse.setResult(excludeConfig);\n        ObjectMapper objectMapper = new ObjectMapper();\n        String mockJsonResponse = objectMapper.writeValueAsString(excludeConfigResponse);\n        when(mockEntity.getContent()).thenReturn(new java.io.ByteArrayInputStream(mockJsonResponse.getBytes()));\n\n        // Mock HTTP客户端\n        org.apache.http.impl.client.CloseableHttpClient mockClient = mock(org.apache.http.impl.client.CloseableHttpClient.class);\n        when(mockClient.execute(org.mockito.ArgumentMatchers.any(org.apache.http.client.methods.HttpGet.class)))\n            .thenReturn(mockResponse);\n\n        // 使用Mockito的静态方法mock功能\n        try (org.mockito.MockedStatic<org.apache.http.impl.client.HttpClients> httpClientsMock = \n                org.mockito.Mockito.mockStatic(org.apache.http.impl.client.HttpClients.class)) {\n            \n            httpClientsMock.when(org.apache.http.impl.client.HttpClients::createDefault)\n                .thenReturn(mockClient);\n\n            ModuleSlimConfig config = new ModuleSlimConfig();\n            ModuleSlimExecutor strategy = new ModuleSlimExecutor(null, null, null, null,\n                config, mockBaseDir(), null, mockLog());\n            \n            // 执行测试方法\n            strategy.extensionExcludeArtifactsFromUrl(packExcludesUrl, artifacts);\n            \n            // 验证配置是否正确设置\n            assertTrue(config.getExcludeGroupIds().contains(\"com.test.black\"));\n            assertTrue(config.getExcludeArtifactIds().contains(\"test-artifact-black\"));\n            assertTrue(config.getExcludes().contains(\"com.test.black:test-artifact-black:1.0\"));\n            assertTrue(config.getIncludeGroupIds().contains(\"com.test.white\"));\n            assertTrue(config.getIncludeArtifactIds().contains(\"test-artifact-white\"));\n            assertTrue(config.getIncludes().contains(\"com.test.white:test-artifact-white:1.0\"));\n        }\n    }\n\n    @Test\n    public void testLogExcludeMessageWithMoreCases() throws URISyntaxException {\n        List<String> jarGroupIds = new ArrayList<>();\n        jarGroupIds.add(\"group1*\");\n        jarGroupIds.add(\"group2.*\");\n\n        List<String> jarArtifactIds = new ArrayList<>();\n        jarArtifactIds.add(\"artifact1*\");\n        jarArtifactIds.add(\"artifact2.g.*\");\n\n        List<String> jarList = new ArrayList<>();\n        Set<Artifact> artifacts = new HashSet<>();\n        Artifact artifact = new DefaultArtifact(\"group1.a.b\", \"artifact1gkl\", \"1.0\", \"test\", \"\",\n            null, new DefaultArtifactHandler());\n        artifact.setFile(new File(\"./\"));\n        artifacts.add(artifact);\n        artifact = new DefaultArtifact(\"group2.c\", \"artifact1gkl\", \"1.0\", \"\", \"\", null,\n            new DefaultArtifactHandler());\n        artifact.setFile(new File(\"./\"));\n        artifacts.add(artifact);\n        artifact = new DefaultArtifact(\"group3\", \"artifact1.e\", \"1.0\", \"\", \"\", null,\n            new DefaultArtifactHandler());\n        artifact.setFile(new File(\"./\"));\n        artifacts.add(artifact);\n        artifact = new DefaultArtifact(\"group3\", \"artifact2.g.h\", \"1.0\", \"\", \"\", null,\n            new DefaultArtifactHandler());\n        artifact.setFile(new File(\"./\"));\n        artifacts.add(artifact);\n\n        ModuleSlimExecutor strategy = new ModuleSlimExecutor(null, null, null, null, null,\n            mockBaseDir(), null, mockLog());\n        strategy.logExcludeMessage(jarGroupIds, jarArtifactIds, jarList, artifacts, true);\n        strategy.logExcludeMessage(jarGroupIds, jarArtifactIds, jarList, artifacts, false);\n    }\n\n    @Test\n    public void testExcludeWithoutItsDependencies() throws URISyntaxException {\n        MavenProject proj = mock(MavenProject.class);\n        Artifact a1 = mockArtifact(\"com.exclude\", \"a1\", \"1.0.0\", \"jar\", null, \"compile\");\n        Artifact a2 = mockArtifact(\"com.exclude.group.id\", \"a2\", \"1.0.0\", \"jar\", null, \"compile\");\n        Artifact a3 = mockArtifact(\"com.exclude.artifact.id\", \"a3\", \"1.0.0\", \"jar\", null, \"compile\");\n        Artifact a4 = mockArtifact(\"com.include\", \"a4\", \"1.0.0\", \"jar\", null, \"compile\");\n        Set<Artifact> artifacts = Sets.newHashSet(a1, a2, a3, a4);\n\n        ModuleSlimConfig moduleSlimConfig = new ModuleSlimConfig();\n        moduleSlimConfig.setExcludes(Sets.newLinkedHashSet(Collections\n            .singletonList(\"com.exclude:a1\")));\n        moduleSlimConfig.setExcludeGroupIds(Sets.newLinkedHashSet(Collections\n            .singletonList(\"com.exclude.group.id\")));\n        moduleSlimConfig.setExcludeArtifactIds(Sets.newLinkedHashSet(Collections\n            .singletonList(\"a3\")));\n        moduleSlimConfig.setExcludeWithIndirectDependencies(false);\n\n        ModuleSlimExecutor strategy = spy(new ModuleSlimExecutor(proj, null, null, null,\n            moduleSlimConfig, mockBaseDir(), null, mockLog()));\n        Set<Artifact> res = strategy.getArtifactsToFilterByExcludeConfig(artifacts);\n        assertEquals(3, res.size());\n    }\n\n    @Test\n    public void testExcludeWithIndirectDependencies() throws URISyntaxException {\n        MavenProject proj = mock(MavenProject.class);\n        Artifact a1 = mockArtifact(\"com.exclude\", \"a1\", \"1.0.0\", \"jar\", null, \"compile\");\n        Artifact a2 = mockArtifact(\"com.exclude.group.id\", \"a2\", \"1.0.0\", \"jar\", null, \"compile\");\n        Artifact a3 = mockArtifact(\"com.exclude.artifact.id\", \"a3\", \"1.0.0\", \"jar\", null, \"compile\");\n        Artifact a4 = mockArtifact(\"com.include\", \"a4\", \"1.0.0\", \"jar\", null, \"compile\");\n        Artifact d1 = mockArtifact(\"com.exclude.dependency\", \"d1\", \"1.0.0\", \"jar\", null, \"compile\");\n        Artifact d2 = mockArtifact(\"com.exclude.dependency\", \"d2\", \"1.0.0\", \"jar\", null, \"compile\");\n        Artifact d3 = mockArtifact(\"com.exclude.dependency\", \"d3\", \"1.0.0\", \"jar\", null, \"compile\");\n        Artifact d4 = mockArtifact(\"com.exclude.dependency\", \"d4\", \"1.0.0\", \"jar\", null, \"compile\");\n        Artifact d5 = mockArtifact(\"com.include.dependency\", \"d5\", \"1.0.0\", \"jar\", null, \"compile\");\n\n\n        Set<Artifact> artifacts = Sets.newHashSet(a1, a2, a3, a4, d1,d2,d3,d4,d5);\n\n        ModuleSlimConfig moduleSlimConfig = new ModuleSlimConfig();\n        moduleSlimConfig.setExcludes(Sets.newLinkedHashSet(Collections.singletonList(\"com.exclude:a1\")));\n        moduleSlimConfig.setExcludeGroupIds(Sets.newLinkedHashSet(Collections.singletonList(\"com.exclude.group.id\")));\n        moduleSlimConfig.setExcludeArtifactIds(Sets.newLinkedHashSet(Collections.singletonList(\"a3\")));\n        moduleSlimConfig.setExcludeWithIndirectDependencies(true);\n\n        /*\n         * 依赖关系如下：\n         *       -> a1 -> d1 -> d2\n         *     /\n         * root  -> a2 -> a3 -> d3\n         *     \\            \\\n         *      \\            -> d4\n         *       -> a4 -> d5\n         * 在此依赖关系下，a1, a2, a3 会因为 exclude 被排除\n         * d1, d2, d3, d4 会因为 excludeWithDependencies 被排除\n         * a4, d5 不会被排除\n         */\n        DependencyNode root = mockNode(mockArtifact(\"com.mock\", \"root\", \"1.0\", \"jar\", null, \"compile\"));\n        DependencyNode a1Node = mockNode(a1);\n        DependencyNode a2Node = mockNode(a2);\n        DependencyNode a3Node = mockNode(a3);\n        DependencyNode a4Node = mockNode(a4);\n        DependencyNode d1Node = mockNode(d1);\n        DependencyNode d2Node = mockNode(d2);\n        DependencyNode d3Node = mockNode(d3);\n        DependencyNode d4Node = mockNode(d4);\n        DependencyNode d5Node = mockNode(d5);\n\n        when(root.getChildren()).thenReturn(Lists.newArrayList(a1Node, a2Node,a4Node));\n        when(a1Node.getChildren()).thenReturn(Lists.newArrayList(d1Node));\n        when(d1Node.getChildren()).thenReturn(Lists.newArrayList(d2Node));\n        when(a2Node.getChildren()).thenReturn(Lists.newArrayList(a3Node));\n        when(a3Node.getChildren()).thenReturn(Lists.newArrayList(d3Node,d4Node));\n        when(a4Node.getChildren()).thenReturn(Lists.newArrayList(d5Node));\n        when(d2Node.getChildren()).thenReturn(Lists.newArrayList());\n        when(d3Node.getChildren()).thenReturn(Lists.newArrayList(d4Node));\n        when(d4Node.getChildren()).thenReturn(Lists.newArrayList());\n\n\n        ModuleSlimExecutor strategy = spy(new ModuleSlimExecutor(proj,null,null, root, moduleSlimConfig,\n                mockBaseDir(), null, mockLog()));\n        Set<Artifact> res = strategy.getArtifactsToFilterByExcludeConfig(artifacts);\n        assertEquals(7, res.size());\n\n        Set<String> resIdentities = res.stream().map(Artifact::getArtifactId).collect(Collectors.toSet());\n        assertTrue(resIdentities.contains(\"a1\"));\n        assertTrue(resIdentities.contains(\"a2\"));\n        assertTrue(resIdentities.contains(\"a3\"));\n        assertTrue(resIdentities.contains(\"d1\"));\n        assertTrue(resIdentities.contains(\"d2\"));\n        assertTrue(resIdentities.contains(\"d3\"));\n        assertTrue(resIdentities.contains(\"d4\"));\n    }\n\n    private DependencyNode mockNode(Artifact artifact) {\n        DependencyNode node = mock(DependencyNode.class);\n        when(node.getArtifact()).thenReturn(artifact);\n        return node;\n    }\n\n    private Artifact mockArtifact(String groupId, String artifactId, String version, String type,\n                                  String classifier, String scope) {\n        Artifact artifact = mock(Artifact.class);\n        when(artifact.getArtifactId()).thenReturn(artifactId);\n        when(artifact.getGroupId()).thenReturn(groupId);\n        when(artifact.getVersion()).thenReturn(version);\n        when(artifact.getType()).thenReturn(type);\n        when(artifact.getClassifier()).thenReturn(classifier);\n        when(artifact.getScope()).thenReturn(scope);\n        return artifact;\n    }\n\n    private MavenProject getMockBootstrapProject() throws URISyntaxException {\n        MavenProject project = new MavenProject();\n        project.setArtifactId(\"base-bootstrap\");\n        project.setGroupId(\"com.mock\");\n        project.setVersion(\"0.0.1-SNAPSHOT\");\n        project.setPackaging(\"jar\");\n\n        Artifact artifact = mock(Artifact.class);\n        when(artifact.getArtifactId()).thenReturn(\"base-bootstrap\");\n        when(artifact.getGroupId()).thenReturn(\"com.mock\");\n        when(artifact.getVersion()).thenReturn(\"0.0.1-SNAPSHOT\");\n\n        project.setArtifact(artifact);\n\n        project.setParent(getRootProject());\n\n        Model model = new Model();\n        model.setGroupId(\"com.mock\");\n        model.setArtifactId(\"base-bootstrap\");\n        model.setVersion(\"0.0.1-SNAPSHOT\");\n        DependencyManagement dependencyManagement = new DependencyManagement();\n        Dependency d = new Dependency();\n        d.setType(\"pom\");\n        d.setScope(\"import\");\n        d.setGroupId(\"com.mock\");\n        d.setArtifactId(\"base-plugin-bom\");\n        d.setVersion(\"1.0\");\n        dependencyManagement.setDependencies(Lists.newArrayList(d));\n        model.setDependencyManagement(dependencyManagement);\n        project.setOriginalModel(model);\n\n        setField(\"basedir\", project, CommonUtils.getResourceFile(\"baseDir\"));\n        return project;\n    }\n\n    private MavenProject getRootProject() {\n        MavenProject project = new MavenProject();\n        project.setArtifactId(\"base-dependencies-starter\");\n        project.setGroupId(\"com.mock\");\n        project.setVersion(\"1.0\");\n        project.setPackaging(\"pom\");\n\n        Artifact artifact = mock(Artifact.class);\n        when(artifact.getArtifactId()).thenReturn(\"base-dependencies-starter\");\n        when(artifact.getGroupId()).thenReturn(\"com.mock\");\n        when(artifact.getVersion()).thenReturn(\"1.0\");\n        when(artifact.getBaseVersion()).thenReturn(\"1.0\");\n\n        project.setArtifact(artifact);\n        project.setParent(null);\n\n        Dependency sameDependency = new Dependency();\n        sameDependency.setArtifactId(\"same-dependency-artifact\");\n        sameDependency.setGroupId(\"com.mock\");\n        sameDependency.setVersion(\"1.0-SNAPSHOT\");\n\n        Dependency differenceDependency = new Dependency();\n        differenceDependency.setArtifactId(\"difference-dependency-artifact\");\n        differenceDependency.setGroupId(\"com.mock\");\n        differenceDependency.setVersion(\"1.0-SNAPSHOT\");\n\n        DependencyManagement dm = new DependencyManagement();\n        dm.setDependencies(Lists.newArrayList(sameDependency, differenceDependency));\n\n        Model pom = new Model();\n        pom.setDependencyManagement(dm);\n\n        project.setOriginalModel(pom);\n        return project;\n    }\n\n    private Log mockLog() {\n        Log log = mock(Log.class);\n        doNothing().when(log).info(anyString());\n        return log;\n    }\n\n    private File mockBaseDir() throws URISyntaxException {\n        return CommonUtils.getResourceFile(\"baseDir\");\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-maven-plugin/src/test/java/com/alipay/sofa/ark/boot/mojo/ReflectionUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.boot.mojo;\n\nimport java.lang.reflect.Field;\n\n/**\n * @author lianglipeng.llp@alibaba-inc.com\n * @version $Id: ReflectionUtils.java, v 0.1 2024年07月24日 19:37 立蓬 Exp $\n */\npublic class ReflectionUtils {\n\n    public static <T> void setField(String fieldName, Object o, T value) {\n        Class<?> klass = o.getClass();\n        while (klass != null) {\n            try {\n                Field f = klass.getDeclaredField(fieldName);\n                f.setAccessible(true);\n                f.set(o, value);\n                return;\n            } catch (Exception e) {\n                klass = klass.getSuperclass();\n            }\n        }\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-maven-plugin/src/test/java/com/alipay/sofa/ark/boot/mojo/RepackageMojoTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.boot.mojo;\n\nimport com.alipay.sofa.ark.boot.mojo.ModuleSlimExecutor.ExcludeConfig;\nimport com.alipay.sofa.ark.boot.mojo.ModuleSlimExecutor.ExcludeConfigResponse;\nimport com.alipay.sofa.ark.tools.ArtifactItem;\nimport com.google.common.collect.Lists;\nimport com.google.common.io.Files;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.io.FileUtils;\nimport org.apache.maven.artifact.Artifact;\nimport org.apache.maven.artifact.DefaultArtifact;\nimport org.apache.maven.artifact.handler.DefaultArtifactHandler;\nimport org.apache.maven.execution.DefaultMavenExecutionRequest;\nimport org.apache.maven.execution.MavenExecutionRequest;\nimport org.apache.maven.execution.MavenSession;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugin.descriptor.PluginDescriptor;\nimport org.apache.maven.project.DefaultProjectBuildingRequest;\nimport org.apache.maven.project.MavenProject;\nimport org.apache.maven.project.MavenProjectHelper;\nimport org.apache.maven.repository.RepositorySystem;\nimport org.apache.maven.settings.Settings;\nimport org.apache.maven.shared.dependency.graph.DependencyNode;\nimport org.apache.maven.shared.dependency.graph.internal.DefaultDependencyNode;\nimport org.apache.maven.shared.invoker.DefaultInvocationRequest;\nimport org.apache.maven.shared.invoker.InvocationRequest;\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.net.URISyntaxException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport static com.alipay.sofa.ark.boot.mojo.RepackageMojo.ArkConstants.getClassifier;\nimport static java.lang.System.clearProperty;\nimport static java.lang.System.setProperty;\nimport static org.apache.commons.beanutils.BeanUtils.copyProperties;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\n/**\n * @author guolei.sgl (guolei.sgl@antfin.com) 2020/12/16 2:25 下午\n * @since\n **/\npublic class RepackageMojoTest {\n\n    @After\n    public void tearDown() {\n        clearProperty(\"maven.home\");\n    }\n\n    /**\n     * 测试依赖解析\n     *\n     * @throws NoSuchMethodException\n     * @throws InvocationTargetException\n     * @throws IllegalAccessException\n     */\n    @Test\n    public void testParseArtifactItems() throws NoSuchMethodException, InvocationTargetException,\n                                        IllegalAccessException {\n\n        /**\n         *  构建依赖图\n         *\n         *                   biz-child1-child1\n         *                 /\n         *        biz-child1\n         *       /         \\\n         *     /            biz-child1-child2\n         *  biz\n         *     \\            biz-child2-child1\n         *      \\          /\n         *      biz-child2\n         *                 \\\n         *                  biz-child2-child1\n         *\n         */\n        DefaultDependencyNode bizNode = buildDependencyNode(null, \"com.alipay.sofa\", \"biz\", \"1.0.0\");\n        DefaultDependencyNode bizChild1 = buildDependencyNode(bizNode, \"com.alipay.sofa\",\n            \"biz-child1\", \"1.0.0\");\n        DefaultDependencyNode bizChild2 = buildDependencyNode(bizNode, \"com.alipay.sofa\",\n            \"biz-child2\", \"1.0.0\");\n        buildDependencyNode(bizChild1, \"com.alipay.sofa\", \"biz-child1-child1\", \"1.0.0\");\n        buildDependencyNode(bizChild1, \"com.alipay.sofa\", \"biz-child1-child2\", \"1.0.0\");\n        buildDependencyNode(bizChild2, \"com.alipay.sofa\", \"biz-child2-child1\", \"1.0.0\");\n        buildDependencyNode(bizChild2, \"com.alipay.sofa\", \"biz-child2-child2\", \"1.0.0\");\n\n        RepackageMojo repackageMojo = new RepackageMojo();\n        Method parseArtifactItems = repackageMojo.getClass().getDeclaredMethod(\n            \"parseArtifactItems\", DependencyNode.class, Set.class);\n        parseArtifactItems.setAccessible(true);\n        Set<ArtifactItem> artifactItems = new HashSet<>();\n        parseArtifactItems.invoke(repackageMojo, bizNode, artifactItems);\n        assertTrue(artifactItems.size() == 7);\n    }\n\n    private DefaultDependencyNode buildDependencyNode(DefaultDependencyNode parent, String groupId,\n                                                      String artifactId, String version) {\n        DefaultDependencyNode dependencyNode = new DefaultDependencyNode(parent,\n            new DefaultArtifact(groupId, artifactId, version, \"provided\", \"jar\", \"biz-jar\", null),\n            null, null, null);\n        if (parent != null) {\n            if (CollectionUtils.isEmpty(parent.getChildren())) {\n                parent.setChildren(Lists.newArrayList());\n            }\n            parent.getChildren().add(dependencyNode);\n        }\n        return dependencyNode;\n    }\n\n    @Test\n    public void testSetSettingsLocation() throws Exception {\n        String userSettingsFilePath = System.getProperty(\"user.home\") + File.separator\n                                      + \"user-settings-test.xml\";\n        String globalSettingsFilePath = System.getProperty(\"user.home\") + File.separator\n                                        + \"global-settings-test.xml\";\n        File userSettingsFile = com.alipay.sofa.ark.common.util.FileUtils\n            .file(userSettingsFilePath);\n        File globalSettingsFile = com.alipay.sofa.ark.common.util.FileUtils\n            .file(globalSettingsFilePath);\n        InvocationRequest request = new DefaultInvocationRequest();\n        invokeSetSettingsLocation(request, userSettingsFilePath, globalSettingsFilePath);\n        Assert.assertNull(request.getUserSettingsFile());\n        Assert.assertNull(request.getGlobalSettingsFile());\n\n        Files.touch(globalSettingsFile);\n        invokeSetSettingsLocation(request, userSettingsFilePath, globalSettingsFilePath);\n        Assert.assertNull(request.getUserSettingsFile());\n        assertNotNull(request.getGlobalSettingsFile());\n\n        Files.touch(userSettingsFile);\n        invokeSetSettingsLocation(request, userSettingsFilePath, globalSettingsFilePath);\n        assertNotNull(request.getUserSettingsFile());\n        assertNotNull(request.getGlobalSettingsFile());\n\n        FileUtils.deleteQuietly(userSettingsFile);\n        FileUtils.deleteQuietly(globalSettingsFile);\n    }\n\n    private void invokeSetSettingsLocation(InvocationRequest request, String userSettingsFilePath,\n                                           String globalSettingsFilePath) throws Exception {\n        RepackageMojo repackageMojo = new RepackageMojo();\n        MavenExecutionRequest executionRequest = new DefaultMavenExecutionRequest();\n        executionRequest.setUserSettingsFile(com.alipay.sofa.ark.common.util.FileUtils\n            .file(userSettingsFilePath));\n        executionRequest.setGlobalSettingsFile(com.alipay.sofa.ark.common.util.FileUtils\n            .file(globalSettingsFilePath));\n        // 构造对象\n        MavenSession mavenSession = new MavenSession(null, executionRequest, null,\n            new ArrayList<>());\n        Field mavenSessionField = repackageMojo.getClass().getDeclaredField(\"mavenSession\");\n        mavenSessionField.setAccessible(true);\n        mavenSessionField.set(repackageMojo, mavenSession);\n\n        Method setSettingsLocation = repackageMojo.getClass().getDeclaredMethod(\n            \"setSettingsLocation\", InvocationRequest.class);\n        setSettingsLocation.setAccessible(true);\n        setSettingsLocation.invoke(repackageMojo, request);\n    }\n\n    @Test\n    public void testIsSameWithVersion() {\n        ArtifactItem artifactItem = new ArtifactItem();\n        artifactItem.setGroupId(\"groupId1\");\n        artifactItem.setArtifactId(\"artifactId\");\n        artifactItem.setVersion(\"1.1.1\");\n        Assert.assertFalse(artifactItem.isSameWithVersion(null));\n        ArtifactItem artifactItem1 = new ArtifactItem();\n        artifactItem1.setGroupId(\"groupId1\");\n        artifactItem1.setArtifactId(\"artifactId\");\n        artifactItem1.setVersion(\"1.1.1\");\n        assertTrue(artifactItem.isSameWithVersion(artifactItem1));\n        artifactItem1.setVersion(\"2.2.2\");\n        Assert.assertFalse(artifactItem.isSameWithVersion(artifactItem1));\n        artifactItem1.setVersion(\"*\");\n        assertTrue(artifactItem.isSameWithVersion(artifactItem1));\n    }\n\n    @Test\n    public void testExecute() throws Exception {\n\n        RepackageMojo repackageMojo = new RepackageMojo();\n        // 1) test war maven project packaging\n        MavenProject mavenProject = new MavenProject();\n        mavenProject.setPackaging(\"war\");\n        Field field = RepackageMojo.class.getDeclaredField(\"mavenProject\");\n        field.setAccessible(true);\n        field.set(repackageMojo, mavenProject);\n        repackageMojo.execute();\n\n        // 2) test pom maven project packaging\n        mavenProject.setPackaging(\"pom\");\n        repackageMojo.execute();\n\n        // 3) test arkClassifier equals bizClassifier\n        field = RepackageMojo.class.getDeclaredField(\"arkClassifier\");\n        field.setAccessible(true);\n        field.set(repackageMojo, \"aaa\");\n        field = RepackageMojo.class.getDeclaredField(\"bizClassifier\");\n        field.setAccessible(true);\n        field.set(repackageMojo, \"aaa\");\n        mavenProject.setPackaging(\"jar\");\n        repackageMojo.execute();\n\n        // 4) test arkClassifier not equals bizClassifier\n        field = RepackageMojo.class.getDeclaredField(\"bizClassifier\");\n        field.setAccessible(true);\n        field.set(repackageMojo, \"bbb\");\n        field = RepackageMojo.class.getDeclaredField(\"skip\");\n        field.setAccessible(true);\n        field.set(repackageMojo, true);\n        repackageMojo.execute();\n\n        // 5) test complicated artifacts with excludes, excludeGroupIds, excludeArtifactIds, declaredMode=true, attach=true config\n        field.set(repackageMojo, false);\n        mavenProject.setProjectBuildingRequest(new DefaultProjectBuildingRequest());\n\n        PluginDescriptor pluginDescriptor = new PluginDescriptor();\n        pluginDescriptor.setVersion(\"2.0\");\n        Map pluginContext = new HashMap<>();\n        pluginContext.put(\"pluginDescriptor\", pluginDescriptor);\n        repackageMojo.setPluginContext(pluginContext);\n\n        field = RepackageMojo.class.getDeclaredField(\"declaredMode\");\n        field.setAccessible(true);\n        field.set(repackageMojo, true);\n\n        DefaultArtifact artifact = new DefaultArtifact(\"group1\", \"artifact1\", \"1.0\", \"compile\", \"\",\n            null, new DefaultArtifactHandler());\n        artifact.setFile(com.alipay.sofa.ark.common.util.FileUtils.file(getClass().getClassLoader()\n            .getResource(\"excludes.txt\").getPath()));\n        mavenProject.setArtifact(artifact);\n\n        Set<Artifact> artifacts = new HashSet<>();\n        artifact = new DefaultArtifact(\"group1\", \"artifact2\", \"1.0\", \"compile\", \"\", \"jdk17\",\n            new DefaultArtifactHandler());\n        artifact.setFile(new File(\"./\"));\n        artifacts.add(artifact);\n        artifact = new DefaultArtifact(\"group1\", \"artifact3\", \"1.0\", \"compile\", \"\", null,\n            new DefaultArtifactHandler());\n        artifact.setFile(new File(\"./\"));\n        artifacts.add(artifact);\n        artifact = new DefaultArtifact(\"group2\", \"artifact1\", \"1.0\", \"compile\", \"\", null,\n            new DefaultArtifactHandler());\n        artifact.setFile(new File(\"./\"));\n        artifacts.add(artifact);\n        artifact = new DefaultArtifact(\"group2.a.b.b\", \"artifact4\", \"1.0\", \"compile\", \"\", null,\n            new DefaultArtifactHandler());\n        artifact.setFile(new File(\"./\"));\n        artifacts.add(artifact);\n        artifact = new DefaultArtifact(\"group3.c\", \"artifact5\", \"1.0\", \"compile\", \"\", null,\n            new DefaultArtifactHandler());\n        artifact.setFile(new File(\"./\"));\n        artifacts.add(artifact);\n        artifact = new DefaultArtifact(\"group3def\", \"artifact5\", \"1.0\", \"compile\", \"\", null,\n            new DefaultArtifactHandler());\n        artifact.setFile(new File(\"./\"));\n        artifacts.add(artifact);\n        artifact = new DefaultArtifact(\"group4\", \"artifact1.g.h.g\", \"1.0\", \"compile\", \"\", null,\n            new DefaultArtifactHandler());\n        artifact.setFile(new File(\"./\"));\n        artifacts.add(artifact);\n        artifact = new DefaultArtifact(\"group4\", \"artifact1.i\", \"1.0\", \"compile\", \"\", null,\n            new DefaultArtifactHandler());\n        artifact.setFile(new File(\"./\"));\n        artifacts.add(artifact);\n        artifact = new DefaultArtifact(\"group4\", \"artifact1gkl\", \"1.0\", \"compile\", \"\", null,\n            new DefaultArtifactHandler());\n        artifact.setFile(new File(\"./\"));\n        artifacts.add(artifact);\n        mavenProject.setArtifacts(artifacts);\n\n        Set<String> excludeGroupIds = new LinkedHashSet<>();\n        excludeGroupIds.add(\"group2.a.*\");\n        excludeGroupIds.add(\"group3d*\");\n        excludeGroupIds.add(\"group3.c\");\n        field = RepackageMojo.class.getDeclaredField(\"excludeGroupIds\");\n        field.setAccessible(true);\n        field.set(repackageMojo, excludeGroupIds);\n\n        Set<String> excludeArtifactIds = new LinkedHashSet<>();\n        excludeArtifactIds.add(\"artifact1.g.*\");\n        excludeArtifactIds.add(\"artifact1gk*\");\n        excludeArtifactIds.add(\"artifact1.i\");\n        field = RepackageMojo.class.getDeclaredField(\"excludeArtifactIds\");\n        field.setAccessible(true);\n        field.set(repackageMojo, excludeArtifactIds);\n\n        Set<String> excludes = new LinkedHashSet<>();\n        excludes.add(\"group1:artifact3:1.0\");\n        excludes.add(\"group1:artifact2:1.0:17\");\n        excludes.add(\"groupx:x:1.0\");\n        field = RepackageMojo.class.getDeclaredField(\"excludes\");\n        field.setAccessible(true);\n        field.set(repackageMojo, excludes);\n\n        field = RepackageMojo.class.getDeclaredField(\"attach\");\n        field.setAccessible(true);\n        field.set(repackageMojo, true);\n\n        field = RepackageMojo.class.getDeclaredField(\"outputDirectory\");\n        field.setAccessible(true);\n        field.set(repackageMojo, new File(\"./\"));\n\n        field = RepackageMojo.class.getDeclaredField(\"outputDirectory\");\n        field.setAccessible(true);\n        field.set(repackageMojo, new File(\"./\"));\n\n        RepositorySystem repositorySystem = mock(RepositorySystem.class);\n        field = RepackageMojo.class.getDeclaredField(\"repositorySystem\");\n        field.setAccessible(true);\n        field.set(repackageMojo, repositorySystem);\n\n        MavenSession mavenSession = mock(MavenSession.class);\n        when(mavenSession.getProjectBuildingRequest()).thenReturn(\n            new DefaultProjectBuildingRequest());\n\n        MavenExecutionRequest mavenExecutionRequest = new DefaultMavenExecutionRequest();\n        mavenExecutionRequest.setUserSettingsFile(new File(\"./\"));\n        mavenExecutionRequest.setGlobalSettingsFile(new File(\"./\"));\n        when(mavenSession.getRequest()).thenReturn(mavenExecutionRequest);\n\n        Settings settings = new Settings();\n        settings.setInteractiveMode(true);\n        settings.setActiveProfiles(new ArrayList<>());\n        when(mavenSession.getSettings()).thenReturn(settings);\n\n        field = RepackageMojo.class.getDeclaredField(\"mavenSession\");\n        field.setAccessible(true);\n        field.set(repackageMojo, mavenSession);\n\n        MavenProject parentMavenProject = new MavenProject();\n        parentMavenProject.setFile(new File(\"./a\"));\n        mavenProject.setParent(parentMavenProject);\n        setProperty(\"maven.home\", \"./\");\n\n        Exception exception = null;\n        try {\n            repackageMojo.execute();\n        } catch (MojoExecutionException mee) {\n            exception = mee;\n        }\n        assertNotNull(exception);\n\n        // 6) test with declaredMode=false\n        exception = null;\n        field = RepackageMojo.class.getDeclaredField(\"declaredMode\");\n        field.setAccessible(true);\n        field.set(repackageMojo, false);\n        try {\n            repackageMojo.execute();\n        } catch (MojoExecutionException mee) {\n            exception = mee;\n        }\n        assertNotNull(exception);\n\n        // 7) test updateArtifact with skipArkExecutable=false\n        MavenProjectHelper mavenProjectHelper = mock(MavenProjectHelper.class);\n        field = RepackageMojo.class.getDeclaredField(\"projectHelper\");\n        field.setAccessible(true);\n        field.set(repackageMojo, mavenProjectHelper);\n\n        Method method = RepackageMojo.class.getDeclaredMethod(\"updateArtifact\", File.class,\n            File.class);\n        method.setAccessible(true);\n        method.invoke(repackageMojo, new File(\"./\"), new File(\"./\"));\n\n        // 8) test updateArtifact with skipArkExecutable=true and keepArkBizJar=false\n        field = RepackageMojo.class.getDeclaredField(\"skipArkExecutable\");\n        field.setAccessible(true);\n        field.set(repackageMojo, true);\n        field = RepackageMojo.class.getDeclaredField(\"keepArkBizJar\");\n        field.setAccessible(true);\n        field.set(repackageMojo, true);\n        method.invoke(repackageMojo, new File(\"./\"), new File(\"./\"));\n    }\n\n    @Test\n    public void testInnerModelClass() throws InvocationTargetException, IllegalAccessException {\n        copyProperties(new ExcludeConfig(), new ExcludeConfig());\n        copyProperties(new ExcludeConfigResponse(), new ExcludeConfigResponse());\n        assertEquals(\"\", getClassifier());\n    }\n\n    @Test\n    public void testGetDeclaredLibrariesWhiteList() throws URISyntaxException, IOException {\n        RepackageMojo repackageMojo = new RepackageMojo();\n        ReflectionUtils.setField(\"declaredMode\", repackageMojo, true);\n        ReflectionUtils.setField(\"baseDir\", repackageMojo, CommonUtils.getResourceFile(\"baseDir\"));\n\n        Set<ArtifactItem> whitelist = repackageMojo.getDeclaredLibrariesWhitelist();\n\n        Set<String> res = whitelist.stream().map(it->it.getGroupId()+\":\"+it.getArtifactId()).collect(Collectors.toSet());\n        assertTrue(res.contains(\"com.ark.yml:ark-common-yml\"));\n        assertTrue(res.contains(\"com.biz.yml:biz-common-yml\"));\n        assertTrue(res.contains(\"com.biz:biz-common\"));\n        assertTrue(res.contains(\"com.ark:ark-common\"));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-maven-plugin/src/test/java/com/alipay/sofa/ark/boot/mojo/utils/ParseUtilsTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.boot.mojo.utils;\n\nimport com.alipay.sofa.ark.boot.mojo.CommonUtils;\nimport org.junit.Test;\nimport org.yaml.snakeyaml.Yaml;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.net.URISyntaxException;\nimport java.util.Map;\nimport java.util.Set;\n\nimport static com.alipay.sofa.ark.boot.mojo.utils.ParseUtils.getBooleanWithDefault;\nimport static com.alipay.sofa.ark.spi.constant.Constants.DECLARED_LIBRARIES_WHITELIST;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * @author lianglipeng.llp@alibaba-inc.com\n * @version $Id: ParseUtilsTest.java, v 0.1 2024年08月23日 10:55 立蓬 Exp $\n */\npublic class ParseUtilsTest {\n    @Test\n    public void testGetStringSetForYaml() throws URISyntaxException {\n        Map<String, Object> yml = (Map<String, Object>) loadYaml();\n        Set<String> excludes = ParseUtils.getStringSet(yml, \"excludes\");\n        Set<String> whitelist = ParseUtils.getStringSet(yml, DECLARED_LIBRARIES_WHITELIST);\n\n        assertTrue(excludes.contains(\"org.apache.commons:commons-lang3-yml\"));\n        assertTrue(excludes.contains(\"commons-beanutils:commons-beanutils-yml\"));\n\n        assertTrue(whitelist.contains(\"com.biz.yml:biz-common-yml\"));\n        assertTrue(whitelist.contains(\"com.ark.yml:ark-common-yml\"));\n    }\n\n    @Test\n    public void testGetBooleanWithDefault() throws URISyntaxException {\n        Map<String, Object> yml = (Map<String, Object>) loadYaml();\n        assertFalse(getBooleanWithDefault(yml, \"excludeWithIndirectDependencies\", true));\n        assertTrue(getBooleanWithDefault(yml, \"aaa\", true));\n    }\n\n    private Object loadYaml() throws URISyntaxException {\n        File yml = CommonUtils.getResourceFile(\"baseDir/conf/ark/bootstrap.yml\");\n        try (FileInputStream fis = new FileInputStream(yml)) {\n            Yaml yaml = new Yaml();\n            return yaml.load(fis);\n        } catch (FileNotFoundException e) {\n            throw new RuntimeException(e);\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-maven-plugin/src/test/resources/baseDir/conf/ark/bootstrap.properties",
    "content": "# excludes config {groupId:artifactId} {groupId:artifactId:version} or {groupId:artifactId:version:classifier}, split by ','\nexcludes=org.apache.commons:commons-lang3,commons-beanutils:commons-beanutils\n# excludeGroupIds config ${groupId}, split by ','\nexcludeGroupIds=org.springframework\n# excludeArtifactIds config ${artifactId}, split by ','\nexcludeArtifactIds=sofa-ark-spi\n\n# includes config {groupId:artifactId} {groupId:artifactId:version} or {groupId:artifactId:version:classifier}, split by ','\nincludes=com.alipay.sofa:sofa-ark-all\n# includeGroupIds config ${groupId}, split by ','\nincludeGroupIds=com.alipay.sofa\n# includeArtifactIds config ${artifactId}, split by ','\nincludeArtifactIds=sofa-ark-all\n\n# declared libraries whitelist config {groupId:artifactId}, split by ','\ndeclared.libraries.whitelist=com.ark:ark-common,com.biz:biz-common\n\n\nexcludeWithIndirectDependencies=false"
  },
  {
    "path": "sofa-ark-parent/support/ark-maven-plugin/src/test/resources/baseDir/conf/ark/bootstrap.yml",
    "content": "# excludes 中配置 groupId:artifactId} {groupId:artifactId:version} 或 {groupId:artifactId:version:classifier}, 不同依赖以 - 隔开\n# excludeGroupIds 中配置 ${groupId}, 不同依赖以 - 隔开\n# excludeArtifactIds 中配置 ${artifactId}, 不同依赖以 - 隔开\nexcludes:\n  - org.apache.commons:commons-lang3-yml\n  - commons-beanutils:commons-beanutils-yml\nexcludeGroupIds:\n  - org.springframework-yml\nexcludeArtifactIds:\n  - sofa-ark-spi-yml\nincludes:\n  - com.alipay.sofa:sofa-ark-all-yml\nincludeGroupIds:\n  - com.alipay.sofa\nincludeArtifactIds:\n  - sofa-ark-all-yml\n\n# declaredArtifactIds 中配置 ${artifactId}, 不同依赖以 - 隔开\n\ndeclared:\n  libraries:\n    whitelist:\n      - com.ark.yml:ark-common-yml\n      - com.biz.yml:biz-common-yml\n\nexcludeWithIndirectDependencies: false"
  },
  {
    "path": "sofa-ark-parent/support/ark-maven-plugin/src/test/resources/dependency-tree-mock.txt",
    "content": "--- dependency:3.6.0:tree (default-cli) @ sofa-ark-springboot-starter ---\n[INFO] com.alipay.sofa:sofa-ark-springboot-starter:jar:2.2.4-SNAPSHOT\n[INFO] +- org.springframework.boot:spring-boot:jar:2.7.14:provided\n[INFO] |  +- org.springframework:spring-core:jar:5.3.29:provided\n[INFO] |  |  \\- org.springframework:spring-jcl:jar:5.3.29:provided\n[INFO] |  \\- org.springframework:spring-context:jar:5.3.29:provided\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-maven-plugin/src/test/resources/excludes.txt",
    "content": "excludes=tracer-core:3.0.10\nexcludes=tracer-core:3.0.11\nexcludeGroupIds=com.alipay.sofa\nexcludeGroupIds=org.springframework.boot\nexcludeArtifactIds=tracer-core\nexcludeArtifactIds=tracer-extension"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-gradle-plugin/README.md",
    "content": "# Ark Plugin Gradle打包插件使用\n`sofa-ark-plugin-gradle-plugin`模块是Ark Plugin打包工具的Gradle版本实现，和Maven打包工具`sofa-ark-plugin-maven-plugin`有同样的功能。在后文，使用**Gradle插件**来指代`sofa-ark-plugin-gradle-plugin`。\n\n本小节会对**Gradle插件**进行介绍，随后会展示如何使用**Gradle插件**打包Ark Plugin。\n\n### 配置项\n**Gradle插件**提供了和Maven基本相同的配置项，在使用上略有不同，想要使用配置项，需要在打包的项目build.gradle使用arkPlugin：\n\n```\narkPlugin{\n    //具体配置项\n}\n```\n\n- activator使用\n```\nactivator = 'sample.activator.SamplePluginActivator'\n```\n\n- excludes使用\n```\nexcludes = ['com.fasterxml.jackson.module:jackson-module-parameter-names', 'org.example:common']\n```\n\n- exported使用\n```\nexported {\n    packages = [\n            'com.alipay.sofa.ark.sample.common'\n\n    ]\n    classes = [\n            'sample.facade.SamplePluginService'\n    ]\n\n    resource = [\n        'META-INF/spring/bean.xml'\n    ]\n\n}\n```\n\n\n### 引入\n引入方式可以分为两种：\n1. 本地引入\n2. 远程引入（正在申请）\n\n本地引入的方式是将Gradle插件发布到本地的Maven仓库中，之后使用Gradle进行加载。\n\n#### 将Gradle插件发布到本地仓库\n1. 在**Gradle插件**的build.gradle的plugin解开注释，如下所示：\n```\nplugins {\n    id 'java'\n    id 'java-gradle-plugin'\n\n//    本地调试用，发布到maven\n    id 'maven-publish'\n}\n```\n\n2. 配置publish\n   在build.gradle中增加如下内容：\n```\npublishing {\n    // 配置Plugin GAV\n    publications {\n        maven(MavenPublication) {\n            groupId = group\n            artifactId = 'plugin'\n            version = version\n            from components.java\n        }\n    }\n    // 配置仓库地址\n    repositories {\n        maven {\n            url file('E:/repo')\n        }\n\n    }\n}\n```\n点击在IDEA的右侧Gradle中的 Tasks > publishing > publish 将插件发布到本地仓库。\n\n#### 在本地项目中引入\n\n1. 在Gradle项目根目录的setting.gradle中设置pluginManagement\n\n```\n pluginManagement {\n    repositories {\n        // 指定maven仓库\n        maven {\n            url \"file:///E:/repo\"\n        }\n    }\n}\n```\n\n2. 在需要打包的项目中的build.gradle进行如下的配置\n\n```\nbuildscript {\n    repositories {\n        // ...\n        maven {\n            url \"file:///E:/repo\"\n        }\n\n    }\n    dependencies {\n        classpath(\"sofa.ark.gradle:sofa-ark-gradle-plugin:1.1\")\n    }\n}\n\nplugins {\n    id 'sofa.ark.gradle.plugin' version \"1.1\"\n}\n```\n\n3. 增加配置\n\n在需要打包的项目中的build.gradle创建配置项：\n\n```\narkPlugin{\n    outputDirectory = layout.buildDirectory.dir(\"custom-output\")\n\n    activator = 'sample.activator.SamplePluginActivator'\n    excludes = ['com.fasterxml.jackson.module:jackson-module-parameter-names']\n\n    shades = [\n            'org.example:common:1.0'\n    ]\n    exported {\n        packages = [\n                'com.alipay.sofa.ark.sample.common'\n\n        ]\n        classes = [\n                'sample.facade.SamplePluginService'\n        ]\n\n    }\n}\n\n```\n\n使用Gradle刷新后，如果一切正常，会在IDEA右侧Gradle任务列表中出现arkPluginJar，具体如下： Tasks > build > arkPluginJar，点击arkPluginJa执行，会在指定的outputDirectory中输出Ark Plugin包。"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-gradle-plugin/build.gradle",
    "content": "plugins {\n    id 'java'\n    id 'java-gradle-plugin'\n    id 'maven-publish'\n}\n\next {\n    arkPluginGradlePluginId = 'sofa-ark-plugin-gradle-plugin'\n}\n\ngroup = 'com.alipay.sofa'\nversion = '1.0.0'\nsourceCompatibility = '1.8'\n\ngradlePlugin {\n    plugins {\n        DependenciesPlugin{\n            id = arkPluginGradlePluginId\n            implementationClass = 'com.alipay.sofa.ark.boot.mojo.ArkPlugin'\n        }\n    }\n}\n\nrepositories {\n    mavenLocal()\n    mavenCentral()\n}\n\ndependencies {\n    implementation 'org.apache.commons:commons-compress:1.26.0'\n    testImplementation 'junit:junit:4.13.1'\n    testRuntimeOnly(\"org.junit.vintage:junit-vintage-engine:5.8.2\")\n}\n\npublishing {\n    publications {\n        maven(MavenPublication) {\n            from components.java\n            groupId = project.group\n            artifactId = arkPluginGradlePluginId\n            version = project.version\n        }\n    }\n    // publish to local maven repository\n    repositories {\n        maven {\n            mavenLocal()\n        }\n    }\n}\n\ntasks.named('test') {\n    useJUnitPlatform()\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ArkPlugin.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.boot.mojo;\n\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\n\npublic class ArkPlugin implements Plugin<Project> {\n\n    @Override\n    public void apply(Project project) {\n        project.getExtensions().create(\"arkPlugin\", ArkPluginExtension.class, project);\n        project.getTasks().register(\"arkPluginJar\", ArkPluginJarTask.class,\n            task -> {\n                task.setGroup(\"build\");\n                task.setDescription(\"Generates an Ark plugin JAR file\");\n            }\n        );\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ArkPluginCopyAction.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.boot.mojo;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Set;\nimport java.util.zip.CRC32;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\nimport org.apache.commons.compress.archivers.zip.UnixStat;\nimport org.apache.commons.compress.archivers.zip.ZipArchiveEntry;\nimport org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;\nimport org.gradle.api.internal.file.CopyActionProcessingStreamAction;\nimport org.gradle.api.internal.file.copy.CopyAction;\nimport org.gradle.api.internal.file.copy.CopyActionProcessingStream;\nimport org.gradle.api.internal.file.copy.FileCopyDetailsInternal;\nimport org.gradle.api.tasks.WorkResult;\nimport org.gradle.api.tasks.WorkResults;\n\npublic class ArkPluginCopyAction implements CopyAction {\n    private final File jarFile;\n\n    private final Set<File> shadeFiles;\n\n    public ArkPluginCopyAction(File jarFile, Set<File> shadeFiles) {\n        this.jarFile = jarFile;\n        this.shadeFiles = shadeFiles;\n    }\n\n    @Override\n    public WorkResult execute(CopyActionProcessingStream stream) {\n        try (ZipArchiveOutputStream zipStream = new ZipArchiveOutputStream(jarFile)) {\n            zipStream.setEncoding(String.valueOf(StandardCharsets.UTF_8));\n            StreamAction action = new StreamAction(zipStream, shadeFiles);\n            stream.process(action);\n            return WorkResults.didWork(true);\n        } catch (IOException e) {\n            throw new RuntimeException(\"Failed to create JAR file\", e);\n        }\n    }\n\n    private static class StreamAction implements CopyActionProcessingStreamAction {\n        private final ZipArchiveOutputStream zipStream;\n\n        private final Set<File> shadeFiles;\n        StreamAction(ZipArchiveOutputStream zipStream, Set<File> shadeFiles) {\n            this.zipStream = zipStream;\n            this.shadeFiles = shadeFiles;\n        }\n\n        @Override\n        public void processFile(FileCopyDetailsInternal details) {\n            try {\n                if (details.isDirectory()) {\n                    addDirectory(details);\n                } else if (shadeFiles.contains(details.getFile())) {\n                    addShadeFileContents(details.getFile());\n                } else {\n                    addFile(details);\n                }\n            } catch (IOException e) {\n                throw new RuntimeException(\"Failed to add file to JAR: \" + details.getPath(), e);\n            }\n        }\n\n        private void addDirectory(FileCopyDetailsInternal details) throws IOException {\n            ZipArchiveEntry entry = createEntry(details);\n            zipStream.putArchiveEntry(entry);\n            zipStream.closeArchiveEntry();\n        }\n\n        private void addShadeFileContents(File shadeFile) throws IOException {\n            try (ZipFile zipFile = new ZipFile(shadeFile)) {\n                zipFile.stream()\n                    .filter(this::shouldProcessEntry)\n                    .forEach(entry -> processShadeEntry(zipFile, entry));\n            }\n        }\n\n        private void addFile(FileCopyDetailsInternal details) throws IOException {\n\n            ZipArchiveEntry entry = createEntry(details);\n\n            String path = details.getRelativePath().getPathString();\n            if (path.startsWith(\"lib\")) {\n                try (InputStream inputStream = details.open()) {\n                    CrcAndSize crcAndSize = new CrcAndSize(inputStream);\n                    crcAndSize.setUpStoredEntry(entry);\n                } catch (Exception e) {\n                    throw new IOException(\"please check this jar file\");\n                }\n            }\n\n            zipStream.putArchiveEntry(entry);\n            details.copyTo(zipStream);\n            zipStream.closeArchiveEntry();\n        }\n\n        private ZipArchiveEntry createEntry(FileCopyDetailsInternal details){\n            String path = details.isDirectory() ? details.getRelativePath().getPathString() + '/' : details.getRelativePath().getPathString();\n            ZipArchiveEntry entry = new ZipArchiveEntry(path);\n            entry.setTime(details.getLastModified());\n            int unixMode = details.getMode() | (details.isDirectory()  ? UnixStat.DIR_FLAG : UnixStat.FILE_FLAG);\n            entry.setUnixMode(unixMode);\n            return entry;\n        }\n\n        private boolean shouldProcessEntry(ZipEntry entry) {\n            return !\"META-INF/MANIFEST.MF\".equals(entry.getName());\n        }\n\n        private void processShadeEntry(ZipFile zipFile, ZipEntry entry) {\n            try {\n                ZipArchiveEntry newEntry = createNewEntry(entry);\n\n                if (entry.isDirectory()) {\n                    addDirectoryEntry(newEntry);\n                } else {\n                    addFileEntry(zipFile, entry, newEntry);\n                }\n            } catch (IOException e) {\n                throw new RuntimeException(\"Failed to process shade entry: \" + entry.getName(), e);\n            }\n        }\n\n        private ZipArchiveEntry createNewEntry(ZipEntry entry) {\n            ZipArchiveEntry newEntry = new ZipArchiveEntry(entry.getName());\n            newEntry.setTime(entry.getTime());\n            newEntry.setUnixMode(UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM);\n            return newEntry;\n        }\n\n        private void addDirectoryEntry(ZipArchiveEntry entry) throws IOException {\n            zipStream.putArchiveEntry(entry);\n            zipStream.closeArchiveEntry();\n        }\n\n        private void addFileEntry(ZipFile zipFile, ZipEntry entry, ZipArchiveEntry newEntry) throws IOException {\n            zipStream.putArchiveEntry(newEntry);\n            try (InputStream inputStream = zipFile.getInputStream(entry)) {\n                copy(inputStream, zipStream);\n            }\n            zipStream.closeArchiveEntry();\n        }\n\n        private void copy(InputStream in, OutputStream out) throws IOException {\n            byte[] buffer = new byte[8192];\n            int bytesRead;\n            while ((bytesRead = in.read(buffer)) != -1) {\n                out.write(buffer, 0, bytesRead);\n            }\n        }\n    }\n\n\n    /**\n     * Data holder for CRC and Size.\n     */\n    private static class CrcAndSize {\n\n        private static final int BUFFER_SIZE = 32 * 1024;\n\n        private final CRC32 crc = new CRC32();\n\n        private long size;\n\n        CrcAndSize(InputStream inputStream) throws IOException {\n            try {\n                load(inputStream);\n            }\n            finally {\n                inputStream.close();\n            }\n        }\n\n        private void load(InputStream inputStream) throws IOException {\n            byte[] buffer = new byte[BUFFER_SIZE];\n            int bytesRead;\n            while ((bytesRead = inputStream.read(buffer)) != -1) {\n                this.crc.update(buffer, 0, bytesRead);\n                this.size += bytesRead;\n            }\n        }\n\n        void setUpStoredEntry(ZipArchiveEntry entry) {\n            entry.setSize(this.size);\n            entry.setCompressedSize(this.size);\n            entry.setCrc(this.crc.getValue());\n            entry.setMethod(ZipEntry.STORED);\n        }\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ArkPluginExtension.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.boot.mojo;\n\nimport org.gradle.api.Action;\nimport org.gradle.api.Project;\nimport org.gradle.api.file.DirectoryProperty;\nimport org.gradle.api.provider.Property;\nimport org.gradle.api.provider.SetProperty;\n\nabstract public class ArkPluginExtension {\n\n    public ArkPluginExtension(Project project){\n        getPriority().convention(project.provider(() -> \"100\"));\n        getOutputDirectory().convention(project.getLayout().getBuildDirectory().dir(\"libs\"));\n        getPluginName().convention(project.getName());\n        getDescription().convention(\"\");\n        getActivator().convention(\"\");\n    }\n\n    abstract public SetProperty<String> getShades();\n    abstract public SetProperty<String> getExcludeArtifactIds();\n    abstract public SetProperty<String> getExcludeGroupIds();\n    abstract public SetProperty<String> getExcludes();\n    abstract public Property<String> getActivator();\n    abstract public Property<String>  getPriority();\n    abstract public Property<String>  getPluginName();\n\n    abstract public Property<String> getDescription();\n    abstract public DirectoryProperty getOutputDirectory();\n    abstract public Property<Boolean>  getAttach();\n\n    private final ImportedConfig imported = new ImportedConfig();\n    private final ExportedConfig exported = new ExportedConfig();\n\n    public ImportedConfig getImported() {\n        return imported;\n    }\n\n    public void imported(Action<? super ImportedConfig> action) {\n        action.execute(imported);\n    }\n\n    public ExportedConfig getExported() {\n        return exported;\n    }\n\n    public void exported(Action<? super ExportedConfig> action) {\n        action.execute(exported);\n    }\n\n    public static class ImportedConfig extends BaseConfig{\n\n    }\n\n    public static class ExportedConfig extends BaseConfig{\n\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ArkPluginJarTask.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.boot.mojo;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.zip.ZipFile;\nimport org.gradle.api.Project;\nimport org.gradle.api.artifacts.Configuration;\nimport org.gradle.api.artifacts.ExcludeRule;\nimport org.gradle.api.artifacts.ResolvedArtifact;\nimport org.gradle.api.internal.file.copy.CopyAction;\nimport org.gradle.api.plugins.JavaPluginExtension;\nimport org.gradle.api.provider.Provider;\nimport org.gradle.api.tasks.SourceSet;\nimport org.gradle.api.tasks.TaskAction;\nimport org.gradle.jvm.tasks.Jar;\n\npublic class ArkPluginJarTask extends Jar {\n\n    private final ArkPluginExtension arkPluginExtension;\n\n    private final Set<ResolvedArtifact> filteredArtifacts;\n    private final Set<ResolvedArtifact> conflictArtifacts;\n\n    private final Set<File> shadeFiles = new HashSet<>();\n\n    public ArkPluginJarTask(){\n        super();\n        Project project = getProject();\n        arkPluginExtension = project.getExtensions().findByType(ArkPluginExtension.class);\n\n        configureDestination();\n        configurePluginJar();\n\n        from(project.getTasks().getByName(\"classes\"));\n\n        Set<ResolvedArtifact> allArtifacts = getProjectArtifacts();\n        filteredArtifacts = filterArtifacts1(allArtifacts);\n        conflictArtifacts = filterConflictArtifacts(filteredArtifacts);\n\n        configureSourceSet(project);\n        configureArtifacts();\n        handleConflictArtifacts(conflictArtifacts);\n\n        configureManifest(project);\n\n        addArkPluginMark();\n    }\n\n    private void configurePluginJar(){\n        getArchiveFileName().set(getProject().provider(() -> {\n            String pluginName = arkPluginExtension.getPluginName().get();\n            return pluginName + \".jar\";\n        }));\n    }\n\n    private void configureArtifacts() {\n        into(\"lib\", copySpec -> {\n                copySpec.from(getProject().provider(this::getFilteredArtifactFiles));\n            copySpec.rename(this::renameArtifactIfConflict);\n        });\n\n        into(\"\", copySpec -> {\n            copySpec.from(getProject().provider(() -> shadeFiles));\n        });\n    }\n\n    private String renameArtifactIfConflict(String fileName) {\n        ResolvedArtifact artifact = findArtifactByFileName(fileName);\n        if (artifact != null && conflictArtifacts.contains(artifact)) {\n            return artifact.getModuleVersion().getId().getGroup() + \"-\" + fileName;\n        }\n        return fileName;\n    }\n\n    private ResolvedArtifact findArtifactByFileName(String fileName) {\n        return filteredArtifacts.stream()\n            .filter(artifact -> artifact.getFile().getName().equals(fileName))\n            .findFirst()\n            .orElse(null);\n    }\n\n    private Set<File> getFilteredArtifactFiles() {\n        Set<String> shadeNames = arkPluginExtension.getShades().get().stream()\n            .map(this::getShadeFileName)\n            .collect(Collectors.toSet());\n\n        return filteredArtifacts.stream()\n            .filter(artifact -> {\n                boolean isShade = shadeNames.contains(artifact.getFile().getName());\n                if (isShade) {\n                    shadeFiles.add(artifact.getFile());\n                }\n                return !isShade;\n            })\n            .map(ResolvedArtifact::getFile)\n            .filter(this::isZip)\n            .collect(Collectors.toSet());\n    }\n\n    private String getShadeFileName(String shade) {\n        String[] parts = shade.split(\":\");\n        if (parts.length != 3) {\n            throw new IllegalArgumentException(\"Invalid shade format: \" + shade);\n        }\n        String name = parts[1];\n        String version = parts[2];\n        return name + \"-\" + version + \".jar\";\n    }\n\n    private void configureManifest(Project project){\n        Provider<Map<String, String>> manifestAttributes = project.provider(() -> {\n            Map<String, String> attributes = new HashMap<>();\n            attributes.put(\"Manifest-Version\", \"1.0\");\n            attributes.put(\"Ark-Plugin-Name\", arkPluginExtension.getPluginName().get());\n            attributes.put(\"Ark-Plugin-Version\", project.getVersion().toString());\n\n            attributes.put(\"groupId\", project.getGroup().toString());\n            attributes.put(\"artifactId\", project.getName());\n            attributes.put(\"version\", project.getVersion().toString());\n            attributes.put(\"priority\", arkPluginExtension.getPriority().get());\n            attributes.put(\"pluginName\", arkPluginExtension.getPluginName().get());\n            attributes.put(\"description\", arkPluginExtension.getDescription().get());\n            attributes.put(\"activator\", arkPluginExtension.getActivator().get());\n\n            attributes.putAll(arkPluginExtension.getImported().toAttributes(\"import\"));\n            attributes.putAll(arkPluginExtension.getExported().toAttributes(\"export\"));\n            return attributes;\n        });\n\n        getManifest().attributes(manifestAttributes.get());\n    }\n\n    private void configureArtifacts(Project project, Set<ResolvedArtifact> filteredArtifacts){\n        from(project.provider(() -> {\n            return filteredArtifacts.stream()\n                .map(ResolvedArtifact::getFile)\n                .collect(Collectors.toSet());\n        }));\n    }\n\n    private void configureDestination(){\n        getDestinationDirectory().set(arkPluginExtension.getOutputDirectory());\n    }\n    private void configureSourceSet(Project project){\n        SourceSet mainSourceSet = project.getExtensions()\n                .getByType(JavaPluginExtension.class)\n                .getSourceSets()\n                .getByName(SourceSet.MAIN_SOURCE_SET_NAME);\n        from(mainSourceSet.getOutput());\n    }\n\n    private void configureCopySpec(Project project) {\n\n        from(project.provider(() -> {\n            Configuration runtimeClasspath = project.getConfigurations().getByName(\"runtimeClasspath\");\n            Set<ResolvedArtifact> artifacts = runtimeClasspath.getResolvedConfiguration().getResolvedArtifacts();\n            return filterArtifacts(artifacts);\n        }));\n\n    }\n\n    private Set<ResolvedArtifact> filterArtifacts1(Set<ResolvedArtifact> artifacts) {\n        return artifacts.stream()\n            .filter(this::shouldIncludeArtifact)\n            .collect(Collectors.toSet());\n    }\n\n\n    private Set<File> filterArtifacts(Set<ResolvedArtifact> artifacts) {\n        return artifacts.stream()\n            .filter(this::shouldIncludeArtifact)\n            .map(ResolvedArtifact::getFile)\n            .collect(Collectors.toSet());\n    }\n\n    private boolean shouldIncludeArtifact(ResolvedArtifact artifact) {\n        String groupId = artifact.getModuleVersion().getId().getGroup();\n        String artifactId = artifact.getName();\n        String gav = groupId + \":\" + artifactId ;\n\n        if (this.arkPluginExtension.getExcludes().get().contains(gav)) {\n            return false;\n        }\n\n        if (this.arkPluginExtension.getExcludeGroupIds().get().contains(groupId)) {\n            return false;\n        }\n\n        if (this.arkPluginExtension.getExcludeArtifactIds().get().contains(artifactId)) {\n            return false;\n        }\n\n        return true;\n    }\n\n    private Set<ResolvedArtifact> getProjectArtifacts() {\n        Configuration configuration = getProject().getConfigurations().getByName(\"runtimeClasspath\");\n        return configuration.getResolvedConfiguration().getResolvedArtifacts();\n    }\n\n    private boolean isZip(File file) {\n        try (ZipFile zipFile = new ZipFile(file)) {\n            return true;\n        } catch (Exception e) {\n            return false;\n        }\n    }\n\n    @Override\n    protected CopyAction createCopyAction() {\n        File jarFile = getArchiveFile().get().getAsFile();\n        return new ArkPluginCopyAction(jarFile, shadeFiles);\n    }\n\n    @TaskAction\n    private void action(){\n        super.copy();\n    }\n\n    private void addArkPluginMark() {\n        String markContent = \"this is plugin mark\";\n        String markPath = \"com/alipay/sofa/ark/plugin\";\n\n        from(getProject().provider(() -> {\n            try {\n                File tempFile = File.createTempFile(\"mark\", null);\n                tempFile.deleteOnExit();\n                Files.write(tempFile.toPath(), markContent.getBytes(StandardCharsets.UTF_8));\n                return tempFile;\n            } catch (IOException e) {\n                throw new RuntimeException(\"Failed to create mark file\", e);\n            }\n        }), copySpec -> {\n            copySpec.into(markPath);\n            copySpec.rename(fileName -> \"mark\");\n        });\n    }\n\n    protected Set<ResolvedArtifact> filterConflictArtifacts(Set<ResolvedArtifact> artifacts) {\n        Project project = getProject();\n        String projectArtifactId = project.getName();\n\n        Map<String, ResolvedArtifact> existArtifacts = new HashMap<>();\n\n        existArtifacts.put(projectArtifactId, null); //  ResolvedArtifact\n\n        Set<ResolvedArtifact> conflictArtifacts = new HashSet<>();\n\n        for (ResolvedArtifact artifact : artifacts) {\n            String artifactId = artifact.getName();\n            if (existArtifacts.containsKey(artifactId)) {\n                conflictArtifacts.add(artifact);\n                ResolvedArtifact existingArtifact = existArtifacts.get(artifactId);\n                if (existingArtifact != null) {\n                    conflictArtifacts.add(existingArtifact);\n                }\n            } else {\n                existArtifacts.put(artifactId, artifact);\n            }\n        }\n        return conflictArtifacts;\n    }\n\n    private void handleConflictArtifacts(Set<ResolvedArtifact> conflictArtifacts) {\n        for (ResolvedArtifact conflictArtifact : conflictArtifacts) {\n            getLogger().warn(\"Conflict artifact found: {}:{}:{}\",\n                conflictArtifact.getModuleVersion().getId().getGroup(),\n                conflictArtifact.getName(),\n                conflictArtifact.getModuleVersion().getId().getVersion());\n        }\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/BaseConfig.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.boot.mojo;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nabstract public class BaseConfig {\n    private List<String> packages = new ArrayList<>();\n    private List<String> classes = new ArrayList<>();\n    private List<String> resources = new ArrayList<>();\n\n    public List<String> getPackages() {\n        return packages;\n    }\n\n    public void setPackages(List<String> packages) {\n        this.packages = packages;\n    }\n\n    public List<String> getClasses() {\n        return classes;\n    }\n\n    public void setClasses(List<String> classes) {\n        this.classes = classes;\n    }\n\n    public List<String> getResources() {\n        return resources;\n    }\n\n    public void setResources(List<String> resources) {\n        this.resources = resources;\n    }\n\n\n    public Map<String, String> toAttributes(String prefix) {\n        Map<String, String> attributes = new HashMap<>();\n        attributes.put(prefix + \"-packages\", packages != null ? String.join(\",\", packages) : \"\");\n        attributes.put(prefix + \"-classes\", classes != null ? String.join(\",\", classes) : \"\");\n        attributes.put(prefix + \"-resources\", resources != null ? String.join(\",\", resources) : \"\");\n        return attributes;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-gradle-plugin/src/test/java/com/alipay/sofa/ark/boot/mojo/ArkPluginExtensionTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.boot.mojo;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\nimport java.io.File;\nimport org.gradle.api.Project;\nimport org.gradle.testfixtures.ProjectBuilder;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class ArkPluginExtensionTest {\n\n    private Project project;\n    private ArkPluginExtension extension;\n\n    @Before\n    public void setup() {\n        project = ProjectBuilder.builder().withName(\"test-project\").build();\n        project.getPluginManager().apply(\"sofa-ark-plugin-gradle-plugin\");\n        extension = project.getExtensions().getByType(ArkPluginExtension.class);\n    }\n\n    @Test\n    public void testDefaultValues() {\n        assertEquals(\"100\", extension.getPriority().get());\n        assertEquals(\"test-project\", extension.getPluginName().get());\n        assertEquals(\"\", extension.getDescription().get());\n        assertEquals(\"\", extension.getActivator().get());\n        assertTrue(extension.getOutputDirectory().get().getAsFile().getPath().endsWith(\"build\" + File.separator + \"libs\"));\n    }\n\n    @Test\n    public void testSetAndGetValues() {\n        extension.getPriority().set(\"200\");\n        extension.getPluginName().set(\"test-plugin\");\n        extension.getDescription().set(\"Test description\");\n        extension.getActivator().set(\"com.example.TestActivator\");\n        extension.getAttach().set(true);\n\n        assertEquals(\"200\", extension.getPriority().get());\n        assertEquals(\"test-plugin\", extension.getPluginName().get());\n        assertEquals(\"Test description\", extension.getDescription().get());\n        assertEquals(\"com.example.TestActivator\", extension.getActivator().get());\n        assertTrue(extension.getAttach().get());\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-maven-plugin/CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Module Overview\n\n**Artifact ID**: `sofa-ark-plugin-maven-plugin`\n**Packaging**: `maven-plugin`\n**Goal**: `ark-plugin`\n\nThis Maven plugin packages multiple JARs into a single Ark Plugin. Plugins provide class-isolated shared capabilities that can be used by business modules.\n\n## Purpose\n\n- Create Ark Plugin archives from Maven dependencies\n- Configure plugin class import/export settings\n- Set plugin priority and activator\n\n## Key Classes\n\n### `ArkPluginMojo`\nMain Maven Mojo bound to `package` phase:\n- Aggregates multiple JARs into a single plugin\n- Configures classloader isolation settings\n\nKey configuration parameters:\n- `activator` - Plugin activator class (implements `PluginActivator`)\n- `excludeGroupIds` / `excludeArtifactIds` - Dependencies to exclude from plugin\n- `exportPackages` - Packages to export for other plugins/biz to use\n- `exportClasses` - Specific classes to export\n- `importPackages` - Packages to import from other plugins\n- `importClasses` - Specific classes to import\n- `exportResources` - Resources to export\n- `importResources` - Resources to import\n- `priority` - Plugin startup priority (higher starts earlier)\n\n### `ImportConfig`\nConfiguration for imported packages/classes/resources.\n\n### `ExportConfig`\nConfiguration for exported packages/classes/resources.\n\n### `LinkedProperties` / `LinkedManifest` / `LinkedAttributes`\nUtility classes for maintaining order in manifest entries.\n\n## Plugin Structure\n\n```\nplugin.jar\n├── com/alipay/sofa/ark/plugin/  # Plugin metadata\n│   └── export.index             # Export index\n├── lib/                         # Bundled JARs\n└── META-INF/MANIFEST.MF         # Plugin metadata\n```\n\n## Usage\n\n```xml\n<plugin>\n    <groupId>com.alipay.sofa</groupId>\n    <artifactId>sofa-ark-plugin-maven-plugin</artifactId>\n    <executions>\n        <execution>\n            <goals>\n                <goal>ark-plugin</goal>\n            </goals>\n        </execution>\n    </executions>\n    <configuration>\n        <activator>com.example.MyPluginActivator</activator>\n        <exportPackages>com.example.api</exportPackages>\n        <priority>1000</priority>\n    </configuration>\n</plugin>\n```\n\n## Dependencies\n\n- Maven Core/Plugin APIs\n- `sofa-ark-common` - Utilities\n- `sofa-ark-tools` - Repackaging utilities"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-maven-plugin/META-INF/MANIFEST.MF",
    "content": "Manifest-Version: 1.0\r\ngroupId: a\r\nartifactId: b\r\nversion: c\r\npriority: 10\r\npluginName: xxx\r\ndescription: yyy\r\nactivator: \r\nimport-packages: \r\nimport-classes: \r\nimport-resources: \r\nexport-mode: \r\nexport-packages: \r\nexport-classes: \r\nexport-resources: \r\n\r\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-maven-plugin/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>sofa-ark-support</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>sofa-ark-plugin-maven-plugin</artifactId>\n    <name>${project.groupId}:${project.artifactId}</name>\n    <packaging>maven-plugin</packaging>\n\n    <dependencies>\n\n        <!--SOFAArk modules-->\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-spi</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-common</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-tools</artifactId>\n        </dependency>\n\n        <!--third party libraries-->\n        <dependency>\n            <groupId>commons-io</groupId>\n            <artifactId>commons-io</artifactId>\n        </dependency>\n\n        <!-- maven -->\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-core</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.maven.plugin-tools</groupId>\n            <artifactId>maven-plugin-annotations</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-archiver</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.maven.shared</groupId>\n            <artifactId>maven-common-artifact-filters</artifactId>\n        </dependency>\n\n        <!-- plexus -->\n        <dependency>\n            <groupId>org.codehaus.plexus</groupId>\n            <artifactId>plexus-component-annotations</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.codehaus.plexus</groupId>\n            <artifactId>plexus-utils</artifactId>\n        </dependency>\n\n        <!--third party libraries-->\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n        </dependency>\n\n        <!-- test -->\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-inline</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-plugin-plugin</artifactId>\n                <configuration>\n                    <!--add this config when use maven2-->\n                    <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>\n                </configuration>\n                <executions>\n                    <execution>\n                        <id>mojo-descriptor</id>\n                        <goals>\n                            <goal>descriptor</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-maven-plugin/src/main/java/com/alipay/sofa/ark/plugin/mojo/AbstractPropertiesConfig.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin.mojo;\n\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.LinkedHashSet;\nimport java.util.Properties;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic abstract class AbstractPropertiesConfig {\n\n    public static final String      KEY_MODE      = \"mode\";\n\n    public static final String      KEY_PACKAGES  = \"packages\";\n    public static final String      KEY_CLASSES   = \"classes\";\n    public static final String      KEY_RESOURCES = \"resources\";\n    public static final String      KEY_EXPORT    = \"export\";\n    public static final String      KEY_IMPORT    = \"import\";\n    public static final String      KEY_SPLIT     = \"-\";\n    public static final String      VALUE_SPLIT   = \",\";\n\n    protected String                mode;\n\n    /**\n     * imported or exported packages config\n     */\n    protected LinkedHashSet<String> packages;\n\n    /**\n     * imported or exported classes config\n     */\n    protected LinkedHashSet<String> classes;\n\n    /**\n     * imported or exported class config\n     */\n    protected LinkedHashSet<String> resources;\n\n    public void addClass(String className) {\n        if (classes == null) {\n            classes = new LinkedHashSet<>();\n        }\n        classes.add(className);\n    }\n\n    public String getMode() {\n        return mode;\n    }\n\n    public void setMode(String mode) {\n        this.mode = mode;\n    }\n\n    public LinkedHashSet<String> getPackages() {\n        return packages;\n    }\n\n    public void setPackages(LinkedHashSet<String> packages) {\n        this.packages = packages;\n    }\n\n    public LinkedHashSet<String> getClasses() {\n        return classes;\n    }\n\n    public void setClasses(LinkedHashSet<String> classes) {\n        this.classes = classes;\n    }\n\n    /**\n     * Getter method for property <code>resources</code>.\n     *\n     * @return property value of resources\n     */\n    public LinkedHashSet<String> getResources() {\n        return resources;\n    }\n\n    public void setResources(LinkedHashSet<String> resources) {\n        this.resources = resources;\n    }\n\n    public static void storeKeyValuePair(Properties prop, String name, Collection<String> value) {\n        if (value == null) {\n            value = new LinkedHashSet<>();\n        }\n        prop.setProperty(name, join(value.iterator(), VALUE_SPLIT));\n    }\n\n    public static String join(Iterator iterator, String separator) {\n        if (separator == null) {\n            separator = \"\";\n        }\n        StringBuffer buf = new StringBuffer(256);\n        while (iterator.hasNext()) {\n            buf.append(iterator.next());\n            if (iterator.hasNext()) {\n                buf.append(separator);\n            }\n        }\n        return buf.toString();\n    }\n\n    /**\n     * Store user configuration\n     * @param props\n     */\n    public abstract void store(Properties props);\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-maven-plugin/src/main/java/com/alipay/sofa/ark/plugin/mojo/ArkPluginMojo.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin.mojo;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.jar.JarFile;\nimport java.util.jar.Manifest;\nimport java.util.stream.Collectors;\n\nimport com.alipay.sofa.ark.common.util.ClassUtils;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.tools.ArtifactItem;\nimport com.alipay.sofa.ark.tools.JarWriter;\nimport com.alipay.sofa.ark.tools.Repackager;\nimport org.apache.maven.artifact.Artifact;\nimport org.apache.maven.plugin.AbstractMojo;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugins.annotations.*;\nimport org.apache.maven.project.MavenProject;\nimport org.apache.maven.project.MavenProjectHelper;\nimport org.codehaus.plexus.archiver.Archiver;\nimport org.codehaus.plexus.archiver.ArchiverException;\nimport org.codehaus.plexus.archiver.manager.ArchiverManager;\nimport org.codehaus.plexus.archiver.manager.NoSuchArchiverException;\nimport org.codehaus.plexus.archiver.zip.AbstractZipArchiver;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\n@Mojo(name = \"ark-plugin\", defaultPhase = LifecyclePhase.PACKAGE, requiresDependencyResolution = ResolutionScope.RUNTIME)\npublic class ArkPluginMojo extends AbstractMojo {\n\n    @Component\n    protected MavenProject          project;\n\n    @Component\n    protected ArchiverManager       archiverManager;\n\n    @Component\n    protected MavenProjectHelper    projectHelper;\n\n    /**\n     * The location of the generated ark plugin\n     */\n    @Parameter(defaultValue = \"${project.build.directory}\", property = \"sofa.ark.plugin.repository\")\n    protected File                  outputDirectory;\n\n    /**\n     * The location of sofa-ark-maven-plugin temporary file\n     */\n    @Parameter(defaultValue = \"${project.build.directory}/sofa-ark-maven-plugin\")\n    protected File                  workDirectory;\n\n    /**\n     * The configuration of ark plugin\n     */\n    @Parameter(defaultValue = \"${project.groupId}\", readonly = true)\n    protected String                groupId;\n\n    @Parameter(defaultValue = \"${project.artifactId}\", readonly = true)\n    protected String                artifactId;\n\n    @Parameter(defaultValue = \"${project.version}\", readonly = true)\n    protected String                version;\n\n    @Parameter(defaultValue = \"${project.artifactId}\")\n    public String                   pluginName;\n\n    @Parameter(defaultValue = \" \")\n    protected String                description;\n\n    @Parameter(defaultValue = \"100\", property = \"sofa.ark.plugin.priority\")\n    protected Integer               priority;\n\n    @Parameter\n    protected String                activator;\n\n    @Parameter\n    protected ExportConfig          exported;\n\n    @Parameter\n    protected ImportConfig          imported;\n\n    /**\n     * Colon separated groupId, artifactId [and classifier] to exclude (exact match)\n     */\n    @Parameter(defaultValue = \"\")\n    protected LinkedHashSet<String> excludes           = new LinkedHashSet<>();\n\n    /**\n     * list of groupId names to exclude (exact match).\n     */\n    @Parameter(defaultValue = \"\")\n    protected LinkedHashSet<String> excludeGroupIds;\n\n    /**\n     * list of artifact names to exclude (exact match).\n     */\n    @Parameter(defaultValue = \"\")\n    protected LinkedHashSet<String> excludeArtifactIds;\n\n    /**\n     * Colon separated groupId, artifactId, classifier(optional), version.\n     */\n    @Parameter(defaultValue = \"\")\n    protected LinkedHashSet<String> shades             = new LinkedHashSet<>();\n\n    /**\n     * whether install ark-plugin to local maven repository, if 'true',\n     * it will be installed when execute 'mvn install' or 'mvn deploy';\n     * default set 'true'.\n     */\n    @Parameter(defaultValue = \"true\")\n    private Boolean                 attach;\n\n    /**\n     * default ark plugin artifact classifier: empty\n     */\n    @Parameter(defaultValue = \"\")\n    private String                  classifier;\n\n    /**\n     * Export plugin project classes by default\n     */\n    @Parameter(defaultValue = \"true\")\n    protected Boolean               exportPluginClass;\n\n    private static final String     ARCHIVE_MODE       = \"zip\";\n    private static final String     PLUGIN_SUFFIX      = \".ark.plugin\";\n    private static final String     TEMP_PLUGIN_SUFFIX = \".ark.plugin.bak\";\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public void execute() throws MojoExecutionException {\n\n        Archiver archiver;\n\n        try {\n            archiver = getArchiver();\n        } catch (NoSuchArchiverException e) {\n            throw new MojoExecutionException(e.getMessage());\n        }\n\n        if (!outputDirectory.exists()) {\n            outputDirectory.mkdirs();\n        }\n\n        String fileName = getFileName();\n        File destination = new File(outputDirectory, fileName);\n        File tmpDestination = new File(outputDirectory, getTempFileName());\n        if (destination.exists()) {\n            destination.delete();\n        }\n        if (tmpDestination.exists()) {\n            tmpDestination.delete();\n        }\n        archiver.setDestFile(tmpDestination);\n\n        Set<Artifact> artifacts = project.getArtifacts();\n\n        artifacts = filterExcludeArtifacts(artifacts);\n\n        Set<Artifact> conflictArtifacts = filterConflictArtifacts(artifacts);\n\n        addArkPluginArtifact(archiver, artifacts, conflictArtifacts);\n\n        addArkPluginConfig(archiver);\n\n        try {\n            archiver.createArchive();\n            shadeJarIntoArkPlugin(destination, tmpDestination, artifacts);\n        } catch (ArchiverException | IOException e) {\n            throw new MojoExecutionException(e.getMessage());\n        } finally {\n            tmpDestination.delete();\n        }\n        if (isAttach()) {\n            if (StringUtils.isEmpty(classifier)) {\n                Artifact artifact = project.getArtifact();\n                artifact.setFile(destination);\n                project.setArtifact(artifact);\n            } else {\n                projectHelper.attachArtifact(project, destination, classifier);\n            }\n        }\n    }\n\n    public void shadeJarIntoArkPlugin(File pluginFile, File tmpPluginFile, Set<Artifact> artifacts)\n                                                                                                   throws IOException {\n        Set<Artifact> shadeJars = new HashSet<>();\n        shadeJars.add(project.getArtifact());\n        for (Artifact artifact : artifacts) {\n            if (isShadeJar(artifact)) {\n                shadeJars.add(artifact);\n            }\n        }\n\n        JarWriter writer = new JarWriter(pluginFile);\n        JarFile tmpJarFile = new JarFile(tmpPluginFile);\n        try {\n            writer.writeEntries(tmpJarFile);\n            for (Artifact jar : shadeJars) {\n                writer.writeEntries(new JarFile(jar.getFile()));\n            }\n        } finally {\n            writer.close();\n            tmpJarFile.close();\n        }\n    }\n\n    public LinkedHashSet<String> getShades() {\n        return shades;\n    }\n\n    public void setShades(LinkedHashSet<String> shades) {\n        this.shades = shades;\n    }\n\n    public void setProject(MavenProject project) {\n        this.project = project;\n    }\n\n    public boolean isShadeJar(Artifact artifact) {\n        for (String shade : getShades()) {\n            ArtifactItem artifactItem = ArtifactItem.parseArtifactItemWithVersion(shade);\n            if (!artifact.getGroupId().equals(artifactItem.getGroupId())) {\n                continue;\n            }\n            if (!artifact.getArtifactId().equals(artifactItem.getArtifactId())) {\n                continue;\n            }\n            if (!artifact.getVersion().equals(artifactItem.getVersion())) {\n                continue;\n            }\n            if (!StringUtils.isEmpty(artifactItem.getClassifier())\n                && !artifactItem.getClassifier().equals(artifact.getClassifier())) {\n                continue;\n            }\n            if (artifact.getArtifactId().equals(project.getArtifactId())\n                && artifact.getGroupId().equals(project.getGroupId())) {\n                throw new RuntimeException(\"Can't shade jar-self.\");\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * put all dependencies together into archive\n     *\n     * @param archiver ark plugin archiver\n     * @param dependencies all dependencies of ark plugin\n     * @param conflicts dependencies whose jar name (artifact id) is conflict\n     */\n    protected void addArkPluginArtifact(Archiver archiver, Set<Artifact> dependencies,\n                                        Set<Artifact> conflicts) {\n        for (Artifact artifact : dependencies) {\n            if (Repackager.isZip(artifact.getFile())) {\n                addArtifact(archiver, artifact, conflicts.contains(artifact));\n            }\n        }\n    }\n\n    /**\n     * add a artifact into archiver\n     *\n     * @param archiver archiver which represents a ark plugin\n     * @param artifact artifact which will be put into archiver\n     * @param artifactIdConflict whether artifact is conflicted, it will determine the final jar name.\n     */\n    protected void addArtifact(Archiver archiver, Artifact artifact, boolean artifactIdConflict) {\n        if (isShadeJar(artifact)) {\n            return;\n        }\n        String destination = artifact.getFile().getName();\n        if (artifactIdConflict) {\n            destination = artifact.getGroupId() + \"-\" + destination;\n        }\n        destination = \"lib/\" + destination;\n        getLog().debug(\"  \" + artifact + \" => \" + destination);\n        archiver.addFile(artifact.getFile(), destination);\n    }\n\n    /**\n     * compute conflict artifacts\n     *\n     * @param artifacts all dependencies of project\n     * @return artifacts whose jar name (artifact id) is conflict\n     */\n    protected Set<Artifact> filterConflictArtifacts(Set<Artifact> artifacts) {\n        HashMap<String, Artifact> existArtifacts = new HashMap<>(Collections.singletonMap(project\n            .getArtifact().getArtifactId(), project.getArtifact()));\n        HashSet<Artifact> conflictArtifacts = new HashSet<>();\n\n        for (Artifact artifact : artifacts) {\n            if (existArtifacts.containsKey(artifact.getArtifactId())) {\n                conflictArtifacts.add(artifact);\n                conflictArtifacts.add(existArtifacts.get(artifact.getArtifactId()));\n            } else {\n                existArtifacts.put(artifact.getArtifactId(), artifact);\n            }\n        }\n\n        return conflictArtifacts;\n    }\n\n    /**\n     * filter the excluded dependencies\n     *\n     * @param artifacts all dependencies of project\n     * @return dependencies excluded the excludes config\n     */\n    protected Set<Artifact> filterExcludeArtifacts(Set<Artifact> artifacts) {\n        List<ArtifactItem> excludeList = new ArrayList<>();\n        for (String exclude : excludes) {\n            ArtifactItem item = ArtifactItem.parseArtifactItemIgnoreVersion(exclude);\n            excludeList.add(item);\n        }\n\n        Set<Artifact> result = new LinkedHashSet<>();\n        for (Artifact e : artifacts) {\n            boolean isExclude = false;\n\n            for (ArtifactItem exclude : excludeList) {\n                if (exclude.isSameIgnoreVersion(ArtifactItem.parseArtifactItem(e))) {\n                    isExclude = true;\n                    break;\n                }\n            }\n\n            if (excludeGroupIds != null && excludeGroupIds.contains(e.getGroupId())) {\n                isExclude = true;\n            }\n\n            if (excludeArtifactIds != null && excludeArtifactIds.contains(e.getArtifactId())) {\n                isExclude = true;\n            }\n\n            if (!isExclude) {\n                result.add(e);\n            }\n        }\n\n        return result;\n    }\n\n    /**\n     * create a zip archiver\n     *\n     * @return a un-compress zip archiver\n     * @throws NoSuchArchiverException\n     */\n    protected Archiver getArchiver() throws NoSuchArchiverException {\n        Archiver archiver = archiverManager.getArchiver(ARCHIVE_MODE);\n        ((AbstractZipArchiver) archiver).setCompress(false);\n        return archiver;\n    }\n\n    /**\n     * repackage jar name\n     *\n     * @return ark plugin name\n     */\n    protected String getFileName() {\n        return String.format(\"%s%s\", pluginName, PLUGIN_SUFFIX);\n    }\n\n    protected String getTempFileName() {\n        return String.format(\"%s%s\", pluginName, TEMP_PLUGIN_SUFFIX);\n    }\n\n    /**\n     * check whether install ark plugin install local repo\n     * default true.\n     *\n     * @return whether install local repo\n     */\n    protected boolean isAttach() {\n        return attach;\n    }\n\n    /**\n     * check whether to export plugin project\n     * default true.\n     *\n     * @return whether to export plugin project\n     */\n    protected boolean getExportPluginClass() {\n        return exportPluginClass;\n    }\n\n    /**\n     * generate ark.plugin configuration file\n     * archive\n     * @param archiver\n     * @throws MojoExecutionException\n     */\n    protected void addArkPluginConfig(Archiver archiver) throws MojoExecutionException {\n        addManifest(archiver);\n        addArkPluginMark(archiver);\n    }\n\n    private void addManifest(Archiver archiver) throws MojoExecutionException {\n        LinkedProperties properties = new LinkedProperties();\n\n        properties.setProperty(\"groupId\", groupId);\n        properties.setProperty(\"artifactId\", artifactId);\n        properties.setProperty(\"version\", version);\n        properties.setProperty(\"priority\", String.valueOf(priority));\n        properties.setProperty(\"pluginName\", pluginName);\n        properties.setProperty(\"description\", description);\n        properties.setProperty(\"activator\", activator == null ? \"\" : activator);\n        properties.putAll(collectArkPluginImport());\n        properties.putAll(collectArkPluginExport());\n\n        addArkPluginConfig(archiver, \"META-INF/MANIFEST.MF\", properties);\n    }\n\n    private Properties collectArkPluginExport() throws MojoExecutionException {\n        Properties properties = new LinkedProperties();\n        if (exported == null) {\n            exported = new ExportConfig();\n        }\n        if (exportPluginClass) {\n            Set<String> projectClasses = findProjectClasses();\n            for (String projectClass : projectClasses) {\n                if (!StringUtils.isEmpty(projectClass)) {\n                    exported.addClass(projectClass);\n                }\n            }\n        }\n        exported.store(properties);\n        return properties;\n    }\n\n    private Set<String> findProjectClasses() throws MojoExecutionException {\n        try {\n            // Accessing the target/classes directory where compiled classes are located\n            File outputDirectory = new File(project.getBuild().getOutputDirectory());\n            // Ensure the directory exists\n            if (outputDirectory.exists()) {\n                Set<String> classes = new HashSet<>(ClassUtils.collectClasses(outputDirectory));\n                classes = classes.stream().filter(className -> !className.equals(this.activator)).collect(\n                        Collectors.toSet());\n                return classes;\n            } else {\n                getLog().warn(\"Output directory does not exist!\");\n            }\n            return new HashSet<>();\n        } catch (IOException e) {\n            throw new MojoExecutionException(\"Error finding compiled classes\", e);\n        }\n    }\n\n    private Properties collectArkPluginImport() {\n        Properties properties = new LinkedProperties();\n        if (imported == null) {\n            imported = new ImportConfig();\n        }\n        imported.store(properties);\n        return properties;\n    }\n\n    private void addArkPluginConfig(Archiver archiver, String path, LinkedProperties properties)\n                                                                                                throws MojoExecutionException {\n\n        File file = new File(workDirectory.getPath() + File.separator + path);\n        if (!file.getParentFile().exists()) {\n            file.getParentFile().mkdirs();\n        }\n\n        PrintStream outputStream = null;\n        Manifest manifest = new LinkedManifest();\n        manifest.getMainAttributes().putValue(\"Manifest-Version\", \"1.0\");\n        Enumeration enumeration = properties.keys();\n        while (enumeration.hasMoreElements()) {\n            String key = (String) enumeration.nextElement();\n            manifest.getMainAttributes().putValue(key, properties.getProperty(key));\n        }\n\n        try {\n            outputStream = new PrintStream(file, \"UTF-8\");\n            manifest.write(outputStream);\n        } catch (Exception ex) {\n            throw new MojoExecutionException(ex.getMessage());\n        } finally {\n            if (outputStream != null) {\n                outputStream.close();\n            }\n        }\n\n        archiver.addFile(file, path);\n\n    }\n\n    private void addArkPluginMark(Archiver archiver) throws MojoExecutionException {\n        addArkPluginConfig(archiver, Constants.ARK_PLUGIN_MARK_ENTRY, new LinkedProperties());\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-maven-plugin/src/main/java/com/alipay/sofa/ark/plugin/mojo/ExportConfig.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin.mojo;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Properties;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class ExportConfig extends AbstractPropertiesConfig {\n\n    @Override\n    public void store(Properties prop) {\n        storeKeyValuePair(prop, KEY_EXPORT + KEY_SPLIT + KEY_MODE, getMode() == null ? null\n            : Collections.singletonList(getMode()));\n        storeKeyValuePair(prop, KEY_EXPORT + KEY_SPLIT + KEY_PACKAGES, getPackages());\n        storeKeyValuePair(prop, KEY_EXPORT + KEY_SPLIT + KEY_CLASSES, getClasses());\n        storeKeyValuePair(prop, KEY_EXPORT + KEY_SPLIT + KEY_RESOURCES, getResources());\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-maven-plugin/src/main/java/com/alipay/sofa/ark/plugin/mojo/ImportConfig.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin.mojo;\n\nimport java.util.Properties;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class ImportConfig extends AbstractPropertiesConfig {\n\n    @Override\n    public void store(Properties prop) {\n        storeKeyValuePair(prop, KEY_IMPORT + KEY_SPLIT + KEY_PACKAGES, getPackages());\n        storeKeyValuePair(prop, KEY_IMPORT + KEY_SPLIT + KEY_CLASSES, getClasses());\n        storeKeyValuePair(prop, KEY_IMPORT + KEY_SPLIT + KEY_RESOURCES, getResources());\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-maven-plugin/src/main/java/com/alipay/sofa/ark/plugin/mojo/LinkedAttributes.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin.mojo;\n\nimport java.io.DataOutputStream;\nimport java.io.IOException;\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.jar.Attributes;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class LinkedAttributes extends Attributes {\n\n    private LinkedHashMap<Object, Object> linkedHashMap = new LinkedHashMap<>(11);\n\n    @Override\n    public Object put(Object name, Object value) {\n        linkedHashMap.put(name, value);\n        return super.put(name, value);\n    }\n\n    @Override\n    public Object remove(Object name) {\n        linkedHashMap.remove(name);\n        return super.remove(name);\n    }\n\n    @Override\n    public Set<Entry<Object, Object>> entrySet() {\n        return linkedHashMap.entrySet();\n    }\n\n    public void writeMain(DataOutputStream out) throws IOException {\n        // write out the *-Version header first, if it exists\n        String vername = Name.MANIFEST_VERSION.toString();\n        String version = getValue(vername);\n        if (version == null) {\n            vername = Name.SIGNATURE_VERSION.toString();\n            version = getValue(vername);\n        }\n\n        if (version != null) {\n            out.writeBytes(vername + \": \" + version + \"\\r\\n\");\n        }\n\n        // write out all attributes except for the version\n        // we wrote out earlier\n        Iterator it = entrySet().iterator();\n        while (it.hasNext()) {\n            Map.Entry e = (Map.Entry) it.next();\n            String name = e.getKey().toString();\n            if ((version != null) && !(name.equalsIgnoreCase(vername))) {\n\n                StringBuffer buffer = new StringBuffer(name);\n                buffer.append(\": \");\n\n                String value = (String) e.getValue();\n                if (value != null) {\n                    byte[] vb = value.getBytes(\"UTF8\");\n                    value = new String(vb, 0, 0, vb.length);\n                }\n                buffer.append(value);\n\n                buffer.append(\"\\r\\n\");\n                LinkedManifest.make72Safe(buffer);\n                out.writeBytes(buffer.toString());\n            }\n        }\n        out.writeBytes(\"\\r\\n\");\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-maven-plugin/src/main/java/com/alipay/sofa/ark/plugin/mojo/LinkedManifest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin.mojo;\n\nimport java.io.DataOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.util.jar.Attributes;\nimport java.util.jar.Manifest;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class LinkedManifest extends Manifest {\n\n    private LinkedAttributes attr = new LinkedAttributes();\n\n    @Override\n    public Attributes getMainAttributes() {\n        return attr;\n    }\n\n    @Override\n    public void write(OutputStream out) throws IOException {\n        DataOutputStream dos = new DataOutputStream(out);\n        // Write out the main attributes for the manifest\n        attr.writeMain(dos);\n        dos.flush();\n    }\n\n    /**\n     * Adds line breaks to enforce a maximum 72 bytes per line.\n     */\n    public static void make72Safe(StringBuffer line) {\n        int length = line.length();\n        if (length > 72) {\n            int index = 70;\n            while (index < length - 2) {\n                line.insert(index, \"\\r\\n \");\n                index += 72;\n                length += 3;\n            }\n        }\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-maven-plugin/src/main/java/com/alipay/sofa/ark/plugin/mojo/LinkedProperties.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin.mojo;\n\nimport java.util.*;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class LinkedProperties extends Properties {\n\n    private static final long             serialVersionUID = 1L;\n    private LinkedHashMap<Object, Object> linkedHashMap    = new LinkedHashMap<>(20);\n\n    @Override\n    public synchronized Object put(Object key, Object value) {\n        linkedHashMap.put(key, value);\n        return super.put(key, value);\n    }\n\n    @Override\n    public synchronized Object remove(Object key) {\n        linkedHashMap.remove(key);\n        return super.remove(key);\n    }\n\n    @Override\n    public synchronized Enumeration<Object> keys() {\n        return Collections.enumeration(linkedHashMap.keySet());\n    }\n\n    @Override\n    public Set<Map.Entry<Object, Object>> entrySet() {\n        return linkedHashMap.entrySet();\n    }\n\n    @Override\n    public synchronized void putAll(Map<?, ?> t) {\n        for (Map.Entry<?, ?> e : t.entrySet())\n            put(e.getKey(), e.getValue());\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-maven-plugin/src/main/resources/META-INF/maven/plugin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!-- Generated by maven-plugin-tools 3.1 on 2019-02-28 -->\n\n<plugin>\n    <name>sofa-ark-plugin-maven-plugin</name>\n    <description>A light-weight, java based classloader-isolated framework open-sourced by Ant Financial.</description>\n    <groupId>com.alipay.sofa</groupId>\n    <artifactId>sofa-ark-plugin-maven-plugin</artifactId>\n    <version>2.2.12</version>\n    <goalPrefix>sofa-ark</goalPrefix>\n    <isolatedRealm>false</isolatedRealm>\n    <inheritedByDefault>true</inheritedByDefault>\n    <mojos>\n        <mojo>\n            <goal>ark-plugin</goal>\n            <description></description>\n            <requiresDependencyResolution>runtime</requiresDependencyResolution>\n            <requiresDirectInvocation>false</requiresDirectInvocation>\n            <requiresProject>true</requiresProject>\n            <requiresReports>false</requiresReports>\n            <aggregator>false</aggregator>\n            <requiresOnline>false</requiresOnline>\n            <inheritedByDefault>true</inheritedByDefault>\n            <phase>package</phase>\n            <implementation>com.alipay.sofa.ark.plugin.mojo.ArkPluginMojo</implementation>\n            <language>java</language>\n            <instantiationStrategy>per-lookup</instantiationStrategy>\n            <executionStrategy>once-per-session</executionStrategy>\n            <since>0.1.0</since>\n            <threadSafe>false</threadSafe>\n            <parameters>\n                <parameter>\n                    <name>activator</name>\n                    <type>java.lang.String</type>\n                    <required>false</required>\n                    <editable>true</editable>\n                    <description></description>\n                </parameter>\n                <parameter>\n                    <name>artifactId</name>\n                    <type>java.lang.String</type>\n                    <required>false</required>\n                    <editable>false</editable>\n                    <description></description>\n                </parameter>\n                <parameter>\n                    <name>attach</name>\n                    <type>java.lang.Boolean</type>\n                    <required>false</required>\n                    <editable>true</editable>\n                    <description>whether install ark-plugin to local maven repository, if &apos;true&apos;,\n                        it will be installed when execute &apos;mvn install&apos; or &apos;mvn deploy&apos;;\n                        default set &apos;true&apos;.</description>\n                </parameter>\n                <parameter>\n                    <name>classifier</name>\n                    <type>java.lang.String</type>\n                    <required>false</required>\n                    <editable>true</editable>\n                    <description>default ark plugin artifact classifier: empty</description>\n                </parameter>\n                <parameter>\n                    <name>exportClass</name>\n                    <type>java.lang.Boolean</type>\n                    <required>false</required>\n                    <editable>true</editable>\n                    <description>Export plugin project package by default</description>\n                </parameter>\n                <parameter>\n                    <name>description</name>\n                    <type>java.lang.String</type>\n                    <required>false</required>\n                    <editable>true</editable>\n                    <description></description>\n                </parameter>\n                <parameter>\n                    <name>excludeArtifactIds</name>\n                    <type>java.util.LinkedHashSet</type>\n                    <required>false</required>\n                    <editable>true</editable>\n                    <description>list of artifact names to exclude (exact match).</description>\n                </parameter>\n                <parameter>\n                    <name>excludeGroupIds</name>\n                    <type>java.util.LinkedHashSet</type>\n                    <required>false</required>\n                    <editable>true</editable>\n                    <description>list of groupId names to exclude (exact match).</description>\n                </parameter>\n                <parameter>\n                    <name>excludes</name>\n                    <type>java.util.LinkedHashSet</type>\n                    <required>false</required>\n                    <editable>true</editable>\n                    <description>Colon separated groupId, artifactId [and classifier] to exclude (exact match)</description>\n                </parameter>\n                <parameter>\n                    <name>exported</name>\n                    <type>com.alipay.sofa.ark.plugin.mojo.ExportConfig</type>\n                    <required>false</required>\n                    <editable>true</editable>\n                    <description></description>\n                </parameter>\n                <parameter>\n                    <name>groupId</name>\n                    <type>java.lang.String</type>\n                    <required>false</required>\n                    <editable>false</editable>\n                    <description>The configuration of ark plugin</description>\n                </parameter>\n                <parameter>\n                    <name>imported</name>\n                    <type>com.alipay.sofa.ark.plugin.mojo.ImportConfig</type>\n                    <required>false</required>\n                    <editable>true</editable>\n                    <description></description>\n                </parameter>\n                <parameter>\n                    <name>outputDirectory</name>\n                    <type>java.io.File</type>\n                    <required>false</required>\n                    <editable>true</editable>\n                    <description>The location of the generated ark plugin</description>\n                </parameter>\n                <parameter>\n                    <name>pluginName</name>\n                    <type>java.lang.String</type>\n                    <required>false</required>\n                    <editable>true</editable>\n                    <description></description>\n                </parameter>\n                <parameter>\n                    <name>priority</name>\n                    <type>java.lang.Integer</type>\n                    <required>false</required>\n                    <editable>true</editable>\n                    <description></description>\n                </parameter>\n                <parameter>\n                    <name>shades</name>\n                    <type>java.util.LinkedHashSet</type>\n                    <required>false</required>\n                    <editable>true</editable>\n                    <description>Colon separated groupId, artifactId, classifier(optional), version.</description>\n                </parameter>\n                <parameter>\n                    <name>version</name>\n                    <type>java.lang.String</type>\n                    <required>false</required>\n                    <editable>false</editable>\n                    <description></description>\n                </parameter>\n                <parameter>\n                    <name>workDirectory</name>\n                    <type>java.io.File</type>\n                    <required>false</required>\n                    <editable>true</editable>\n                    <description>The location of sofa-ark-maven-plugin temporary file</description>\n                </parameter>\n                <parameter>\n                    <name>project</name>\n                    <type>org.apache.maven.project.MavenProject</type>\n                    <required>true</required>\n                    <editable>false</editable>\n                    <description></description>\n                </parameter>\n            </parameters>\n            <configuration>\n                <artifactId implementation=\"java.lang.String\" default-value=\"${project.artifactId}\"/>\n                <attach implementation=\"java.lang.Boolean\" default-value=\"true\"/>\n                <description implementation=\"java.lang.String\" default-value=\" \"/>\n                <groupId implementation=\"java.lang.String\" default-value=\"${project.groupId}\"/>\n                <outputDirectory implementation=\"java.io.File\" default-value=\"${project.build.directory}\">${sofa.ark.plugin.repository}</outputDirectory>\n                <pluginName implementation=\"java.lang.String\" default-value=\"${project.artifactId}\"/>\n                <priority implementation=\"java.lang.Integer\" default-value=\"100\">${sofa.ark.plugin.priority}</priority>\n                <version implementation=\"java.lang.String\" default-value=\"${project.version}\"/>\n                <workDirectory implementation=\"java.io.File\" default-value=\"${project.build.directory}/sofa-ark-maven-plugin\"/>\n                <project implementation=\"org.apache.maven.project.MavenProject\" default-value=\"${project}\"/>\n            </configuration>\n            <requirements>\n                <requirement>\n                    <role>org.codehaus.plexus.archiver.manager.ArchiverManager</role>\n                    <field-name>archiverManager</field-name>\n                </requirement>\n                <requirement>\n                    <role>org.apache.maven.project.MavenProjectHelper</role>\n                    <field-name>projectHelper</field-name>\n                </requirement>\n            </requirements>\n        </mojo>\n    </mojos>\n    <dependencies>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-spi</artifactId>\n            <type>jar</type>\n            <version>2.0.1-SNAPSHOT</version>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-exception</artifactId>\n            <type>jar</type>\n            <version>2.0.1-SNAPSHOT</version>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-common</artifactId>\n            <type>jar</type>\n            <version>2.0.1-SNAPSHOT</version>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>log-sofa-boot-starter</artifactId>\n            <type>jar</type>\n            <version>3.2.0</version>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa.common</groupId>\n            <artifactId>sofa-common-tools</artifactId>\n            <type>jar</type>\n            <version>1.0.19</version>\n        </dependency>\n        <dependency>\n            <groupId>com.google.inject</groupId>\n            <artifactId>guice</artifactId>\n            <type>jar</type>\n            <version>4.0</version>\n        </dependency>\n        <dependency>\n            <groupId>javax.inject</groupId>\n            <artifactId>javax.inject</artifactId>\n            <type>jar</type>\n            <version>1</version>\n        </dependency>\n        <dependency>\n            <groupId>aopalliance</groupId>\n            <artifactId>aopalliance</artifactId>\n            <type>jar</type>\n            <version>1.0</version>\n        </dependency>\n        <dependency>\n            <groupId>com.google.guava</groupId>\n            <artifactId>guava</artifactId>\n            <type>jar</type>\n            <version>16.0.1</version>\n        </dependency>\n        <dependency>\n            <groupId>ch.qos.logback</groupId>\n            <artifactId>logback-classic</artifactId>\n            <type>jar</type>\n            <version>1.1.11</version>\n        </dependency>\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-api</artifactId>\n            <type>jar</type>\n            <version>1.7.32</version>\n        </dependency>\n        <dependency>\n            <groupId>ch.qos.logback</groupId>\n            <artifactId>logback-core</artifactId>\n            <type>jar</type>\n            <version>1.1.11</version>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-tools</artifactId>\n            <type>jar</type>\n            <version>2.0.1-SNAPSHOT</version>\n        </dependency>\n        <dependency>\n            <groupId>org.ow2.asm</groupId>\n            <artifactId>asm</artifactId>\n            <type>jar</type>\n            <version>8.0</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-artifact</artifactId>\n            <type>jar</type>\n            <version>2.2.1</version>\n        </dependency>\n        <dependency>\n            <groupId>commons-io</groupId>\n            <artifactId>commons-io</artifactId>\n            <type>jar</type>\n            <version>2.5</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-core</artifactId>\n            <type>jar</type>\n            <version>3.8.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-settings</artifactId>\n            <type>jar</type>\n            <version>2.2.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven.wagon</groupId>\n            <artifactId>wagon-file</artifactId>\n            <type>jar</type>\n            <version>1.0-beta-6</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-plugin-parameter-documenter</artifactId>\n            <type>jar</type>\n            <version>2.2.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven.wagon</groupId>\n            <artifactId>wagon-http-lightweight</artifactId>\n            <type>jar</type>\n            <version>1.0-beta-6</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven.wagon</groupId>\n            <artifactId>wagon-http-shared</artifactId>\n            <type>jar</type>\n            <version>1.0-beta-6</version>\n        </dependency>\n        <dependency>\n            <groupId>nekohtml</groupId>\n            <artifactId>xercesMinimal</artifactId>\n            <type>jar</type>\n            <version>1.9.6.2</version>\n        </dependency>\n        <dependency>\n            <groupId>nekohtml</groupId>\n            <artifactId>nekohtml</artifactId>\n            <type>jar</type>\n            <version>1.9.6.2</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven.wagon</groupId>\n            <artifactId>wagon-http</artifactId>\n            <type>jar</type>\n            <version>1.0-beta-6</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven.wagon</groupId>\n            <artifactId>wagon-webdav-jackrabbit</artifactId>\n            <type>jar</type>\n            <version>1.0-beta-6</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.jackrabbit</groupId>\n            <artifactId>jackrabbit-webdav</artifactId>\n            <type>jar</type>\n            <version>1.5.0</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.jackrabbit</groupId>\n            <artifactId>jackrabbit-jcr-commons</artifactId>\n            <type>jar</type>\n            <version>1.5.0</version>\n        </dependency>\n        <dependency>\n            <groupId>commons-httpclient</groupId>\n            <artifactId>commons-httpclient</artifactId>\n            <type>jar</type>\n            <version>3.0</version>\n        </dependency>\n        <dependency>\n            <groupId>commons-codec</groupId>\n            <artifactId>commons-codec</artifactId>\n            <type>jar</type>\n            <version>1.2</version>\n        </dependency>\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-nop</artifactId>\n            <type>jar</type>\n            <version>1.5.3</version>\n        </dependency>\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-jdk14</artifactId>\n            <type>jar</type>\n            <version>1.5.6</version>\n        </dependency>\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>jcl-over-slf4j</artifactId>\n            <type>jar</type>\n            <version>1.5.6</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven.reporting</groupId>\n            <artifactId>maven-reporting-api</artifactId>\n            <type>jar</type>\n            <version>2.2.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven.doxia</groupId>\n            <artifactId>doxia-sink-api</artifactId>\n            <type>jar</type>\n            <version>1.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven.doxia</groupId>\n            <artifactId>doxia-logging-api</artifactId>\n            <type>jar</type>\n            <version>1.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-profile</artifactId>\n            <type>jar</type>\n            <version>2.2.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-model</artifactId>\n            <type>jar</type>\n            <version>2.2.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven.wagon</groupId>\n            <artifactId>wagon-provider-api</artifactId>\n            <type>jar</type>\n            <version>1.0-beta-6</version>\n        </dependency>\n        <dependency>\n            <groupId>org.codehaus.plexus</groupId>\n            <artifactId>plexus-container-default</artifactId>\n            <type>jar</type>\n            <version>1.0-alpha-9-stable-1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-repository-metadata</artifactId>\n            <type>jar</type>\n            <version>2.2.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-error-diagnostics</artifactId>\n            <type>jar</type>\n            <version>2.2.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-core</artifactId>\n            <type>jar</type>\n            <version>3.8.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-plugin-registry</artifactId>\n            <type>jar</type>\n            <version>2.2.1</version>\n        </dependency>\n        <dependency>\n            <groupId>commons-cli</groupId>\n            <artifactId>commons-cli</artifactId>\n            <type>jar</type>\n            <version>1.2</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-plugin-api</artifactId>\n            <type>jar</type>\n            <version>3.1.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.eclipse.sisu</groupId>\n            <artifactId>org.eclipse.sisu.plexus</artifactId>\n            <type>jar</type>\n            <version>0.0.0.M5</version>\n        </dependency>\n        <dependency>\n            <groupId>javax.enterprise</groupId>\n            <artifactId>cdi-api</artifactId>\n            <type>jar</type>\n            <version>1.0</version>\n        </dependency>\n        <dependency>\n            <groupId>javax.annotation</groupId>\n            <artifactId>jsr250-api</artifactId>\n            <type>jar</type>\n            <version>1.0</version>\n        </dependency>\n        <dependency>\n            <groupId>org.sonatype.sisu</groupId>\n            <artifactId>sisu-guice</artifactId>\n            <type>jar</type>\n            <version>3.1.0</version>\n        </dependency>\n        <dependency>\n            <groupId>org.eclipse.sisu</groupId>\n            <artifactId>org.eclipse.sisu.inject</artifactId>\n            <type>jar</type>\n            <version>0.0.0.M5</version>\n        </dependency>\n        <dependency>\n            <groupId>org.codehaus.plexus</groupId>\n            <artifactId>plexus-classworlds</artifactId>\n            <type>jar</type>\n            <version>2.4</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven.wagon</groupId>\n            <artifactId>wagon-ssh-external</artifactId>\n            <type>jar</type>\n            <version>1.0-beta-6</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven.wagon</groupId>\n            <artifactId>wagon-ssh-common</artifactId>\n            <type>jar</type>\n            <version>1.0-beta-6</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-plugin-descriptor</artifactId>\n            <type>jar</type>\n            <version>2.2.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.codehaus.plexus</groupId>\n            <artifactId>plexus-interactivity-api</artifactId>\n            <type>jar</type>\n            <version>1.0-alpha-4</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-artifact-manager</artifactId>\n            <type>jar</type>\n            <version>2.2.1</version>\n        </dependency>\n        <dependency>\n            <groupId>backport-util-concurrent</groupId>\n            <artifactId>backport-util-concurrent</artifactId>\n            <type>jar</type>\n            <version>3.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-monitor</artifactId>\n            <type>jar</type>\n            <version>2.2.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven.wagon</groupId>\n            <artifactId>wagon-ssh</artifactId>\n            <type>jar</type>\n            <version>1.0-beta-6</version>\n        </dependency>\n        <dependency>\n            <groupId>com.jcraft</groupId>\n            <artifactId>jsch</artifactId>\n            <type>jar</type>\n            <version>0.1.38</version>\n        </dependency>\n        <dependency>\n            <groupId>classworlds</groupId>\n            <artifactId>classworlds</artifactId>\n            <type>jar</type>\n            <version>1.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.sonatype.plexus</groupId>\n            <artifactId>plexus-sec-dispatcher</artifactId>\n            <type>jar</type>\n            <version>1.3</version>\n        </dependency>\n        <dependency>\n            <groupId>org.sonatype.plexus</groupId>\n            <artifactId>plexus-cipher</artifactId>\n            <type>jar</type>\n            <version>1.4</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven.plugin-tools</groupId>\n            <artifactId>maven-plugin-annotations</artifactId>\n            <type>jar</type>\n            <version>3.2</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-archiver</artifactId>\n            <type>jar</type>\n            <version>2.5</version>\n        </dependency>\n        <dependency>\n            <groupId>org.codehaus.plexus</groupId>\n            <artifactId>plexus-archiver</artifactId>\n            <type>jar</type>\n            <version>2.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.codehaus.plexus</groupId>\n            <artifactId>plexus-io</artifactId>\n            <type>jar</type>\n            <version>2.0.2</version>\n        </dependency>\n        <dependency>\n            <groupId>org.codehaus.plexus</groupId>\n            <artifactId>plexus-interpolation</artifactId>\n            <type>jar</type>\n            <version>1.15</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven.shared</groupId>\n            <artifactId>maven-common-artifact-filters</artifactId>\n            <type>jar</type>\n            <version>1.4</version>\n        </dependency>\n        <dependency>\n            <groupId>org.codehaus.plexus</groupId>\n            <artifactId>plexus-component-annotations</artifactId>\n            <type>jar</type>\n            <version>1.5.5</version>\n        </dependency>\n        <dependency>\n            <groupId>org.codehaus.plexus</groupId>\n            <artifactId>plexus-utils</artifactId>\n            <type>jar</type>\n            <version>3.0</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n            <type>jar</type>\n            <version>3.3.1</version>\n        </dependency>\n    </dependencies>\n</plugin>\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-maven-plugin/src/test/java/com/alipay/sofa/ark/plugin/mojo/ArkPluginMojoTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin.mojo;\n\nimport org.apache.maven.artifact.Artifact;\nimport org.apache.maven.artifact.DefaultArtifact;\nimport org.apache.maven.artifact.handler.DefaultArtifactHandler;\nimport org.apache.maven.model.Build;\nimport org.apache.maven.project.MavenProject;\nimport org.codehaus.plexus.archiver.AbstractArchiver;\nimport org.codehaus.plexus.archiver.manager.ArchiverManager;\nimport org.codehaus.plexus.archiver.zip.ZipArchiver;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.reflect.Field;\nimport java.util.*;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\npublic class ArkPluginMojoTest {\n\n    ArkPluginMojo arkPluginMojo = new ArkPluginMojo();\n\n    @Test\n    public void testExecute() throws Exception {\n\n        ArchiverManager archiverManager = mock(ArchiverManager.class);\n        AtomicInteger finalResourcesCountInJar = new AtomicInteger(0);\n        when(archiverManager.getArchiver(\"zip\")).thenReturn(new ZipArchiver() {\n            @Override\n            public void cleanUp() throws IOException {\n                try {\n                    Field field = AbstractArchiver.class.getDeclaredField(\"resources\");\n                    field.setAccessible(true);\n                    finalResourcesCountInJar.set(((List<Object>) field.get(this)).size());\n                } catch (Exception e) {\n                    throw new RuntimeException(e);\n                }\n                super.cleanUp();\n            }\n        });\n\n        Field field = ArkPluginMojo.class.getDeclaredField(\"archiverManager\");\n        field.setAccessible(true);\n        field.set(arkPluginMojo, archiverManager);\n\n        File outputDirectory = new File(\"./\");\n        field = ArkPluginMojo.class.getDeclaredField(\"outputDirectory\");\n        field.setAccessible(true);\n        field.set(arkPluginMojo, outputDirectory);\n        new File(outputDirectory, \"xxx.ark.plugin\").createNewFile();\n        new File(outputDirectory, \"xxx.ark.plugin.bak\").createNewFile();\n\n        LinkedHashSet<String> excludes = new LinkedHashSet<>();\n        excludes.add(\"group1:artifact3\");\n        excludes.add(\"group1:artifact2:17\");\n        excludes.add(\"groupx:x\");\n        arkPluginMojo.excludes = excludes;\n        LinkedHashSet<String> excludeGroupIds = new LinkedHashSet<>();\n        excludeGroupIds.add(\"groupy\");\n        arkPluginMojo.excludeGroupIds = excludeGroupIds;\n        LinkedHashSet<String> excludeArtifactIds = new LinkedHashSet<>();\n        excludeArtifactIds.add(\"art1\");\n        arkPluginMojo.excludeArtifactIds = excludeArtifactIds;\n\n        Set<Artifact> artifacts = new HashSet<>();\n        DefaultArtifact defaultArtifact = new DefaultArtifact(\"groupx\", \"x\", \"version\", \"provided\",\n            \"jar\", \"17\", new DefaultArtifactHandler());\n        defaultArtifact.setFile(new File(\"src/test/resources/test-demo.jar\"));\n        artifacts.add(defaultArtifact);\n\n        artifacts.add(new DefaultArtifact(\"group1\", \"artifact3\", \"version\", \"provided\", \"jar\",\n            null, new DefaultArtifactHandler()));\n        artifacts.add(new DefaultArtifact(\"group1\", \"artifact2\", \"version\", \"provided\", \"jar\",\n            \"17\", new DefaultArtifactHandler()));\n\n        defaultArtifact = new DefaultArtifact(\"groupyxx\", \"x\", \"version\", \"provided\", \"jar\", \"17\",\n            new DefaultArtifactHandler());\n        defaultArtifact.setFile(new File(\"src/test/resources/test-demo.jar\"));\n        artifacts.add(defaultArtifact);\n\n        artifacts.add(new DefaultArtifact(\"groupy\", \"x\", \"version\", \"provided\", \"jar\", \"17\",\n            new DefaultArtifactHandler()));\n        artifacts.add(new DefaultArtifact(\"groupyxx\", \"art1\", \"version\", \"provided\", \"jar\", \"17\",\n            new DefaultArtifactHandler()));\n\n        DefaultArtifact shadeArtifact = new DefaultArtifact(\"shadeGroup\", \"shadeArtifact\",\n            \"shadeVersion\", \"provided\", \"jar\", \"shadeClassifier\", new DefaultArtifactHandler());\n        shadeArtifact.setFile(new File(\"src/test/resources/test-demo.jar\"));\n        artifacts.add(shadeArtifact);\n\n        DefaultArtifact projectArtifact = new DefaultArtifact(\"a\", \"b\", \"c\", \"compile\", \"jar\",\n            null, new DefaultArtifactHandler());\n        projectArtifact.setFile(new File(\"src/test/resources/test-demo.jar\"));\n\n        Build build = new Build();\n        build.setOutputDirectory(\"./notexist\");\n        MavenProject mavenProject = mock(MavenProject.class);\n        when(mavenProject.getArtifacts()).thenReturn(artifacts);\n        when(mavenProject.getArtifact()).thenReturn(projectArtifact);\n        when(mavenProject.getBuild()).thenReturn(build);\n        arkPluginMojo.setProject(mavenProject);\n        arkPluginMojo.setShades(new LinkedHashSet<>(Collections\n            .singleton(\"shadeGroup:shadeArtifact:shadeVersion:shadeClassifier\")));\n\n        field = ArkPluginMojo.class.getDeclaredField(\"attach\");\n        field.setAccessible(true);\n        field.set(arkPluginMojo, true);\n\n        arkPluginMojo.groupId = \"a\";\n        arkPluginMojo.artifactId = \"b\";\n        arkPluginMojo.version = \"c\";\n        arkPluginMojo.priority = 10;\n        arkPluginMojo.pluginName = \"xxx\";\n        arkPluginMojo.description = \"yyy\";\n        arkPluginMojo.workDirectory = new File(\"./\");\n        arkPluginMojo.exportPluginClass = true;\n        arkPluginMojo.execute();\n        assertEquals(4, finalResourcesCountInJar.get());\n    }\n\n    @Test\n    public void testExportConfig() {\n        Properties properties = new Properties();\n        ExportConfig exportConfig = new ExportConfig();\n        LinkedHashSet<String> classes = new LinkedHashSet();\n        classes.add(\"a\");\n        classes.add(\"b\");\n        exportConfig.setClasses(classes);\n        exportConfig.store(properties);\n        assertEquals(\"a,b\", properties.getProperty(\"export-classes\"));\n        assertEquals(\"\", properties.getProperty(\"export-mode\"));\n        assertEquals(\"\", properties.getProperty(\"export-resources\"));\n        assertEquals(\"\", properties.getProperty(\"export-packages\"));\n    }\n\n    @Test\n    public void testImportConfig() {\n        Properties properties = new Properties();\n        ImportConfig importConfig = new ImportConfig();\n        LinkedHashSet<String> resources = new LinkedHashSet();\n        resources.add(\"c\");\n        resources.add(\"d\");\n        importConfig.setResources(resources);\n        importConfig.store(properties);\n        assertEquals(\"\", properties.getProperty(\"import-classes\"));\n        assertEquals(\"c,d\", properties.getProperty(\"import-resources\"));\n        assertEquals(\"\", properties.getProperty(\"import-packages\"));\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-maven-plugin/src/test/java/com/alipay/sofa/ark/plugin/mojo/LinkedAttributesTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin.mojo;\n\nimport org.junit.Test;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.DataOutputStream;\n\nimport static java.util.jar.Attributes.Name.*;\nimport static org.apache.commons.lang3.StringUtils.repeat;\nimport static org.junit.Assert.assertEquals;\n\npublic class LinkedAttributesTest {\n\n    private LinkedAttributes linkedAttributes = new LinkedAttributes();\n\n    @Test\n    public void testLinkedAttributesAllMethods() throws Exception {\n\n        assertEquals(0, linkedAttributes.entrySet().size());\n        linkedAttributes.put(EXTENSION_NAME, \"b\");\n        assertEquals(1, linkedAttributes.entrySet().size());\n\n        linkedAttributes.remove(EXTENSION_NAME);\n        assertEquals(0, linkedAttributes.entrySet().size());\n\n        linkedAttributes.put(MANIFEST_VERSION, \"1.0.0\");\n        linkedAttributes.put(CONTENT_TYPE, \"d\");\n        linkedAttributes.put(IMPLEMENTATION_TITLE, repeat(\"f\", 150));\n\n        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();\n        linkedAttributes.writeMain(new DataOutputStream(outputStream));\n        outputStream.flush();\n\n        assertEquals(\"Manifest-Version: 1.0.0\\r\\n\" + \"Content-Type: d\\r\\n\"\n                     + \"Implementation-Title: \" + repeat(\"f\", 48) + \"\\r\\n \" + repeat(\"f\", 69)\n                     + \"\\r\\n \" + repeat(\"f\", 33) + \"\\r\\n\\r\\n\", outputStream.toString(\"UTF-8\"));\n    }\n\n    @Test\n    public void testLinkedManifestWriteEmpty() throws Exception {\n\n        LinkedManifest linkedManifest = new LinkedManifest();\n        linkedManifest.getMainAttributes().putValue(\"a\", \"b\");\n\n        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();\n        linkedManifest.write(outputStream);\n\n        outputStream.flush();\n        assertEquals(\"\\r\\n\", outputStream.toString(\"UTF-8\"));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-maven-plugin/src/test/java/com/alipay/sofa/ark/plugin/mojo/LinkedPropertiesTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin.mojo;\n\nimport org.junit.Test;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static org.junit.Assert.assertEquals;\n\npublic class LinkedPropertiesTest {\n\n    private LinkedProperties linkedProperties = new LinkedProperties();\n\n    @Test\n    public void testLinkedPropertiesAllMethods() {\n\n        assertEquals(false, linkedProperties.keys().hasMoreElements());\n        linkedProperties.put(\"a\", \"b\");\n        assertEquals(1, linkedProperties.entrySet().size());\n\n        linkedProperties.remove(\"a\");\n        assertEquals(0, linkedProperties.entrySet().size());\n\n        Map<Object, Object> properties = new HashMap<>();\n        properties.put(\"c\", \"d\");\n        properties.put(\"e\", \"f\");\n        linkedProperties.putAll(properties);\n        assertEquals(2, linkedProperties.entrySet().size());\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-plugin-maven-plugin/src/test/java/com/alipay/sofa/ark/plugin/mojo/test/ArkPluginMojoTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.plugin.mojo.test;\n\nimport com.alipay.sofa.ark.common.util.FileUtils;\nimport com.alipay.sofa.ark.plugin.mojo.ArkPluginMojo;\nimport org.apache.maven.artifact.Artifact;\nimport org.apache.maven.project.MavenProject;\nimport org.junit.Test;\nimport org.mockito.Mockito;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.util.Collections;\nimport java.util.LinkedHashSet;\n\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.when;\n\n/**\n * @author qilong.zql\n * @since 0.4.0\n */\npublic class ArkPluginMojoTest {\n    @Test\n    public void testArkPluginMojo() throws Exception {\n        Artifact artifact = Mockito.mock(Artifact.class);\n        MavenProject project = Mockito.mock(MavenProject.class);\n\n        when(artifact.getGroupId()).thenReturn(\"invalid\");\n        when(artifact.getArtifactId()).thenReturn(\"invalid\");\n        when(project.getArtifact()).thenReturn(artifact);\n        when(artifact.getFile()).thenReturn(\n            new File(Test.class.getProtectionDomain().getCodeSource().getLocation().getPath()));\n\n        ArkPluginMojo arkPluginMojo = new ArkPluginMojo();\n        arkPluginMojo.setProject(project);\n        arkPluginMojo.setShades(new LinkedHashSet<>(Collections\n            .singleton(\"com.alipay.sofa:test-demo:1.0.0\")));\n        final URL url = this.getClass().getClassLoader().getResource(\"test-demo.jar\");\n\n        String path = url.getPath() + \".shaded\";\n        String shadedUrl = url.toExternalForm() + \".shaded\";\n        String copyPath = url.getPath() + \".copy\";\n        File copyFileForTest = new File(copyPath);\n\n        FileInputStream demoJar = new FileInputStream(url.getPath());\n        FileUtils.copyInputStreamToFile(demoJar, new File(copyPath));\n        demoJar.close();\n\n        arkPluginMojo.shadeJarIntoArkPlugin(new File(path), copyFileForTest,\n            Collections.singleton(artifact));\n\n        assertTrue(copyFileForTest.delete());\n        URLClassLoader urlClassLoader = new URLClassLoader(new URL[] { new URL(shadedUrl) }, null);\n        assertNotNull(urlClassLoader.loadClass(\"com.alipay.sofa.support.test.SampleService\"));\n        assertNotNull(urlClassLoader.loadClass(\"org.junit.Test\"));\n    }\n\n    @Test\n    public void testShadeJar() {\n        ArkPluginMojo arkPluginMojo = new ArkPluginMojo();\n        arkPluginMojo.setShades(new LinkedHashSet<>(Collections\n            .singleton(\"com.alipay.sofa:test-demo:1.0.0\")));\n\n        MavenProject projectOne = Mockito.mock(MavenProject.class);\n        MavenProject projectTwo = Mockito.mock(MavenProject.class);\n        Artifact artifact = Mockito.mock(Artifact.class);\n\n        when(projectOne.getGroupId()).thenReturn(\"com.alipay.sofa\");\n        when(projectOne.getArtifactId()).thenReturn(\"test-demo\");\n\n        when(projectTwo.getGroupId()).thenReturn(\"com.alipay.sofa\");\n        when(projectTwo.getArtifactId()).thenReturn(\"\");\n\n        when(artifact.getGroupId()).thenReturn(\"com.alipay.sofa\");\n        when(artifact.getArtifactId()).thenReturn(\"test-demo\");\n        when(artifact.getVersion()).thenReturn(\"1.0.0\");\n\n        arkPluginMojo.setProject(projectOne);\n        try {\n            arkPluginMojo.isShadeJar(artifact);\n        } catch (Exception ex) {\n            assertTrue(ex.getMessage().equals(\"Can't shade jar-self.\"));\n        }\n\n        arkPluginMojo.setProject(projectTwo);\n        assertTrue(arkPluginMojo.isShadeJar(artifact));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-common-springboot/CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Module Overview\n\n**Artifact ID**: `sofa-ark-common-springboot`\n**Package**: `com.alipay.sofa.ark.springboot`\n\nThis module provides common Spring Boot integration code shared across different Spring Boot versions.\n\n## Purpose\n\n- Conditional annotations for Ark-enabled features\n- Spring Boot version detection\n\n## Key Classes\n\n### `condition.ConditionalOnArkEnabled`\nConditional annotation to enable features only when Ark is active:\n```java\n@ConditionalOnArkEnabled\n@Bean\npublic MyBean myBean() { ... }\n```\n\n### `condition.OnArkEnabled`\nCondition implementation that checks if Ark container is running.\n\n### `condition.ConditionalOnSpringBootVersion`\nConditional annotation based on Spring Boot version:\n```java\n@ConditionalOnSpringBootVersion(\"2.x\")\n@Bean\npublic MyBean myBean() { ... }\n```\n\n### `condition.OnSpringBootVersion`\nCondition implementation that matches specific Spring Boot versions.\n\n## Dependencies\n\n- `sofa-ark-spi` - Service interfaces\n- Spring Boot (provided scope)\n\n## Used By\n\n- `ark-compatible-springboot1` - Spring Boot 1.x compatibility\n- `ark-compatible-springboot2` - Spring Boot 2.x compatibility\n- `ark-springboot-starter` - Main starter"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-common-springboot/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>sofa-ark-support</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n        <relativePath>../../pom.xml</relativePath>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>sofa-ark-common-springboot</artifactId>\n    <name>${project.groupId}:${project.artifactId}</name>\n\n    <properties>\n        <spring.boot.version>1.5.22.RELEASE</spring.boot.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot</artifactId>\n            <version>${spring.boot.version}</version>\n            <scope>provided</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-autoconfigure</artifactId>\n            <version>${spring.boot.version}</version>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-api</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-common-springboot/src/main/java/com/alipay/sofa/ark/springboot/condition/ConditionalOnArkEnabled.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot.condition;\n\nimport org.springframework.context.annotation.Conditional;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * @author qilong.zql 19/2/27\n * @since 0.6.0\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Conditional(OnArkEnabled.class)\npublic @interface ConditionalOnArkEnabled {\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-common-springboot/src/main/java/com/alipay/sofa/ark/springboot/condition/ConditionalOnSpringBootVersion.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot.condition;\n\nimport org.springframework.context.annotation.Conditional;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Conditional(OnSpringBootVersion.class)\npublic @interface ConditionalOnSpringBootVersion {\n    /**\n     * The required version of spring boot\n     * @return the required spring boot version\n     */\n    Version version() default Version.ANY;\n\n    /**\n     * Available application types.\n     */\n    enum Version {\n\n        /**\n         * Spring Boot 1.x\n         */\n        OneX,\n\n        /**\n         * Spring Boot 2.x\n         */\n        TwoX,\n\n        /**\n         * Any Spring Boot Version\n         */\n        ANY\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-common-springboot/src/main/java/com/alipay/sofa/ark/springboot/condition/OnArkEnabled.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot.condition;\n\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport org.springframework.boot.autoconfigure.condition.ConditionOutcome;\nimport org.springframework.boot.autoconfigure.condition.SpringBootCondition;\nimport org.springframework.context.annotation.ConditionContext;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.core.type.AnnotatedTypeMetadata;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@Order(Ordered.HIGHEST_PRECEDENCE + 100)\npublic class OnArkEnabled extends SpringBootCondition {\n    private static final String ARK_TEST_CLASSLOADER_NAME   = \"com.alipay.sofa.ark.container.test.TestClassLoader\";\n    private static final String ARK_BIZ_CLASSLOADER_NAME    = \"com.alipay.sofa.ark.container.service.classloader.BizClassLoader\";\n    private static final String ARK_PLUGIN_CLASSLOADER_NAME = \"com.alipay.sofa.ark.container.service.classloader.PluginClassLoader\";\n\n    @Override\n    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {\n        String currentClassLoader = this.getClass().getClassLoader().getClass().getName();\n        if (ARK_TEST_CLASSLOADER_NAME.equals(currentClassLoader)\n            || ARK_BIZ_CLASSLOADER_NAME.equals(currentClassLoader)\n            || ARK_PLUGIN_CLASSLOADER_NAME.equals(currentClassLoader)) {\n            return new ConditionOutcome(true, \"SOFAArk has started.\");\n        } else if (ArkConfigs.isEmbedEnable()) {\n            return new ConditionOutcome(true, \"Embed SOFAArk has started.\");\n        } else {\n            return new ConditionOutcome(false, \"SOFAArk has not started.\");\n        }\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-common-springboot/src/main/java/com/alipay/sofa/ark/springboot/condition/OnSpringBootVersion.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot.condition;\n\nimport org.springframework.boot.SpringBootVersion;\nimport org.springframework.boot.autoconfigure.condition.ConditionOutcome;\nimport org.springframework.boot.autoconfigure.condition.SpringBootCondition;\nimport org.springframework.context.annotation.ConditionContext;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.core.type.AnnotatedTypeMetadata;\n\nimport java.util.Map;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@Order(Ordered.HIGHEST_PRECEDENCE + 100)\npublic class OnSpringBootVersion extends SpringBootCondition {\n    @Override\n    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {\n        Map<String, Object> springBootVersion = metadata\n            .getAnnotationAttributes(ConditionalOnSpringBootVersion.class.getCanonicalName());\n        if (springBootVersion == null || springBootVersion.get(\"version\") == null) {\n            return new ConditionOutcome(false, \"No specified spring boot version.\");\n        }\n        ConditionalOnSpringBootVersion.Version version = (ConditionalOnSpringBootVersion.Version) springBootVersion\n            .get(\"version\");\n        if (ConditionalOnSpringBootVersion.Version.ANY.equals(version)) {\n            return new ConditionOutcome(true, \"Conditional on Any Spring Boot.\");\n        } else if (ConditionalOnSpringBootVersion.Version.OneX.equals(version)) {\n            String bootVersion = SpringBootVersion.getVersion();\n            if (null != bootVersion && bootVersion.startsWith(\"1\")) {\n                return new ConditionOutcome(true, \"Conditional on OneX Spring Boot.\");\n            } else {\n                return new ConditionOutcome(false, \"Conditional on OneX Spring Boot.\");\n            }\n        } else if (ConditionalOnSpringBootVersion.Version.TwoX.equals(version)) {\n            String bootVersion = SpringBootVersion.getVersion();\n            if (null != bootVersion && bootVersion.startsWith(\"2\")) {\n                return new ConditionOutcome(true, \"Conditional on TwoX Spring Boot.\");\n            } else {\n                return new ConditionOutcome(false, \"Conditional on TwoX Spring Boot.\");\n            }\n        }\n        throw new IllegalStateException(\"Error Spring Boot Version.\");\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot1/CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Module Overview\n\n**Artifact ID**: `sofa-ark-compatible-springboot1`\n**Package**: `com.alipay.sofa.ark.springboot`\n\nThis module provides Spring Boot 1.x compatibility for SOFAArk.\n\n## Purpose\n\n- Enable Ark features in Spring Boot 1.x applications\n- Version-specific adapter implementations\n\n## Notes\n\n- Spring Boot 1.x is end-of-life\n- This module provides backward compatibility for legacy applications\n- For new projects, use Spring Boot 2.x or later\n\n## Dependencies\n\n- `sofa-ark-common-springboot` - Common integration code\n- Spring Boot 1.x (provided scope)\n\n## Used By\n\n- `ark-springboot-starter` - Conditionally loaded for Spring Boot 1.x apps"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot1/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>sofa-ark-support</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n        <relativePath>../../pom.xml</relativePath>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>sofa-ark-compatible-springboot1</artifactId>\n    <name>${project.groupId}:${project.artifactId}</name>\n\n    <properties>\n        <spring.boot.version>1.5.22.RELEASE</spring.boot.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-actuator</artifactId>\n            <version>${spring.boot.version}</version>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n            <version>${spring.boot.version}</version>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-api</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-common-springboot</artifactId>\n        </dependency>\n\n        <!-- test -->\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-support-starter</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-all</artifactId>\n            <version>${sofa.ark.version.old}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>web-ark-plugin</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot1/src/main/java/com/alipay/sofa/ark/springboot1/CompatibleSpringBoot1AutoConfiguration.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot1;\n\nimport com.alipay.sofa.ark.springboot.condition.ConditionalOnArkEnabled;\nimport com.alipay.sofa.ark.springboot.condition.ConditionalOnSpringBootVersion;\nimport com.alipay.sofa.ark.springboot1.endpoint.IntrospectBizEndpoint;\nimport com.alipay.sofa.ark.springboot1.endpoint.IntrospectBizEndpointMvcAdapter;\nimport org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnBean;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@Configuration\n@ConditionalOnSpringBootVersion(version = ConditionalOnSpringBootVersion.Version.OneX)\n@ConditionalOnArkEnabled\npublic class CompatibleSpringBoot1AutoConfiguration {\n\n    @ConditionalOnClass(name = \"org.springframework.boot.actuate.endpoint.Endpoint\")\n    public static class ConditionIntrospectEndpointConfiguration {\n        @Bean\n        @ConditionalOnEnabledEndpoint(\"bizState\")\n        public IntrospectBizEndpoint introspectBizEndpoint() {\n            return new IntrospectBizEndpoint();\n        }\n    }\n\n    @Bean\n    @ConditionalOnBean(IntrospectBizEndpoint.class)\n    @ConditionalOnWebApplication\n    public IntrospectBizEndpointMvcAdapter introspectBizEndpointMvcAdapter(IntrospectBizEndpoint introspectBizEndpoint) {\n        return new IntrospectBizEndpointMvcAdapter(introspectBizEndpoint);\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot1/src/main/java/com/alipay/sofa/ark/springboot1/endpoint/IntrospectBizEndpoint.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot1.endpoint;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport org.springframework.boot.actuate.endpoint.AbstractEndpoint;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class IntrospectBizEndpoint extends AbstractEndpoint<Object> {\n\n    public IntrospectBizEndpoint() {\n        super(\"bizState\", false, true);\n    }\n\n    @Override\n    public Object invoke() {\n        return ArkClient.checkBiz();\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot1/src/main/java/com/alipay/sofa/ark/springboot1/endpoint/IntrospectBizEndpointMvcAdapter.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot1.endpoint;\n\nimport org.springframework.boot.actuate.endpoint.mvc.AbstractEndpointMvcAdapter;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class IntrospectBizEndpointMvcAdapter extends\n                                            AbstractEndpointMvcAdapter<IntrospectBizEndpoint> {\n    public IntrospectBizEndpointMvcAdapter(IntrospectBizEndpoint delegate) {\n        super(delegate);\n        setPath(\"bizState\");\n    }\n\n    @Override\n    @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)\n    @ResponseBody\n    protected Object invoke() {\n        return super.invoke();\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot1/src/main/java/com/alipay/sofa/ark/springboot1/web/ArkAutoConfiguration.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot1.web;\n\nimport com.alipay.sofa.ark.springboot.condition.ConditionalOnArkEnabled;\nimport org.apache.catalina.startup.Tomcat;\nimport org.apache.coyote.UpgradeProtocol;\nimport org.springframework.boot.autoconfigure.AutoConfigureBefore;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.boot.autoconfigure.condition.SearchStrategy;\nimport org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration;\nimport org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;\nimport org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport javax.servlet.Servlet;\n\n/**\n * @author qixiaobo\n * @since 2.2.4\n */\n@Configuration\n@ConditionalOnArkEnabled\n@ConditionalOnClass(EmbeddedServletContainerAutoConfiguration.class)\n@AutoConfigureBefore(EmbeddedServletContainerAutoConfiguration.class)\npublic class ArkAutoConfiguration {\n    @Configuration\n    @ConditionalOnClass(value = { Servlet.class, Tomcat.class, UpgradeProtocol.class,\n            ArkTomcatEmbeddedServletContainerFactory.class }, name = {\n            \"com.alipay.sofa.ark.web.embed.tomcat.ArkTomcatEmbeddedWebappClassLoader\",\n            \"org.springframework.boot.context.embedded.EmbeddedServletContainerFactory\" })\n    @ConditionalOnMissingBean(value = { EmbeddedServletContainerFactory.class }, search = SearchStrategy.CURRENT)\n    public static class EmbeddedArkTomcat {\n        @Bean\n        @ConditionalOnMissingBean(ArkTomcatEmbeddedServletContainerFactory.class)\n        public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {\n            return new ArkTomcatEmbeddedServletContainerFactory();\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot1/src/main/java/com/alipay/sofa/ark/springboot1/web/ArkTomcatEmbeddedServletContainer.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot1.web;\n\nimport org.apache.catalina.*;\nimport org.apache.catalina.connector.Connector;\nimport org.apache.catalina.startup.Tomcat;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.apache.naming.ContextBindings;\nimport org.springframework.boot.context.embedded.EmbeddedServletContainer;\nimport org.springframework.boot.context.embedded.EmbeddedServletContainerException;\nimport org.springframework.boot.context.embedded.tomcat.ConnectorStartFailedException;\nimport org.springframework.util.Assert;\n\nimport javax.naming.NamingException;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * @author qixiaobo\n * @since 2.2.4\n */\npublic class ArkTomcatEmbeddedServletContainer implements EmbeddedServletContainer {\n    private static final Log                logger            = LogFactory\n                                                                  .getLog(ArkTomcatEmbeddedServletContainer.class);\n\n    private static final AtomicInteger      containerCounter  = new AtomicInteger(-1);\n\n    private final Object                    monitor           = new Object();\n\n    private final Map<Service, Connector[]> serviceConnectors = new HashMap<>();\n\n    private final Tomcat                    tomcat;\n\n    private final boolean                   autoStart;\n\n    private volatile boolean                started;\n\n    private Thread                          awaitThread;\n\n    private Tomcat                          arkEmbedTomcat;\n\n    /**\n     * Create a new {@link ArkTomcatEmbeddedServletContainer} instance.\n     * @param tomcat the underlying Tomcat server\n     */\n    public ArkTomcatEmbeddedServletContainer(Tomcat tomcat) {\n        this(tomcat, true);\n    }\n\n    /**\n     * Create a new {@link ArkTomcatEmbeddedServletContainer} instance.\n     * @param tomcat the underlying Tomcat server\n     * @param autoStart if the server should be started\n     */\n    public ArkTomcatEmbeddedServletContainer(Tomcat tomcat, boolean autoStart) {\n        Assert.notNull(tomcat, \"Tomcat Server must not be null\");\n        this.tomcat = tomcat;\n        this.autoStart = autoStart;\n        initialize();\n    }\n\n    public ArkTomcatEmbeddedServletContainer(Tomcat tomcat, boolean autoStart, Tomcat arkEmbedTomcat) {\n        this(tomcat, autoStart);\n        this.arkEmbedTomcat = arkEmbedTomcat;\n    }\n\n    private void initialize() throws EmbeddedServletContainerException {\n        logger.info(\"Tomcat initialized with port(s): \" + getPortsDescription(false));\n        synchronized (this.monitor) {\n            try {\n                addInstanceIdToEngineName();\n\n                Context context = findContext();\n                context.addLifecycleListener((event) -> {\n                    if (context.equals(event.getSource())\n                            && Lifecycle.START_EVENT.equals(event.getType())) {\n                        // Remove service connectors so that protocol binding doesn't\n                        // happen when the service is started.\n                        removeServiceConnectors();\n                    }\n                });\n\n                // Start the server to trigger initialization listeners\n                this.tomcat.start();\n\n                // We can re-throw failure exception directly in the main thread\n                rethrowDeferredStartupExceptions();\n\n                try {\n                    ContextBindings.bindClassLoader(context, context.getNamingToken(),\n                            Thread.currentThread().getContextClassLoader());\n                }\n                catch (NamingException ex) {\n                    // Naming is not enabled. Continue\n                }\n\n                // Unlike Jetty, all Tomcat threads are daemon threads. We create a\n                // blocking non-daemon to stop immediate shutdown\n                startDaemonAwaitThread();\n            }\n            catch (Exception ex) {\n                stopSilently();\n                throw new EmbeddedServletContainerException(\"Unable to start embedded Tomcat\", ex);\n            }\n        }\n    }\n\n    private Context findContext() {\n        for (Container child : this.tomcat.getHost().findChildren()) {\n            if (child instanceof Context) {\n                if (child.getParentClassLoader().equals(\n                    Thread.currentThread().getContextClassLoader())) {\n                    return (Context) child;\n                }\n            }\n        }\n        throw new IllegalStateException(\"The host does not contain a Context\");\n    }\n\n    private void addInstanceIdToEngineName() {\n        int instanceId = containerCounter.incrementAndGet();\n        if (instanceId > 0) {\n            Engine engine = this.tomcat.getEngine();\n            engine.setName(engine.getName() + \"-\" + instanceId);\n        }\n    }\n\n    private void removeServiceConnectors() {\n        for (Service service : this.tomcat.getServer().findServices()) {\n            Connector[] connectors = service.findConnectors().clone();\n            this.serviceConnectors.put(service, connectors);\n            for (Connector connector : connectors) {\n                service.removeConnector(connector);\n            }\n        }\n    }\n\n    private void rethrowDeferredStartupExceptions() throws Exception {\n        Container[] children = this.tomcat.getHost().findChildren();\n        for (Container container : children) {\n            // just to check current biz status\n            if (container.getParentClassLoader() == Thread.currentThread().getContextClassLoader()) {\n                if (!LifecycleState.STARTED.equals(container.getState())) {\n                    throw new IllegalStateException(container + \" failed to start\");\n                }\n            }\n        }\n    }\n\n    private void startDaemonAwaitThread() {\n        awaitThread = new Thread(\"container-\" + (containerCounter.get())) {\n\n            @Override\n            public void run() {\n                getTomcat().getServer().await();\n            }\n\n        };\n        awaitThread.setContextClassLoader(Thread.currentThread().getContextClassLoader());\n        awaitThread.setDaemon(false);\n        awaitThread.start();\n    }\n\n    @Override\n    public void start() throws EmbeddedServletContainerException {\n        synchronized (this.monitor) {\n            if (this.started) {\n                return;\n            }\n            try {\n                addPreviouslyRemovedConnectors();\n                this.tomcat.getConnector();\n                checkThatConnectorsHaveStarted();\n                this.started = true;\n                logger.info(\"Tomcat started on port(s): \" + getPortsDescription(true)\n                            + \" with context path '\" + getContextPath() + \"'\");\n            } catch (ConnectorStartFailedException ex) {\n                stopSilently();\n                throw ex;\n            } catch (Exception ex) {\n                throw new EmbeddedServletContainerException(\n                    \"Unable to start embedded Tomcat server\", ex);\n            } finally {\n                Context context = findContext();\n                ContextBindings.unbindClassLoader(context, context.getNamingToken(), getClass()\n                    .getClassLoader());\n            }\n        }\n    }\n\n    private void checkThatConnectorsHaveStarted() {\n        checkConnectorHasStarted(this.tomcat.getConnector());\n        for (Connector connector : this.tomcat.getService().findConnectors()) {\n            checkConnectorHasStarted(connector);\n        }\n    }\n\n    private void checkConnectorHasStarted(Connector connector) {\n        if (LifecycleState.FAILED.equals(connector.getState())) {\n            throw new ConnectorStartFailedException(connector.getPort());\n        }\n    }\n\n    public void stopSilently() {\n        stopContext();\n        try {\n            stopTomcatIfNecessary();\n        } catch (LifecycleException ex) {\n            // Ignore\n        }\n    }\n\n    private void stopContext() {\n        Context context = findContext();\n        getTomcat().getHost().removeChild(context);\n    }\n\n    private void stopTomcatIfNecessary() throws LifecycleException {\n        if (tomcat != arkEmbedTomcat) {\n            tomcat.destroy();\n        }\n        awaitThread.stop();\n    }\n\n    private void addPreviouslyRemovedConnectors() {\n        Service[] services = this.tomcat.getServer().findServices();\n        for (Service service : services) {\n            Connector[] connectors = this.serviceConnectors.get(service);\n            if (connectors != null) {\n                for (Connector connector : connectors) {\n                    service.addConnector(connector);\n                    if (!this.autoStart) {\n                        stopProtocolHandler(connector);\n                    }\n                }\n                this.serviceConnectors.remove(service);\n            }\n        }\n    }\n\n    private void stopProtocolHandler(Connector connector) {\n        try {\n            connector.getProtocolHandler().stop();\n        } catch (Exception ex) {\n            logger.error(\"Cannot pause connector: \", ex);\n        }\n    }\n\n    Map<Service, Connector[]> getServiceConnectors() {\n        return this.serviceConnectors;\n    }\n\n    @Override\n    public void stop() throws EmbeddedServletContainerException {\n        synchronized (this.monitor) {\n            boolean wasStarted = this.started;\n            try {\n                this.started = false;\n                try {\n                    stopContext();\n                    stopTomcatIfNecessary();\n                } catch (Throwable ex) {\n                    // swallow and continue\n                }\n            } catch (Exception ex) {\n                throw new EmbeddedServletContainerException(\"Unable to stop embedded Tomcat\", ex);\n            } finally {\n                if (wasStarted) {\n                    containerCounter.decrementAndGet();\n                }\n            }\n        }\n    }\n\n    private String getPortsDescription(boolean localPort) {\n        StringBuilder ports = new StringBuilder();\n        for (Connector connector : this.tomcat.getService().findConnectors()) {\n            if (ports.length() != 0) {\n                ports.append(' ');\n            }\n            int port = localPort ? connector.getLocalPort() : connector.getPort();\n            ports.append(port).append(\" (\").append(connector.getScheme()).append(')');\n        }\n        return ports.toString();\n    }\n\n    @Override\n    public int getPort() {\n        Connector connector = this.tomcat.getConnector();\n        if (connector != null) {\n            return connector.getLocalPort();\n        }\n        return 0;\n    }\n\n    private String getContextPath() {\n        return findContext().getPath();\n    }\n\n    /**\n     * Returns access to the underlying Tomcat server.\n     * @return the Tomcat server\n     */\n    public Tomcat getTomcat() {\n        return this.tomcat;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot1/src/main/java/com/alipay/sofa/ark/springboot1/web/ArkTomcatEmbeddedServletContainerFactory.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot1.web;\n\nimport com.alipay.sofa.ark.common.util.AssertUtils;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.service.ArkInject;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.web.EmbeddedServerService;\nimport org.apache.catalina.*;\nimport org.apache.catalina.connector.Connector;\nimport org.apache.catalina.core.StandardContext;\nimport org.apache.catalina.loader.WebappLoader;\nimport org.apache.catalina.startup.Tomcat;\nimport org.apache.catalina.webresources.StandardRoot;\nimport org.apache.tomcat.util.scan.StandardJarScanFilter;\nimport org.springframework.boot.context.embedded.EmbeddedServletContainer;\nimport org.springframework.boot.context.embedded.EmbeddedServletContainerException;\nimport org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer;\nimport org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;\nimport org.springframework.boot.web.servlet.ServletContextInitializer;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.StringUtils;\n\nimport javax.servlet.ServletContainerInitializer;\nimport java.io.File;\nimport java.net.URL;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.ROOT_WEB_CONTEXT_PATH;\n\n/**\n * @author qixiaobo\n * @since 2.2.4\n */\npublic class ArkTomcatEmbeddedServletContainerFactory extends TomcatEmbeddedServletContainerFactory {\n    private static final Charset          DEFAULT_CHARSET = StandardCharsets.UTF_8;\n\n    @ArkInject\n    private EmbeddedServerService<Tomcat> embeddedServerService;\n\n    @ArkInject\n    private BizManagerService             bizManagerService;\n\n    private File                          baseDirectory;\n\n    private String                        protocol        = DEFAULT_PROTOCOL;\n\n    private int                           backgroundProcessorDelay;\n\n    @Override\n    public EmbeddedServletContainer getEmbeddedServletContainer(ServletContextInitializer... initializers) {\n        if (embeddedServerService == null) {\n            Tomcat tomcat = new Tomcat();\n            File baseDir = (this.baseDirectory != null) ? this.baseDirectory\n                : createTempDir(\"tomcat\");\n            tomcat.setBaseDir(baseDir.getAbsolutePath());\n            Connector connector = new Connector(this.protocol);\n            tomcat.getService().addConnector(connector);\n            customizeConnector(connector);\n            tomcat.setConnector(connector);\n            tomcat.getHost().setAutoDeploy(false);\n            configureEngine(tomcat.getEngine());\n            for (Connector additionalConnector : this.getAdditionalTomcatConnectors()) {\n                tomcat.getService().addConnector(additionalConnector);\n            }\n            prepareContext(tomcat.getHost(), initializers);\n            return getEmbeddedServletContainer(tomcat);\n        } else if (embeddedServerService.getEmbedServer(getPort()) == null) {\n            embeddedServerService.putEmbedServer(getPort(), initEmbedTomcat());\n        }\n        Tomcat embedTomcat = embeddedServerService.getEmbedServer(getPort());\n        prepareContext(embedTomcat.getHost(), initializers);\n\n        return getEmbeddedServletContainer(embedTomcat);\n    }\n\n    @Override\n    public String getContextPath() {\n        String contextPath = super.getContextPath();\n        if (bizManagerService == null) {\n            return contextPath;\n        }\n        Biz biz = bizManagerService.getBizByClassLoader(Thread.currentThread()\n            .getContextClassLoader());\n        if (!StringUtils.isEmpty(contextPath)) {\n            return contextPath;\n        } else if (biz != null) {\n            if (StringUtils.isEmpty(biz.getWebContextPath())) {\n                return ROOT_WEB_CONTEXT_PATH;\n            }\n            return biz.getWebContextPath();\n        } else {\n            return ROOT_WEB_CONTEXT_PATH;\n        }\n    }\n\n    private Tomcat initEmbedTomcat() {\n        Tomcat tomcat = new Tomcat();\n        File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir(\"tomcat\");\n        tomcat.setBaseDir(baseDir.getAbsolutePath());\n        Connector connector = new Connector(this.protocol);\n        tomcat.getService().addConnector(connector);\n        customizeConnector(connector);\n        tomcat.setConnector(connector);\n        tomcat.getHost().setAutoDeploy(false);\n        configureEngine(tomcat.getEngine());\n        for (Connector additionalConnector : getAdditionalTomcatConnectors()) {\n            tomcat.getService().addConnector(additionalConnector);\n        }\n        return tomcat;\n    }\n\n    @Override\n    public void setBaseDirectory(File baseDirectory) {\n        this.baseDirectory = baseDirectory;\n    }\n\n    /**\n     * The Tomcat protocol to use when create the {@link Connector}.\n     *\n     * @param protocol the protocol\n     * @see Connector#Connector(String)\n     */\n    @Override\n    public void setProtocol(String protocol) {\n        AssertUtils.isFalse(StringUtils.isEmpty(protocol), \"Protocol must not be empty\");\n        this.protocol = protocol;\n    }\n\n    @Override\n    public void setBackgroundProcessorDelay(int delay) {\n        this.backgroundProcessorDelay = delay;\n    }\n\n    private void configureEngine(Engine engine) {\n        engine.setBackgroundProcessorDelay(this.backgroundProcessorDelay);\n        for (Valve valve : getEngineValves()) {\n            engine.getPipeline().addValve(valve);\n        }\n    }\n\n    @Override\n    protected void postProcessContext(Context context) {\n        ((WebappLoader) context.getLoader())\n            .setLoaderClass(\"com.alipay.sofa.ark.web.embed.tomcat.ArkTomcatEmbeddedWebappClassLoader\");\n    }\n\n    @Override\n    protected void prepareContext(Host host, ServletContextInitializer[] initializers) {\n        if (host.getState() == LifecycleState.NEW) {\n            super.prepareContext(host, initializers);\n        } else {\n            File documentRoot = getValidDocumentRoot();\n            StandardContext context = new StandardContext();\n            if (documentRoot != null) {\n                context.setResources(new StandardRoot(context));\n            }\n            context.setName(getContextPath());\n            context.setDisplayName(getDisplayName());\n            context.setPath(getContextPath());\n            File docBase = (documentRoot != null) ? documentRoot : createTempDir(\"tomcat-docbase\");\n            context.setDocBase(docBase.getAbsolutePath());\n            context.addLifecycleListener(new Tomcat.FixContextListener());\n            context.setParentClassLoader(Thread.currentThread().getContextClassLoader());\n            resetDefaultLocaleMapping(context);\n            addLocaleMappings(context);\n            context.setUseRelativeRedirects(false);\n            configureTldSkipPatterns(context);\n            WebappLoader loader = new WebappLoader(context.getParentClassLoader());\n            loader\n                .setLoaderClass(\"com.alipay.sofa.ark.web.embed.tomcat.ArkTomcatEmbeddedWebappClassLoader\");\n            loader.setDelegate(true);\n            context.setLoader(loader);\n            if (isRegisterDefaultServlet()) {\n                addDefaultServlet(context);\n            }\n            if (shouldRegisterJspServlet()) {\n                addJspServlet(context);\n                addJasperInitializer(context);\n            }\n            context.addLifecycleListener(new StaticResourceConfigurer(context));\n            ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);\n            context.setParent(host);\n            configureContext(context, initializersToUse);\n            host.addChild(context);\n        }\n    }\n\n    /**\n     * Override Tomcat's default locale mappings to align with other servers. See\n     * {@code org.apache.catalina.util.CharsetMapperDefault.properties}.\n     *\n     * @param context the context to reset\n     */\n    private void resetDefaultLocaleMapping(StandardContext context) {\n        context.addLocaleEncodingMappingParameter(Locale.ENGLISH.toString(),\n            DEFAULT_CHARSET.displayName());\n        context.addLocaleEncodingMappingParameter(Locale.FRENCH.toString(),\n            DEFAULT_CHARSET.displayName());\n    }\n\n    private void addLocaleMappings(StandardContext context) {\n        for (Map.Entry<Locale, Charset> entry : getLocaleCharsetMappings().entrySet()) {\n            context.addLocaleEncodingMappingParameter(entry.getKey().toString(), entry.getValue()\n                .toString());\n        }\n    }\n\n    private void configureTldSkipPatterns(StandardContext context) {\n        StandardJarScanFilter filter = new StandardJarScanFilter();\n        filter.setTldSkip(StringUtils.collectionToCommaDelimitedString(getTldSkipPatterns()));\n        context.getJarScanner().setJarScanFilter(filter);\n    }\n\n    private void addDefaultServlet(Context context) {\n        Wrapper defaultServlet = context.createWrapper();\n        defaultServlet.setName(\"default\");\n        defaultServlet.setServletClass(\"org.apache.catalina.servlets.DefaultServlet\");\n        defaultServlet.addInitParameter(\"debug\", \"0\");\n        defaultServlet.addInitParameter(\"listings\", \"false\");\n        defaultServlet.setLoadOnStartup(1);\n        // Otherwise the default location of a Spring DispatcherServlet cannot be set\n        defaultServlet.setOverridable(true);\n        context.addChild(defaultServlet);\n        context.addServletMappingDecoded(\"/\", \"default\");\n    }\n\n    private void addJspServlet(Context context) {\n        Wrapper jspServlet = context.createWrapper();\n        jspServlet.setName(\"jsp\");\n        jspServlet.setServletClass(getJspServlet().getClassName());\n        jspServlet.addInitParameter(\"fork\", \"false\");\n        for (Map.Entry<String, String> initParameter : getJspServlet().getInitParameters()\n            .entrySet()) {\n            jspServlet.addInitParameter(initParameter.getKey(), initParameter.getValue());\n        }\n        jspServlet.setLoadOnStartup(3);\n        context.addChild(jspServlet);\n        addServletMapping(context, \"*.jsp\", \"jsp\");\n        addServletMapping(context, \"*.jspx\", \"jsp\");\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    private void addServletMapping(Context context, String pattern, String name) {\n        context.addServletMapping(pattern, name);\n    }\n\n    private void addJasperInitializer(StandardContext context) {\n        try {\n            ServletContainerInitializer initializer = (ServletContainerInitializer) ClassUtils\n                .forName(\"org.apache.jasper.servlet.JasperInitializer\", null).newInstance();\n            context.addServletContainerInitializer(initializer, null);\n        } catch (Exception ex) {\n            // Probably not Tomcat 8\n        }\n    }\n\n    final class StaticResourceConfigurer implements LifecycleListener {\n\n        private final Context context;\n\n        private StaticResourceConfigurer(Context context) {\n            this.context = context;\n        }\n\n        @Override\n        public void lifecycleEvent(LifecycleEvent event) {\n            if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {\n                addResourceJars(getUrlsOfJarsWithMetaInfResources());\n            }\n        }\n\n        private void addResourceJars(List<URL> resourceJarUrls) {\n            for (URL url : resourceJarUrls) {\n                String path = url.getPath();\n                if (path.endsWith(\".jar\") || path.endsWith(\".jar!/\")) {\n                    String jar = url.toString();\n                    if (!jar.startsWith(\"jar:\")) {\n                        // A jar file in the file system. Convert to Jar URL.\n                        jar = \"jar:\" + jar + \"!/\";\n                    }\n                    addResourceSet(jar);\n                } else {\n                    addResourceSet(url.toString());\n                }\n            }\n        }\n\n        private void addResourceSet(String resource) {\n            try {\n                if (isInsideNestedJar(resource)) {\n                    // It's a nested jar but we now don't want the suffix because Tomcat\n                    // is going to try and locate it as a root URL (not the resource\n                    // inside it)\n                    resource = resource.substring(0, resource.length() - 2);\n                }\n                URL url = new URL(resource);\n                String path = \"/META-INF/resources\";\n                this.context.getResources().createWebResourceSet(\n                    WebResourceRoot.ResourceSetType.RESOURCE_JAR, \"/\", url, path);\n            } catch (Exception ex) {\n                // Ignore (probably not a directory)\n            }\n        }\n\n        private boolean isInsideNestedJar(String dir) {\n            return dir.indexOf(\"!/\") < dir.lastIndexOf(\"!/\");\n        }\n    }\n\n    @Override\n    protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(Tomcat tomcat) {\n        throw new EmbeddedServletContainerException(\n            \"not support to invoke getTomcatEmbeddedServletContainer\", null);\n    }\n\n    protected EmbeddedServletContainer getEmbeddedServletContainer(Tomcat tomcat) {\n        return new ArkTomcatEmbeddedServletContainer(tomcat, getPort() >= 0, tomcat);\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot1/src/main/java/com/alipay/sofa/ark/springboot1/web/SwitchClassLoaderFilter.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot1.web;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.servlet.Filter;\nimport javax.servlet.FilterChain;\nimport javax.servlet.FilterConfig;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport java.io.IOException;\n\npublic class SwitchClassLoaderFilter implements Filter {\n\n    /**\n     * using user logger not ArkLogger, to print this log into user log dir\n     */\n    private Logger logger = LoggerFactory.getLogger(SwitchClassLoaderFilter.class);\n\n    @Override\n    public void init(FilterConfig filterConfig) throws ServletException {\n    }\n\n    @Override\n    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,\n                         FilterChain filterChain) throws IOException, ServletException {\n        ClassLoader oldClassloader = Thread.currentThread().getContextClassLoader();\n        try {\n            if (\"com.alipay.sofa.ark.web.embed.tomcat.ArkTomcatEmbeddedWebappClassLoader\"\n                .equals(oldClassloader.getClass().getName())) {\n                ClassLoader bizClassLoader = oldClassloader.getParent();\n                if (bizClassLoader != null) {\n                    logger.debug(\"switch web classLoader from {} to {}\", oldClassloader,\n                        bizClassLoader);\n                    Thread.currentThread().setContextClassLoader(bizClassLoader);\n                }\n            }\n\n            filterChain.doFilter(servletRequest, servletResponse);\n        } finally {\n            Thread.currentThread().setContextClassLoader(oldClassloader);\n        }\n    }\n\n    @Override\n    public void destroy() {\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot1/src/main/resources/META-INF/spring.factories",
    "content": "org.springframework.boot.autoconfigure.EnableAutoConfiguration=\\\n  com.alipay.sofa.ark.springboot1.CompatibleSpringBoot1AutoConfiguration,\\\n  com.alipay.sofa.ark.springboot1.web.ArkAutoConfiguration\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot1/src/test/java/com/alipay/sofa/ark/springboot1/web/ArkTomcatEmbeddedServletContainerTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot1.web;\n\nimport com.alipay.sofa.ark.web.embed.tomcat.EmbeddedServerServiceImpl;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.lang.reflect.Field;\n\n/**\n * @author qixiaobo\n * @since 0.6.0\n */\npublic class ArkTomcatEmbeddedServletContainerTest {\n\n    private ArkTomcatEmbeddedServletContainerFactory arkTomcatEmbeddedServletContainerFactory;\n\n    private ArkTomcatEmbeddedServletContainer        arkTomcatEmbeddedServletContainer;\n\n    @Before\n    public void setUp() throws Exception {\n        arkTomcatEmbeddedServletContainerFactory = new ArkTomcatEmbeddedServletContainerFactory();\n        Field field = ArkTomcatEmbeddedServletContainerFactory.class\n            .getDeclaredField(\"embeddedServerService\");\n        field.setAccessible(true);\n        field.set(arkTomcatEmbeddedServletContainerFactory, new EmbeddedServerServiceImpl());\n        arkTomcatEmbeddedServletContainer = (ArkTomcatEmbeddedServletContainer) arkTomcatEmbeddedServletContainerFactory\n            .getEmbeddedServletContainer();\n    }\n\n    @After\n    public void tearDown() {\n    }\n\n    @Test\n    public void testGetWebServerWithEmbeddedServerServiceNull() {\n        //        NOTE: tomcat can not be stopped and restarted due to a Spring context destroy problem.\n        //        Spring community will fix this issue in the future, so catch all exception now.\n        try {\n            arkTomcatEmbeddedServletContainer.stopSilently();\n        } catch (Exception e) {\n        }\n        try {\n            arkTomcatEmbeddedServletContainer.stop();\n        } catch (Exception e) {\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot1/src/test/java/com/alipay/sofa/ark/springboot1/web/ArkTomcatServletWebServerFactoryTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot1.web;\n\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.container.service.biz.BizManagerServiceImpl;\nimport org.apache.catalina.Context;\nimport org.apache.catalina.LifecycleException;\nimport org.apache.catalina.core.StandardContext;\nimport org.apache.catalina.core.StandardHost;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport org.springframework.boot.web.servlet.ServletContextInitializer;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.ROOT_WEB_CONTEXT_PATH;\nimport static com.alipay.sofa.ark.spi.model.BizState.RESOLVED;\nimport static java.lang.Thread.currentThread;\nimport static org.junit.Assert.assertEquals;\n\n/**\n * @author qixiaobo\n * @since 0.6.0\n */\npublic class ArkTomcatServletWebServerFactoryTest {\n\n    private ArkTomcatEmbeddedServletContainerFactory arkTomcatEmbeddedServletContainerFactory = new ArkTomcatEmbeddedServletContainerFactory();\n\n    private ClassLoader                              currentThreadContextClassLoader;\n\n    @Before\n    public void setUp() {\n        currentThreadContextClassLoader = currentThread().getContextClassLoader();\n    }\n\n    @After\n    public void tearDown() {\n        currentThread().setContextClassLoader(currentThreadContextClassLoader);\n    }\n\n    @Test\n    public void testGetWebServerWithEmbeddedServerServiceNull() {\n        assertEquals(ArkTomcatEmbeddedServletContainer.class,\n            arkTomcatEmbeddedServletContainerFactory.getEmbeddedServletContainer().getClass());\n    }\n\n    @Test\n    public void testGetContextPath() throws Exception {\n\n        assertEquals(\"\", arkTomcatEmbeddedServletContainerFactory.getContextPath());\n\n        BizManagerServiceImpl bizManagerService = new BizManagerServiceImpl();\n        Field field = ArkTomcatEmbeddedServletContainerFactory.class\n            .getDeclaredField(\"bizManagerService\");\n        field.setAccessible(true);\n        field.set(arkTomcatEmbeddedServletContainerFactory, bizManagerService);\n        assertEquals(ROOT_WEB_CONTEXT_PATH,\n            arkTomcatEmbeddedServletContainerFactory.getContextPath());\n\n        BizModel biz = new BizModel();\n        biz.setBizName(\"bbb\");\n        biz.setBizState(RESOLVED);\n        biz.setBizVersion(\"ccc\");\n        biz.setClassLoader(this.getClass().getClassLoader());\n        bizManagerService.registerBiz(biz);\n        assertEquals(ROOT_WEB_CONTEXT_PATH,\n            arkTomcatEmbeddedServletContainerFactory.getContextPath());\n\n        biz.setWebContextPath(\"/ddd\");\n        currentThread().setContextClassLoader(biz.getBizClassLoader());\n        assertEquals(\"/ddd\", arkTomcatEmbeddedServletContainerFactory.getContextPath());\n\n        arkTomcatEmbeddedServletContainerFactory.setContextPath(\"/aaa\");\n        assertEquals(\"/aaa\", arkTomcatEmbeddedServletContainerFactory.getContextPath());\n    }\n\n    @Test\n    public void testPrepareContext() throws LifecycleException {\n\n        StandardHost host = new StandardHost();\n        host.init();\n\n        assertEquals(0, host.getChildren().length);\n        arkTomcatEmbeddedServletContainerFactory.setRegisterDefaultServlet(true);\n        currentThread().setContextClassLoader(this.getClass().getClassLoader());\n        arkTomcatEmbeddedServletContainerFactory.prepareContext(host,\n            new ServletContextInitializer[] {});\n        assertEquals(1, host.getChildren().length);\n    }\n\n    @Test\n    public void testOtherMethods() {\n        arkTomcatEmbeddedServletContainerFactory.setBackgroundProcessorDelay(10);\n        arkTomcatEmbeddedServletContainerFactory.setBaseDirectory(null);\n        arkTomcatEmbeddedServletContainerFactory.setProtocol(\"8888\");\n    }\n\n    @Test\n    public void testStaticResourceConfigurer() throws Exception {\n\n        List<URL> urls = new ArrayList<>();\n        urls.add(new URL(\"file:///aaa.jar!/\"));\n        urls.add(new URL(\"jar:file:///aaa.jar!/\"));\n        urls.add(new URL(\"file:///aaa\"));\n        urls.add(new URL(\"file:///!/aaa!/\"));\n\n        Constructor<ArkTomcatEmbeddedServletContainerFactory.StaticResourceConfigurer> declaredConstructor = ArkTomcatEmbeddedServletContainerFactory.StaticResourceConfigurer.class\n            .getDeclaredConstructor(ArkTomcatEmbeddedServletContainerFactory.class, Context.class);\n        declaredConstructor.setAccessible(true);\n        ArkTomcatEmbeddedServletContainerFactory.StaticResourceConfigurer staticResourceConfigurer = declaredConstructor\n            .newInstance(arkTomcatEmbeddedServletContainerFactory, new StandardContext());\n\n        Method addResourceJars = ArkTomcatEmbeddedServletContainerFactory.StaticResourceConfigurer.class\n            .getDeclaredMethod(\"addResourceJars\", List.class);\n        addResourceJars.setAccessible(true);\n        addResourceJars.invoke(staticResourceConfigurer, urls);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot1/src/test/java/com/alipay/sofa/ark/test/springboot1/IntrospectBizEndpointOnArkDisabledTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.test.springboot1;\n\nimport com.alipay.sofa.ark.springboot1.web.ArkAutoConfiguration;\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.lang.reflect.Field;\nimport java.net.URL;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class IntrospectBizEndpointOnArkDisabledTest {\n\n    @After\n    public void removeTomcatInit() {\n        try {\n            Field urlFactory = URL.class.getDeclaredField(\"factory\");\n            urlFactory.setAccessible(true);\n            urlFactory.set(null, null);\n        } catch (Throwable t) {\n            // ignore\n        }\n    }\n\n    @Test\n    public void testIntrospectBizEndpoint() {\n        SpringApplication springApplication = new SpringApplication(EmptyConfiguration.class);\n        ConfigurableApplicationContext applicationContext = springApplication.run(new String[] {});\n        Assert.assertFalse(applicationContext.containsBean(\"introspectBizEndpoint\"));\n        Assert.assertFalse(applicationContext.containsBean(\"introspectBizEndpointMvcAdapter\"));\n        applicationContext.close();\n    }\n\n    @Configuration\n    @EnableAutoConfiguration(exclude = ArkAutoConfiguration.class)\n    static class EmptyConfiguration {\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot1/src/test/java/com/alipay/sofa/ark/test/springboot1/IntrospectBizEndpointOnArkEnabledTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.test.springboot1;\n\nimport com.alipay.sofa.ark.support.runner.ArkJUnit4EmbedRunner;\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.lang.reflect.Field;\nimport java.net.URL;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@RunWith(ArkJUnit4EmbedRunner.class)\npublic class IntrospectBizEndpointOnArkEnabledTest {\n\n    @After\n    public void removeTomcatInit() {\n        try {\n            Field urlFactory = URL.class.getDeclaredField(\"factory\");\n            urlFactory.setAccessible(true);\n            urlFactory.set(null, null);\n        } catch (Throwable t) {\n            // ignore\n        }\n    }\n\n    @Test\n    public void testIntrospectBizEndpoint() {\n        SpringApplication springApplication = new SpringApplication(\n            IntrospectBizEndpointOnArkDisabledTest.EmptyConfiguration.class);\n        ConfigurableApplicationContext applicationContext = springApplication.run(new String[] {});\n        Assert.assertTrue(applicationContext.containsBean(\"introspectBizEndpoint\"));\n        Assert.assertTrue(applicationContext.containsBean(\"introspectBizEndpointMvcAdapter\"));\n        applicationContext.close();\n    }\n\n    @Test\n    public void testDisableBizStateEndpoint() {\n        Map<String, Object> properties = new HashMap<>();\n        properties.put(\"endpoints.bizState.enabled\", \"false\");\n        SpringApplication springApplication = new SpringApplication(\n            IntrospectBizEndpointOnArkDisabledTest.EmptyConfiguration.class);\n        springApplication.setDefaultProperties(properties);\n        ConfigurableApplicationContext applicationContext = springApplication.run(new String[] {});\n        Assert.assertFalse(applicationContext.containsBean(\"introspectBizEndpoint\"));\n        Assert.assertFalse(applicationContext.containsBean(\"introspectBizEndpointMvcAdapter\"));\n        applicationContext.close();\n    }\n\n    @Configuration\n    @EnableAutoConfiguration\n    static class EmptyConfiguration {\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot1/src/test/resources/logback.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration>\n\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>\n                %d{dd-MM-yyyy HH:mm:ss.SSS} %magenta([%thread]) %highlight(%-5level) %logger{36}.%M - %msg%n\n            </pattern>\n        </encoder>\n    </appender>\n\n    <root level=\"info\">\n        <appender-ref ref=\"STDOUT\"/>\n    </root>\n\n</configuration>"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot2/CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Module Overview\n\n**Artifact ID**: `sofa-ark-compatible-springboot2`\n**Package**: `com.alipay.sofa.ark.springboot`\n\nThis module provides Spring Boot 2.x compatibility for SOFAArk.\n\n## Purpose\n\n- Enable Ark features in Spring Boot 2.x applications\n- Version-specific adapter implementations\n\n## Dependencies\n\n- `sofa-ark-common-springboot` - Common integration code\n- Spring Boot 2.x (provided scope)\n\n## Used By\n\n- `ark-springboot-starter` - Conditionally loaded for Spring Boot 2.x apps"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot2/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>sofa-ark-support</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n        <relativePath>../../pom.xml</relativePath>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>sofa-ark-compatible-springboot2</artifactId>\n    <name>${project.groupId}:${project.artifactId}</name>\n\n    <properties>\n        <spring.boot.version>2.7.14</spring.boot.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n            <version>${spring.boot.version}</version>\n            <scope>provided</scope>\n            <exclusions>\n                <exclusion>\n                    <artifactId>spring-boot-starter-logging</artifactId>\n                    <groupId>org.springframework.boot</groupId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n            <version>${spring.boot.version}</version>\n            <scope>provided</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-api</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-common-springboot</artifactId>\n        </dependency>\n\n        <!--test-->\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-support-starter</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-all</artifactId>\n            <version>${sofa.ark.version.old}</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <profiles>\n        <profile>\n            <id>jdk7</id>\n            <activation>\n                <jdk>1.7</jdk>\n            </activation>\n            <build>\n                <plugins>\n                    <plugin>\n                        <groupId>org.apache.maven.plugins</groupId>\n                        <artifactId>maven-surefire-plugin</artifactId>\n                        <configuration>\n                            <excludes>\n                                <exclude>**/SpringBoot2*Test.java</exclude>\n                            </excludes>\n                        </configuration>\n                        <dependencies>\n                            <dependency>\n                                <groupId>org.apache.maven.surefire</groupId>\n                                <artifactId>surefire-junit47</artifactId>\n                                <version>${surefire.version}</version>\n                            </dependency>\n                            <dependency>\n                                <groupId>org.apache.maven.surefire</groupId>\n                                <artifactId>surefire-testng</artifactId>\n                                <version>${surefire.version}</version>\n                            </dependency>\n                        </dependencies>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n    </profiles>\n\n</project>\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot2/src/main/java/com/alipay/sofa/ark/springboot2/CompatibleSpringBoot2AutoConfiguration.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot2;\n\nimport com.alipay.sofa.ark.springboot.condition.ConditionalOnArkEnabled;\nimport com.alipay.sofa.ark.springboot.condition.ConditionalOnSpringBootVersion;\nimport com.alipay.sofa.ark.springboot2.endpoint.IntrospectBizEndpoint;\nimport org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@Configuration\n@ConditionalOnSpringBootVersion(version = ConditionalOnSpringBootVersion.Version.TwoX)\n@ConditionalOnArkEnabled\npublic class CompatibleSpringBoot2AutoConfiguration {\n    @ConditionalOnClass(name = \"org.springframework.boot.actuate.endpoint.annotation.Endpoint\")\n    public static class ConditionIntrospectEndpointConfiguration {\n        @Bean\n        @ConditionalOnAvailableEndpoint\n        public IntrospectBizEndpoint introspectBizEndpoint() {\n            return new IntrospectBizEndpoint();\n        }\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot2/src/main/java/com/alipay/sofa/ark/springboot2/endpoint/IntrospectBizEndpoint.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot2.endpoint;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport org.springframework.boot.actuate.endpoint.annotation.Endpoint;\nimport org.springframework.boot.actuate.endpoint.annotation.ReadOperation;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@Endpoint(id = \"bizState\")\npublic class IntrospectBizEndpoint {\n    @ReadOperation\n    public Object bizState() {\n        return ArkClient.checkBiz();\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot2/src/main/java/com/alipay/sofa/ark/springboot2/web/SwitchClassLoaderFilter.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot2.web;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.servlet.Filter;\nimport javax.servlet.FilterChain;\nimport javax.servlet.FilterConfig;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport java.io.IOException;\n\npublic class SwitchClassLoaderFilter implements Filter {\n\n    /**\n     * using user logger not ArkLogger, to print this log into user log dir\n     */\n    private Logger logger = LoggerFactory.getLogger(SwitchClassLoaderFilter.class);\n\n    @Override\n    public void init(FilterConfig filterConfig) throws ServletException {\n        Filter.super.init(filterConfig);\n    }\n\n    @Override\n    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,\n                         FilterChain filterChain) throws IOException, ServletException {\n        ClassLoader oldClassloader = Thread.currentThread().getContextClassLoader();\n        try {\n            if (\"com.alipay.sofa.ark.web.embed.tomcat.ArkTomcatEmbeddedWebappClassLoader\"\n                .equals(oldClassloader.getClass().getName())) {\n                ClassLoader bizClassLoader = oldClassloader.getParent();\n                if (bizClassLoader != null) {\n                    logger.debug(\"switch web classLoader from {} to {}\", oldClassloader,\n                        bizClassLoader);\n                    Thread.currentThread().setContextClassLoader(bizClassLoader);\n                }\n            }\n\n            filterChain.doFilter(servletRequest, servletResponse);\n        } finally {\n            Thread.currentThread().setContextClassLoader(oldClassloader);\n        }\n    }\n\n    @Override\n    public void destroy() {\n        Filter.super.destroy();\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot2/src/main/resources/META-INF/spring.factories",
    "content": "org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.alipay.sofa.ark.springboot2.CompatibleSpringBoot2AutoConfiguration"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot2/src/test/java/com/alipay/sofa/ark/test/springboot2/SpringBoot2IntrospectBizEndpointOnArkDisabledTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.test.springboot2;\n\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.lang.reflect.Field;\nimport java.net.URL;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class SpringBoot2IntrospectBizEndpointOnArkDisabledTest {\n\n    @After\n    public void removeTomcatInit() {\n        try {\n            Field urlFactory = URL.class.getDeclaredField(\"factory\");\n            urlFactory.setAccessible(true);\n            urlFactory.set(null, null);\n        } catch (Throwable t) {\n            // ignore\n        }\n    }\n\n    @Test\n    public void testIntrospectBizEndpoint() {\n        SpringApplication springApplication = new SpringApplication(EmptyConfiguration.class);\n        ConfigurableApplicationContext applicationContext = springApplication.run(new String[] {});\n        Assert.assertFalse(applicationContext.containsBean(\"introspectBizEndpoint\"));\n        applicationContext.close();\n    }\n\n    @Configuration\n    @EnableAutoConfiguration\n    static class EmptyConfiguration {\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot2/src/test/java/com/alipay/sofa/ark/test/springboot2/SpringBoot2IntrospectBizEndpointOnArkEnabledTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.test.springboot2;\n\nimport com.alipay.sofa.ark.support.runner.ArkJUnit4EmbedRunner;\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.lang.reflect.Field;\nimport java.net.URL;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@RunWith(ArkJUnit4EmbedRunner.class)\npublic class SpringBoot2IntrospectBizEndpointOnArkEnabledTest {\n\n    @After\n    public void removeTomcatInit() {\n        try {\n            Field urlFactory = URL.class.getDeclaredField(\"factory\");\n            urlFactory.setAccessible(true);\n            urlFactory.set(null, null);\n        } catch (Throwable t) {\n            // ignore\n        }\n    }\n\n    @Test\n    public void testIntrospectBizEndpoint() {\n        Map<String, Object> properties = new HashMap<>();\n        properties.put(\"management.endpoints.web.exposure.include\", \"*\");\n        SpringApplication springApplication = new SpringApplication(EmptyConfiguration.class);\n        springApplication.setDefaultProperties(properties);\n        ConfigurableApplicationContext applicationContext = springApplication.run(new String[] {});\n        Assert.assertTrue(applicationContext.containsBean(\"introspectBizEndpoint\"));\n        applicationContext.close();\n    }\n\n    @Test\n    public void testDisableBizStateEndpoint() {\n        Map<String, Object> properties = new HashMap<>();\n        properties.put(\"management.endpoint.bizState.enabled\", \"false\");\n        SpringApplication springApplication = new SpringApplication(EmptyConfiguration.class);\n        springApplication.setDefaultProperties(properties);\n        ConfigurableApplicationContext applicationContext = springApplication.run(new String[] {});\n        Assert.assertFalse(applicationContext.containsBean(\"introspectBizEndpoint\"));\n        applicationContext.close();\n    }\n\n    @Configuration\n    @EnableAutoConfiguration\n    static class EmptyConfiguration {\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-compatible-springboot2/src/test/resources/logback.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration>\n\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>\n                %d{dd-MM-yyyy HH:mm:ss.SSS} %magenta([%thread]) %highlight(%-5level) %logger{36}.%M - %msg%n\n            </pattern>\n        </encoder>\n    </appender>\n\n    <root level=\"info\">\n        <appender-ref ref=\"STDOUT\"/>\n    </root>\n\n</configuration>"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Module Overview\n\n**Artifact ID**: `sofa-ark-springboot-starter`\n**Package**: `com.alipay.sofa.ark.springboot`\n\nThis is the main Spring Boot Starter for SOFAArk. It provides auto-configuration and integration with Spring Boot applications.\n\n## Purpose\n\n- Auto-configure SOFAArk in Spring Boot applications\n- Provide Spring Boot style integration\n- Enable Ark features with minimal configuration\n\n## Key Features\n\n### Auto-Configuration\n- Automatically activates when SOFAArk container is running\n- Conditionally loads version-specific compatibility modules\n- Exposes Ark services as Spring beans\n\n### Integration Points\n- `ApplicationContext` integration with Ark Biz\n- Spring Environment bridging\n- Event system integration\n\n## Usage\n\nAdd dependency:\n```xml\n<dependency>\n    <groupId>com.alipay.sofa</groupId>\n    <artifactId>sofa-ark-springboot-starter</artifactId>\n</dependency>\n```\n\n## Dependencies\n\n- `sofa-ark-common-springboot` - Common integration\n- `sofa-ark-compatible-springboot1` - Spring Boot 1.x support (optional)\n- `sofa-ark-compatible-springboot2` - Spring Boot 2.x support (optional)\n- `sofa-ark-support-starter` - Startup support\n\n## Used By\n\n- Spring Boot applications running on SOFAArk"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>sofa-ark-support</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n        <relativePath>../../pom.xml</relativePath>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <properties>\n        <spring.boot.version>2.7.14</spring.boot.version>\n    </properties>\n\n    <artifactId>sofa-ark-springboot-starter</artifactId>\n    <name>${project.groupId}:${project.artifactId}</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot</artifactId>\n            <version>${spring.boot.version}</version>\n            <scope>provided</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-actuator</artifactId>\n            <version>${spring.boot.version}</version>\n            <scope>provided</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n            <version>${spring.boot.version}</version>\n            <scope>provided</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-autoconfigure</artifactId>\n            <version>${spring.boot.version}</version>\n            <scope>provided</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-support-starter</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-api</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-compatible-springboot1</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-compatible-springboot2</artifactId>\n        </dependency>\n\n        <!--test-->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <version>${spring.boot.version}</version>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-logging</artifactId>\n            <version>${spring.boot.version}</version>\n            <scope>test</scope>\n            <exclusions>\n                <exclusion>\n                    <artifactId>log4j-over-slf4j</artifactId>\n                    <groupId>org.slf4j</groupId>\n                </exclusion>\n                <exclusion>\n                    <artifactId>logback-classic</artifactId>\n                    <groupId>ch.qos.logback</groupId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-container</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <!--   fix https://github.com/sofastack/sofa-ark/issues/851     -->\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-all</artifactId>\n            <!--     please notice to use the old version to make ide import sofa-ark-all by jar rather than local classpath       -->\n            <version>${sofa.ark.version.old}</version>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>web-ark-plugin</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <!--junit-->\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>provided</scope>\n        </dependency>\n\n        <!--testNg-->\n        <dependency>\n            <groupId>org.testng</groupId>\n            <artifactId>testng</artifactId>\n            <scope>provided</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.logging.log4j</groupId>\n            <artifactId>log4j-slf4j-impl</artifactId>\n            <version>2.17.1</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-loader</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>io.projectreactor.netty</groupId>\n            <artifactId>reactor-netty</artifactId>\n            <version>${reactor-netty.version}</version>\n            <scope>provided</scope>\n        </dependency>\n\n        <!--test-->\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-inline</artifactId>\n            <version>${mockito.version}</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <configuration>\n                    <threadCount>1</threadCount>\n                    <properties>\n                        <junit>false</junit>\n                    </properties>\n                </configuration>\n                <dependencies>\n                    <dependency>\n                        <groupId>org.apache.maven.surefire</groupId>\n                        <artifactId>surefire-junit47</artifactId>\n                        <version>${surefire.version}</version>\n                    </dependency>\n                    <dependency>\n                        <groupId>org.apache.maven.surefire</groupId>\n                        <artifactId>surefire-testng</artifactId>\n                        <version>${surefire.version}</version>\n                    </dependency>\n                </dependencies>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/main/java/com/alipay/sofa/ark/springboot/ArkAutoProcessorConfiguration.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot;\n\nimport com.alipay.sofa.ark.springboot.condition.ConditionalOnArkEnabled;\nimport com.alipay.sofa.ark.springboot.processor.ArkEventHandlerProcessor;\nimport com.alipay.sofa.ark.springboot.processor.ArkServiceInjectProcessor;\nimport org.springframework.boot.autoconfigure.AutoConfigureBefore;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * @author qixiaobo\n * @since 2.2.4\n */\n\n@Configuration\n@ConditionalOnArkEnabled\n@AutoConfigureBefore(name = {\n                             \"org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration\",\n                             \"org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration\" })\npublic class ArkAutoProcessorConfiguration {\n    @Bean\n    public static ArkServiceInjectProcessor serviceInjectProcessor() {\n        return new ArkServiceInjectProcessor();\n    }\n\n    @Bean\n    public static ArkEventHandlerProcessor arkEventHandlerProcessor() {\n        return new ArkEventHandlerProcessor();\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/main/java/com/alipay/sofa/ark/springboot/ArkReactiveAutoConfiguration.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot;\n\nimport com.alipay.sofa.ark.springboot.condition.ConditionalOnArkEnabled;\nimport com.alipay.sofa.ark.springboot.web.ArkNettyReactiveWebServerFactory;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.boot.autoconfigure.AutoConfigureBefore;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;\nimport org.springframework.boot.web.embedded.netty.NettyRouteProvider;\nimport org.springframework.boot.web.embedded.netty.NettyServerCustomizer;\nimport org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.client.reactive.ReactorResourceFactory;\nimport reactor.netty.http.server.HttpServer;\n\nimport java.util.Collection;\nimport java.util.stream.Collectors;\n\n@Configuration\n@ConditionalOnArkEnabled\n@AutoConfigureBefore(name = { \"org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration\" })\npublic class ArkReactiveAutoConfiguration {\n\n    @Configuration\n    @ConditionalOnMissingBean({ ReactiveWebServerFactory.class })\n    @ConditionalOnClass(value = { HttpServer.class }, name = { \"com.alipay.sofa.ark.netty.ArkNettyIdentification\" })\n    static class EmbeddedNetty {\n        EmbeddedNetty() {\n        }\n\n        @Bean\n        @ConditionalOnMissingBean\n        ReactorResourceFactory reactorServerResourceFactory() {\n            ReactorResourceFactory reactorResourceFactory = new ReactorResourceFactory();\n            reactorResourceFactory.setUseGlobalResources(false);\n            return reactorResourceFactory;\n        }\n\n        @Bean\n        NettyReactiveWebServerFactory nettyReactiveWebServerFactory(ReactorResourceFactory resourceFactory, ObjectProvider<NettyRouteProvider> routes, ObjectProvider<NettyServerCustomizer> serverCustomizers) {\n            NettyReactiveWebServerFactory serverFactory = new ArkNettyReactiveWebServerFactory();\n            serverFactory.setResourceFactory(resourceFactory);\n            routes.orderedStream().forEach((xva$0) -> {\n                serverFactory.addRouteProviders(new NettyRouteProvider[]{xva$0});\n            });\n            serverFactory.getServerCustomizers().addAll((Collection)serverCustomizers.orderedStream().collect(Collectors.toList()));\n            return serverFactory;\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/main/java/com/alipay/sofa/ark/springboot/ArkServletAutoConfiguration.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot;\n\nimport com.alipay.sofa.ark.springboot.condition.ConditionalOnArkEnabled;\nimport com.alipay.sofa.ark.springboot.web.ArkTomcatServletWebServerFactory;\nimport org.apache.catalina.startup.Tomcat;\nimport org.apache.coyote.UpgradeProtocol;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.boot.autoconfigure.AutoConfigureBefore;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.boot.autoconfigure.condition.SearchStrategy;\nimport org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;\nimport org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;\nimport org.springframework.boot.web.embedded.tomcat.TomcatContextCustomizer;\nimport org.springframework.boot.web.embedded.tomcat.TomcatProtocolHandlerCustomizer;\nimport org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;\nimport org.springframework.boot.web.servlet.server.ServletWebServerFactory;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport javax.servlet.Servlet;\nimport java.util.stream.Collectors;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@Configuration\n@ConditionalOnArkEnabled\n@ConditionalOnClass({ ServletWebServerFactoryAutoConfiguration.class,\n                     TomcatProtocolHandlerCustomizer.class })\n@AutoConfigureBefore(ServletWebServerFactoryAutoConfiguration.class)\npublic class ArkServletAutoConfiguration {\n\n    @Configuration\n    @ConditionalOnClass(value = { Servlet.class, Tomcat.class, UpgradeProtocol.class,\n            ServletWebServerFactory.class }, name = { \"com.alipay.sofa.ark.web.embed.tomcat.ArkTomcatEmbeddedWebappClassLoader\" })\n    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)\n    public static class EmbeddedArkTomcat {\n\n        @Bean\n        @ConditionalOnMissingBean(ArkTomcatServletWebServerFactory.class)\n        public TomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,\n                                                                           ObjectProvider<TomcatContextCustomizer> contextCustomizers,\n                                                                           ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {\n            ArkTomcatServletWebServerFactory factory = new ArkTomcatServletWebServerFactory();\n            factory.getTomcatConnectorCustomizers().addAll(\n                connectorCustomizers.orderedStream().collect(Collectors.toList()));\n            factory.getTomcatContextCustomizers().addAll(\n                contextCustomizers.orderedStream().collect(Collectors.toList()));\n            factory.getTomcatProtocolHandlerCustomizers().addAll(\n                protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));\n            return factory;\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/main/java/com/alipay/sofa/ark/springboot/ArkServletLegacyAutoConfiguration.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot;\n\nimport com.alipay.sofa.ark.springboot.condition.ConditionalOnArkEnabled;\nimport com.alipay.sofa.ark.springboot.web.ArkTomcatServletWebServerFactory;\nimport org.apache.catalina.startup.Tomcat;\nimport org.apache.coyote.UpgradeProtocol;\nimport org.springframework.boot.autoconfigure.AutoConfigureBefore;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;\nimport org.springframework.boot.autoconfigure.condition.SearchStrategy;\nimport org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;\nimport org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;\nimport org.springframework.boot.web.servlet.server.ServletWebServerFactory;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport javax.servlet.Servlet;\n\n/**\n * adapt to springboot 2.1.9.RELEASE\n * @author gaosaroma\n * @since 2.2.8\n */\n@Configuration\n@ConditionalOnArkEnabled\n@ConditionalOnClass(ServletWebServerFactoryAutoConfiguration.class)\n@ConditionalOnMissingClass(\"org.springframework.boot.web.embedded.tomcat.TomcatProtocolHandlerCustomizer\")\n@AutoConfigureBefore(ServletWebServerFactoryAutoConfiguration.class)\npublic class ArkServletLegacyAutoConfiguration {\n\n    @Configuration\n    @ConditionalOnClass(value = { Servlet.class, Tomcat.class, UpgradeProtocol.class,\n            ServletWebServerFactory.class }, name = { \"com.alipay.sofa.ark.web.embed.tomcat.ArkTomcatEmbeddedWebappClassLoader\" })\n    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)\n    public static class EmbeddedArkTomcat {\n\n        @Bean\n        @ConditionalOnMissingBean(ArkTomcatServletWebServerFactory.class)\n        public TomcatServletWebServerFactory tomcatServletWebServerFactory() {\n            return new ArkTomcatServletWebServerFactory();\n        }\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/main/java/com/alipay/sofa/ark/springboot/listener/ArkApplicationStartListener.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot.listener;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.event.AfterFinishDeployEvent;\nimport com.alipay.sofa.ark.spi.event.AfterFinishStartupEvent;\nimport com.alipay.sofa.ark.spi.event.biz.AfterBizStartupEvent;\nimport com.alipay.sofa.ark.support.common.MasterBizEnvironmentHolder;\nimport com.alipay.sofa.ark.support.startup.EmbedSofaArkBootstrap;\nimport com.alipay.sofa.ark.support.startup.SofaArkBootstrap;\nimport org.springframework.boot.SpringBootVersion;\nimport org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;\nimport org.springframework.boot.context.event.ApplicationReadyEvent;\nimport org.springframework.boot.context.event.SpringApplicationEvent;\nimport org.springframework.context.ApplicationListener;\n\n/**\n * Ark Spring boot starter when run on ide\n *\n * @author ruoshan\n * @since 0.1.0\n */\npublic class ArkApplicationStartListener implements ApplicationListener<SpringApplicationEvent> {\n\n    private static final String LAUNCH_CLASSLOADER_NAME    = \"sun.misc.Launcher$AppClassLoader\";\n    private static final String SPRING_BOOT_LOADER         = \"org.springframework.boot.loader.LaunchedURLClassLoader\";\n    //SpringBoot 3.2.0 support\n    private static final String SPRING_BOOT_NEW_LOADER     = \"org.springframework.boot.loader.launch.LaunchedClassLoader\";\n    private static final String APPLICATION_STARTED_EVENT  = \"org.springframework.boot.context.event.ApplicationStartedEvent\";\n    private static final String APPLICATION_STARTING_EVENT = \"org.springframework.boot.context.event.ApplicationStartingEvent\";\n    private static Class<?>     SPRING_BOOT_LOADER_CLASS;\n    private static Class<?>     SPRING_BOOT_NEW_LOADER_CLASS;\n\n    static {\n        try {\n            SPRING_BOOT_LOADER_CLASS = ApplicationListener.class.getClassLoader().loadClass(\n                SPRING_BOOT_LOADER);\n        } catch (Throwable t) {\n            // ignore\n        }\n        try {\n            SPRING_BOOT_NEW_LOADER_CLASS = ApplicationListener.class.getClassLoader().loadClass(\n                SPRING_BOOT_NEW_LOADER);\n        } catch (Throwable t) {\n            // ignore\n        }\n    }\n\n    @Override\n    public void onApplicationEvent(SpringApplicationEvent event) {\n        try {\n            if (isEmbedEnable()) {\n                ArkConfigs.setEmbedEnable(true);\n                startUpArkEmbed(event);\n                return;\n            }\n            if (isSpringBoot2()\n                && APPLICATION_STARTING_EVENT.equals(event.getClass().getCanonicalName())) {\n                startUpArk(event);\n            }\n\n            if (isSpringBoot1()\n                && APPLICATION_STARTED_EVENT.equals(event.getClass().getCanonicalName())) {\n                startUpArk(event);\n            }\n        } catch (Throwable e) {\n            throw new RuntimeException(\"Meet exception when determine whether to start SOFAArk!\", e);\n        }\n    }\n\n    private boolean isEmbedEnable() {\n        if (ArkConfigs.isEmbedEnable()) {\n            return true;\n        }\n        if (SPRING_BOOT_LOADER_CLASS != null\n            && SPRING_BOOT_LOADER_CLASS.isAssignableFrom(this.getClass().getClassLoader()\n                .getClass())) {\n            return true;\n        }\n        if (SPRING_BOOT_NEW_LOADER_CLASS != null\n            && SPRING_BOOT_NEW_LOADER_CLASS.isAssignableFrom(this.getClass().getClassLoader()\n                .getClass())) {\n            return true;\n        }\n        return false;\n    }\n\n    public void startUpArk(SpringApplicationEvent event) {\n        if (LAUNCH_CLASSLOADER_NAME.equals(this.getClass().getClassLoader().getClass().getName())) {\n            SofaArkBootstrap.launch(event.getArgs());\n        }\n    }\n\n    public boolean isSpringBoot1() {\n        String version = SpringBootVersion.getVersion();\n        return null == version ? false : version.startsWith(\"1\");\n    }\n\n    public boolean isSpringBoot2() {\n        String version = SpringBootVersion.getVersion();\n        return null == version ? false : version.startsWith(\"2\");\n    }\n\n    protected void startUpArkEmbed(SpringApplicationEvent event) {\n        // 仅监听基座启动时的Event\n        if (this.getClass().getClassLoader() != Thread.currentThread().getContextClassLoader()) {\n            return;\n        }\n        if (event instanceof ApplicationEnvironmentPreparedEvent) {\n            ApplicationEnvironmentPreparedEvent preparedEvent = (ApplicationEnvironmentPreparedEvent) event;\n            MasterBizEnvironmentHolder.setEnvironment(preparedEvent.getEnvironment());\n            EmbedSofaArkBootstrap.launch(preparedEvent.getEnvironment());\n        }\n        if (event instanceof ApplicationReadyEvent) {\n            // 基座启动后+静态合并部署的biz启动后，发送事件\n            sendEventAfterArkEmbedStartupFinish();\n        }\n    }\n\n    protected void sendEventAfterArkEmbedStartupFinish() {\n        if (ArkClient.getEventAdminService() != null && ArkClient.getMasterBiz() != null) {\n            ArkClient.getEventAdminService().sendEvent(\n                new AfterBizStartupEvent(ArkClient.getMasterBiz()));\n            ArkClient.getEventAdminService().sendEvent(new AfterFinishDeployEvent());\n            ArkClient.getEventAdminService().sendEvent(new AfterFinishStartupEvent());\n        }\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/main/java/com/alipay/sofa/ark/springboot/listener/ArkDeployStaticBizListener.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot.listener;\n\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.support.startup.EmbedSofaArkBootstrap;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.context.event.ApplicationContextEvent;\nimport org.springframework.context.event.ContextRefreshedEvent;\nimport org.springframework.core.Ordered;\n\nimport java.util.concurrent.atomic.AtomicBoolean;\n\npublic class ArkDeployStaticBizListener implements ApplicationListener<ApplicationContextEvent>,\n                                       Ordered {\n\n    private static final AtomicBoolean deployed = new AtomicBoolean(false);\n\n    @Override\n    public void onApplicationEvent(ApplicationContextEvent event) {\n        // Only listen to the event when the master biz is started\n        if (this.getClass().getClassLoader() != Thread.currentThread().getContextClassLoader()) {\n            return;\n        }\n        if (ArkConfigs.isEmbedEnable() && ArkConfigs.isEmbedStaticBizEnable()) {\n            if (event instanceof ContextRefreshedEvent && deployed.compareAndSet(false, true)\n                && event.getApplicationContext().getParent() == null) {\n                // After the master biz is started, statically deploy the other biz from classpath\n                EmbedSofaArkBootstrap.deployStaticBizAfterEmbedMasterBizStarted();\n            }\n        }\n    }\n\n    @Override\n    public int getOrder() {\n        return LOWEST_PRECEDENCE - 10;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/main/java/com/alipay/sofa/ark/springboot/listener/PropertiesResetListener.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot.listener;\n\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor;\nimport org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.core.Ordered;\n\n/**\n *  Listener to reset system properties\n *\n * @author bingjie.lbj\n */\npublic class PropertiesResetListener implements\n                                    ApplicationListener<ApplicationEnvironmentPreparedEvent>,\n                                    Ordered {\n    @Override\n    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {\n        if (ArkConfigs.isEmbedEnable()) {\n            System.getProperties().remove(\"logging.path\");\n        }\n    }\n\n    @Override\n    public int getOrder() {\n        //after ConfigFileApplicationListener\n        return ConfigDataEnvironmentPostProcessor.ORDER + 1;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/main/java/com/alipay/sofa/ark/springboot/loader/CachedLaunchedURLClassLoader.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot.loader;\n\nimport org.springframework.boot.loader.LaunchedURLClassLoader;\nimport org.springframework.boot.loader.archive.Archive;\n\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.Enumeration;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * A cached LaunchedURLClassLoader to accelerate load classes and resources.\n * NOTE:\n *   1. Not found classes will be cached.\n *   2. If findResources(name) return null Enumeration, then it will be cached.\n *   3. findResource(name) will always be cached no matter it was found or not found.\n *   4. Otherwise, classes or resources-enumeration will not be cached explicitly.\n *\n * @author bingjie.lbj\n */\npublic class CachedLaunchedURLClassLoader extends LaunchedURLClassLoader {\n    private final Map<String, LoadClassResult>            classCache        = new ConcurrentHashMap<>(\n                                                                                3000);\n    private final Map<String, Optional<URL>>              resourceUrlCache  = new ConcurrentHashMap<>(\n                                                                                3000);\n    private final Map<String, Optional<Enumeration<URL>>> resourcesUrlCache = new ConcurrentHashMap<>(\n                                                                                300);\n\n    static {\n        ClassLoader.registerAsParallelCapable();\n    }\n\n    public CachedLaunchedURLClassLoader(boolean exploded, Archive rootArchive, URL[] urls,\n                                        ClassLoader parent) {\n        super(exploded, rootArchive, urls, parent);\n    }\n\n    @Override\n    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {\n        return loadClassWithCache(name, resolve);\n    }\n\n    @Override\n    public URL findResource(String name) {\n        Optional<URL> urlOptional = resourceUrlCache.get(name);\n        if (urlOptional != null) {\n            return urlOptional.orElse(null);\n        }\n        URL url = super.findResource(name);\n        resourceUrlCache.put(name, url != null ? Optional.of(url) : Optional.empty());\n        return url;\n    }\n\n    @Override\n    public Enumeration<URL> findResources(String name) throws IOException {\n        Optional<Enumeration<URL>> urlOptional = resourcesUrlCache.get(name);\n        if (urlOptional != null) {\n            return urlOptional.orElse(null);\n        }\n        Enumeration<URL> enumeration = super.findResources(name);\n        if (enumeration == null || !enumeration.hasMoreElements()) {\n            resourcesUrlCache.put(name, Optional.empty());\n        }\n        return enumeration;\n    }\n\n    /**\n     * NOTE: Only cache ClassNotFoundException when class not found.\n     * If class found, do not cache, and just use parent class loader cache.\n     */\n    protected Class<?> loadClassWithCache(String name, boolean resolve)\n                                                                       throws ClassNotFoundException {\n        LoadClassResult resultInCache = classCache.get(name);\n        if (resultInCache != null) {\n            if (resultInCache.getEx() != null) {\n                throw resultInCache.getEx();\n            }\n            return resultInCache.getClazz();\n        }\n        try {\n            Class<?> clazz = super.findLoadedClass(name);\n            if (clazz == null) {\n                clazz = super.loadClass(name, resolve);\n            }\n            if (clazz == null) {\n                classCache.put(name, LoadClassResult.NOT_FOUND);\n            }\n            return clazz;\n        } catch (ClassNotFoundException exception) {\n            classCache.put(name, LoadClassResult.NOT_FOUND);\n            throw exception;\n        }\n    }\n\n    protected static class LoadClassResult {\n        private Class<?>                 clazz;\n        private ClassNotFoundException   ex;\n        protected static LoadClassResult NOT_FOUND = new LoadClassResult(\n                                                       new ClassNotFoundException());\n\n        public LoadClassResult() {\n        }\n\n        public LoadClassResult(ClassNotFoundException ex) {\n            this.ex = ex;\n        }\n\n        public Class<?> getClazz() {\n            return clazz;\n        }\n\n        public void setClazz(Class<?> clazz) {\n            this.clazz = clazz;\n        }\n\n        public ClassNotFoundException getEx() {\n            return ex;\n        }\n\n        public void setEx(ClassNotFoundException ex) {\n            this.ex = ex;\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/main/java/com/alipay/sofa/ark/springboot/loader/JarLauncher.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot.loader;\n\nimport java.net.URL;\n\n/**\n * A JarLauncher to load classes with CachedLaunchedURLClassLoader\n *\n * @author bingjie.lbj\n */\npublic class JarLauncher extends org.springframework.boot.loader.JarLauncher {\n    public static void main(String[] args) throws Exception {\n        new JarLauncher().launch(args);\n    }\n\n    @Override\n    protected ClassLoader createClassLoader(URL[] urls) throws Exception {\n        return new CachedLaunchedURLClassLoader(isExploded(), getArchive(), urls, getClass()\n            .getClassLoader());\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/main/java/com/alipay/sofa/ark/springboot/processor/ArkEventHandlerProcessor.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot.processor;\n\nimport com.alipay.sofa.ark.spi.service.ArkInject;\nimport com.alipay.sofa.ark.spi.service.event.EventAdminService;\nimport com.alipay.sofa.ark.spi.service.event.EventHandler;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.core.Ordered;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class ArkEventHandlerProcessor implements BeanPostProcessor, Ordered {\n    @ArkInject\n    private EventAdminService eventAdminService;\n\n    @Override\n    public Object postProcessBeforeInitialization(Object bean, String beanName)\n                                                                               throws BeansException {\n        return bean;\n    }\n\n    @Override\n    public Object postProcessAfterInitialization(Object bean, String beanName)\n                                                                              throws BeansException {\n        if (bean instanceof EventHandler) {\n            eventAdminService.register((EventHandler) bean);\n        }\n        return bean;\n    }\n\n    @Override\n    public int getOrder() {\n        return LOWEST_PRECEDENCE;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/main/java/com/alipay/sofa/ark/springboot/processor/ArkServiceInjectProcessor.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot.processor;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.core.PriorityOrdered;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class ArkServiceInjectProcessor implements BeanPostProcessor, PriorityOrdered {\n\n    @Override\n    public Object postProcessBeforeInitialization(Object bean, String beanName)\n                                                                               throws BeansException {\n        ArkClient.getInjectionService().inject(bean);\n        return bean;\n    }\n\n    @Override\n    public Object postProcessAfterInitialization(Object bean, String beanName)\n                                                                              throws BeansException {\n        return bean;\n    }\n\n    @Override\n    public int getOrder() {\n        return LOWEST_PRECEDENCE;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/main/java/com/alipay/sofa/ark/springboot/runner/ArkBootEmbedRunner.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot.runner;\n\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.support.common.DelegateArkContainer;\nimport com.alipay.sofa.ark.support.runner.JUnitExecutionListener;\nimport org.junit.runner.Description;\nimport org.junit.runner.Runner;\nimport org.junit.runner.manipulation.Filter;\nimport org.junit.runner.manipulation.Filterable;\nimport org.junit.runner.manipulation.NoTestsRemainException;\nimport org.junit.runner.manipulation.Sortable;\nimport org.junit.runner.manipulation.Sorter;\nimport org.junit.runner.notification.RunNotifier;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.EMBED_ENABLE;\nimport static com.alipay.sofa.ark.spi.constant.Constants.MASTER_BIZ;\n\n/**\n * Corresponding to {@literal org.springframework.test.context.junit4.SpringRunner}\n * used for test ark biz like koupleless, which run ark plugin in embed mode\n * please refer {@link com.alipay.sofa.ark.spi.service.plugin.PluginFactoryService#createEmbedPlugin(com.alipay.sofa.ark.spi.archive.PluginArchive, java.lang.ClassLoader)}\n *\n * @author lvjing2\n * @since 2.2.10\n */\npublic class ArkBootEmbedRunner extends Runner implements Filterable, Sortable {\n\n    private static final String SPRING_RUNNER = \"org.springframework.test.context.junit4.SpringRunner\";\n\n    /**\n     * {@see org.springframework.test.context.junit4.SpringRunner}\n     */\n    private Runner              runner;\n\n    @SuppressWarnings(\"unchecked\")\n    public ArkBootEmbedRunner(Class<?> klazz) {\n        if (!DelegateArkContainer.isStarted()) {\n            System.setProperty(EMBED_ENABLE, \"true\");\n            System.setProperty(MASTER_BIZ, \"test master biz\");\n            DelegateArkContainer.launch(klazz);\n            System.clearProperty(EMBED_ENABLE);\n            System.clearProperty(MASTER_BIZ);\n        }\n\n        Class springRunnerClass = DelegateArkContainer.loadClass(SPRING_RUNNER);\n        Class testClass = DelegateArkContainer.loadClass(klazz.getName());\n        try {\n            runner = (Runner) springRunnerClass.getConstructor(Class.class).newInstance(testClass);\n        } catch (Exception ex) {\n            throw new RuntimeException(ex);\n        }\n    }\n\n    @Override\n    public Description getDescription() {\n        return runner.getDescription();\n    }\n\n    @Override\n    public void run(RunNotifier notifier) {\n        ClassLoader oldClassLoader = ClassLoaderUtils.pushContextClassLoader(DelegateArkContainer\n            .getTestClassLoader());\n        try {\n            notifier.addListener(JUnitExecutionListener.getRunListener());\n            runner.run(notifier);\n        } finally {\n            ClassLoaderUtils.popContextClassLoader(oldClassLoader);\n        }\n    }\n\n    @Override\n    public void filter(Filter filter) throws NoTestsRemainException {\n        ((Filterable) runner).filter(filter);\n    }\n\n    @Override\n    public void sort(Sorter sorter) {\n        ((Sortable) runner).sort(sorter);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/main/java/com/alipay/sofa/ark/springboot/runner/ArkBootRunner.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot.runner;\n\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.support.common.DelegateArkContainer;\nimport com.alipay.sofa.ark.support.runner.JUnitExecutionListener;\nimport org.junit.runner.Description;\nimport org.junit.runner.Runner;\nimport org.junit.runner.manipulation.*;\nimport org.junit.runner.notification.RunNotifier;\n\n/**\n * Corresponding to {@literal org.springframework.test.context.junit4.SpringRunner}\n *\n * used for test ark plugin\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class ArkBootRunner extends Runner implements Filterable, Sortable {\n\n    private static final String SPRING_RUNNER = \"org.springframework.test.context.junit4.SpringRunner\";\n\n    /**\n     * {@see org.springframework.test.context.junit4.SpringRunner}\n     */\n    private Runner              runner;\n\n    @SuppressWarnings(\"unchecked\")\n    public ArkBootRunner(Class<?> klazz) {\n        if (!DelegateArkContainer.isStarted()) {\n            DelegateArkContainer.launch(klazz);\n        }\n\n        Class springRunnerClass = DelegateArkContainer.loadClass(SPRING_RUNNER);\n        Class testClass = DelegateArkContainer.loadClass(klazz.getName());\n        try {\n            runner = (Runner) springRunnerClass.getConstructor(Class.class).newInstance(testClass);\n        } catch (Exception ex) {\n            throw new RuntimeException(ex);\n        }\n    }\n\n    @Override\n    public Description getDescription() {\n        return runner.getDescription();\n    }\n\n    @Override\n    public void run(RunNotifier notifier) {\n        ClassLoader oldClassLoader = ClassLoaderUtils.pushContextClassLoader(DelegateArkContainer\n            .getTestClassLoader());\n        try {\n            notifier.addListener(JUnitExecutionListener.getRunListener());\n            runner.run(notifier);\n        } finally {\n            ClassLoaderUtils.popContextClassLoader(oldClassLoader);\n        }\n    }\n\n    @Override\n    public void filter(Filter filter) throws NoTestsRemainException {\n        ((Filterable) runner).filter(filter);\n    }\n\n    @Override\n    public void sort(Sorter sorter) {\n        ((Sortable) runner).sort(sorter);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/main/java/com/alipay/sofa/ark/springboot/web/ArkCompositeReactorHttpHandlerAdapter.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot.web;\n\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport org.springframework.http.server.reactive.HttpHandler;\nimport org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;\nimport reactor.core.publisher.Mono;\nimport reactor.netty.http.server.HttpServerRequest;\nimport reactor.netty.http.server.HttpServerResponse;\n\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * @author: yuanyuan\n */\npublic class ArkCompositeReactorHttpHandlerAdapter extends ReactorHttpHandlerAdapter {\n\n    private Map<String, ReactorHttpHandlerAdapter> bizReactorHttpHandlerAdapters = new ConcurrentHashMap<>();\n\n    public ArkCompositeReactorHttpHandlerAdapter(HttpHandler httpHandler) {\n        super(httpHandler);\n    }\n\n    @Override\n    public Mono<Void> apply(HttpServerRequest reactorRequest, HttpServerResponse reactorResponse) {\n        String uri = reactorRequest.uri();\n        for (Map.Entry<String, ReactorHttpHandlerAdapter> entry : bizReactorHttpHandlerAdapters\n            .entrySet()) {\n            if (uri.startsWith(entry.getKey())) {\n                ReactorHttpHandlerAdapter adapter = entry.getValue();\n                return adapter.apply(reactorRequest, reactorResponse);\n            }\n        }\n        return super.apply(reactorRequest, reactorResponse);\n    }\n\n    public void registerBizReactorHttpHandlerAdapter(String contextPath,\n                                                     ReactorHttpHandlerAdapter reactorHttpHandlerAdapter) {\n        ReactorHttpHandlerAdapter old = bizReactorHttpHandlerAdapters.putIfAbsent(contextPath,\n            reactorHttpHandlerAdapter);\n        if (old != null) {\n            throw new ArkRuntimeException(\"Duplicated context path\");\n        }\n    }\n\n    public void unregisterBizReactorHttpHandlerAdapter(String contextPath) {\n        bizReactorHttpHandlerAdapters.remove(contextPath);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/main/java/com/alipay/sofa/ark/springboot/web/ArkNettyReactiveWebServerFactory.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot.web;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.service.ArkInject;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.web.EmbeddedServerService;\nimport org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;\nimport org.springframework.boot.web.embedded.netty.NettyRouteProvider;\nimport org.springframework.boot.web.embedded.netty.NettyServerCustomizer;\nimport org.springframework.boot.web.embedded.netty.SslServerCustomizer;\nimport org.springframework.boot.web.server.WebServer;\nimport org.springframework.http.client.reactive.ReactorResourceFactory;\nimport org.springframework.http.server.reactive.ContextPathCompositeHandler;\nimport org.springframework.http.server.reactive.HttpHandler;\nimport org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport reactor.netty.http.HttpProtocol;\nimport reactor.netty.http.server.HttpServer;\nimport reactor.netty.resources.LoopResources;\n\nimport java.net.InetSocketAddress;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.ROOT_WEB_CONTEXT_PATH;\n\npublic class ArkNettyReactiveWebServerFactory extends NettyReactiveWebServerFactory {\n    private static final Charset                         DEFAULT_CHARSET   = StandardCharsets.UTF_8;\n    private Duration                                     lifecycleTimeout;\n\n    private List<NettyRouteProvider>                     routeProviders    = new ArrayList();\n    @ArkInject\n    private EmbeddedServerService                        embeddedNettyService;\n\n    @ArkInject\n    private BizManagerService                            bizManagerService;\n\n    private boolean                                      useForwardHeaders;\n\n    private ReactorResourceFactory                       resourceFactory;\n\n    private int                                          backgroundProcessorDelay;\n    private Set<NettyServerCustomizer>                   serverCustomizers = new LinkedHashSet();\n\n    private static ArkCompositeReactorHttpHandlerAdapter adapter;\n\n    public ArkNettyReactiveWebServerFactory() {\n    }\n\n    @Override\n    public WebServer getWebServer(HttpHandler httpHandler) {\n        if (embeddedNettyService == null && ArkClient.getInjectionService() != null) {\n            // 非应用上下文 (例如: Spring Management Context) 没有经历 Start 生命周期, 不会被注入 ArkServiceInjectProcessor,\n            // 因此 @ArkInject 没有被处理, 需要手动处理\n            ArkClient.getInjectionService().inject(this);\n        }\n        if (embeddedNettyService == null) {\n            // 原有的逻辑中也有这个空值判断, 不确定注入后是否还会有用例会导致 embeddedNettyService 为空\n            // 因此仍保留此 if 空值判断\n            return super.getWebServer(httpHandler);\n        }\n        if (embeddedNettyService.getEmbedServer(getPort()) == null) {\n            embeddedNettyService.putEmbedServer(getPort(), initEmbedNetty());\n        }\n\n        String contextPath = getContextPath();\n        Map<String, HttpHandler> handlerMap = new HashMap<>();\n        handlerMap.put(contextPath, httpHandler);\n        ContextPathCompositeHandler contextHandler = new ContextPathCompositeHandler(handlerMap);\n\n        if (adapter == null) {\n            adapter = new ArkCompositeReactorHttpHandlerAdapter(contextHandler);\n        } else {\n            adapter.registerBizReactorHttpHandlerAdapter(contextPath,\n                new ReactorHttpHandlerAdapter(contextHandler));\n        }\n\n        HttpServer httpServer = (HttpServer) embeddedNettyService.getEmbedServer(getPort());\n        ArkNettyWebServer webServer = (ArkNettyWebServer) createNettyWebServer(contextPath,\n            httpServer, adapter, lifecycleTimeout);\n        webServer.setRouteProviders(this.routeProviders);\n\n        return webServer;\n    }\n\n    public String getContextPath() {\n        String contextPath = \"\";\n        if (bizManagerService == null) {\n            return contextPath;\n        }\n        Biz biz = bizManagerService.getBizByClassLoader(Thread.currentThread()\n            .getContextClassLoader());\n\n        if (!StringUtils.isEmpty(contextPath)) {\n            return contextPath;\n        } else if (biz != null) {\n            if (StringUtils.isEmpty(biz.getWebContextPath())) {\n                return ROOT_WEB_CONTEXT_PATH;\n            }\n            contextPath = biz.getWebContextPath();\n            if (!contextPath.startsWith(\"/\")) {\n                contextPath = \"/\" + contextPath;\n            }\n            return contextPath;\n        } else {\n            return ROOT_WEB_CONTEXT_PATH;\n        }\n    }\n\n    WebServer createNettyWebServer(String contextPath, HttpServer httpServer,\n                                   ReactorHttpHandlerAdapter handlerAdapter,\n                                   Duration lifecycleTimeout) {\n        return new ArkNettyWebServer(contextPath, httpServer, handlerAdapter, lifecycleTimeout);\n    }\n\n    private HttpServer  initEmbedNetty(){\n        HttpServer server = HttpServer.create();\n        if (this.resourceFactory != null) {\n            LoopResources resources = this.resourceFactory.getLoopResources();\n            Assert.notNull(resources, \"No LoopResources: is ReactorResourceFactory not initialized yet?\");\n            server = server.tcpConfiguration((tcpServer) -> tcpServer.runOn(resources)).bindAddress(this::getListenAddress);\n        } else {\n            server = server.bindAddress(this::getListenAddress);\n        }\n\n        if (this.getSsl() != null && this.getSsl().isEnabled()) {\n            server = this.customizeSslConfiguration(server);\n        }\n\n\n        server = server.protocol(this.listProtocols()).forwarded(this.useForwardHeaders);\n        return applyCustomizers(server);\n    }\n\n    private HttpServer customizeSslConfiguration(HttpServer httpServer) {\n        SslServerCustomizer sslServerCustomizer = new SslServerCustomizer(this.getSsl(),\n            this.getHttp2(), this.getSslStoreProvider());\n        return sslServerCustomizer.apply(httpServer);\n    }\n\n    private HttpProtocol[] listProtocols() {\n        List<HttpProtocol> protocols = new ArrayList();\n        protocols.add(HttpProtocol.HTTP11);\n        if (this.getHttp2() != null && this.getHttp2().isEnabled()) {\n            if (this.getSsl() != null && this.getSsl().isEnabled()) {\n                protocols.add(HttpProtocol.H2);\n            } else {\n                protocols.add(HttpProtocol.H2C);\n            }\n        }\n\n        return (HttpProtocol[]) protocols.toArray(new HttpProtocol[0]);\n    }\n\n    private HttpServer applyCustomizers(HttpServer server) {\n        NettyServerCustomizer customizer;\n        for (Iterator var2 = this.serverCustomizers.iterator(); var2.hasNext(); server = (HttpServer) customizer\n            .apply(server)) {\n            customizer = (NettyServerCustomizer) var2.next();\n        }\n\n        return server;\n    }\n\n    private InetSocketAddress getListenAddress() {\n        return this.getAddress() != null ? new InetSocketAddress(\n            this.getAddress().getHostAddress(), this.getPort()) : new InetSocketAddress(\n            this.getPort());\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/main/java/com/alipay/sofa/ark/springboot/web/ArkNettyWebServer.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot.web;\n\nimport io.netty.channel.group.DefaultChannelGroup;\nimport io.netty.channel.unix.Errors;\nimport io.netty.util.concurrent.DefaultEventExecutor;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.reactivestreams.Publisher;\nimport org.springframework.boot.web.embedded.netty.NettyRouteProvider;\nimport org.springframework.boot.web.server.PortInUseException;\nimport org.springframework.boot.web.server.WebServer;\nimport org.springframework.boot.web.server.WebServerException;\nimport org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;\nimport org.springframework.util.Assert;\nimport reactor.netty.ChannelBindException;\nimport reactor.netty.DisposableServer;\nimport reactor.netty.http.server.HttpServer;\nimport reactor.netty.http.server.HttpServerRequest;\nimport reactor.netty.http.server.HttpServerResponse;\nimport reactor.netty.http.server.HttpServerRoutes;\n\nimport java.time.Duration;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.function.BiFunction;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\n\npublic class ArkNettyWebServer implements WebServer {\n    private static final Predicate<HttpServerRequest> ALWAYS = (request) -> {\n        return true;\n    };\n    private static HttpServer arkHttpServer;\n    private static final Log logger = LogFactory.getLog(ArkNettyWebServer.class);\n    private final HttpServer httpServer;\n    private final BiFunction<? super HttpServerRequest, ? super HttpServerResponse, ? extends Publisher<Void>> handler;\n    private final Duration lifecycleTimeout;\n    private List<NettyRouteProvider> routeProviders = Collections.emptyList();\n    private static volatile DisposableServer disposableServer;\n    private Thread awaitThread;\n    private String contextPath;\n\n    public ArkNettyWebServer(String contextPath, HttpServer httpServer, ReactorHttpHandlerAdapter handlerAdapter, Duration lifecycleTimeout) {\n        Assert.notNull(httpServer, \"HttpServer must not be null\");\n        Assert.notNull(handlerAdapter, \"HandlerAdapter must not be null\");\n        this.contextPath = contextPath;\n        this.lifecycleTimeout = lifecycleTimeout;\n        this.handler = handlerAdapter;\n        this.httpServer = httpServer.channelGroup(new DefaultChannelGroup(new DefaultEventExecutor()));\n        if (arkHttpServer == null) {\n            arkHttpServer = this.httpServer;\n        }\n    }\n\n    public void setRouteProviders(List<NettyRouteProvider> routeProviders) {\n        this.routeProviders = routeProviders;\n    }\n\n    @Override\n    public void start() throws WebServerException {\n        if (disposableServer == null) {\n            try {\n                disposableServer = this.startHttpServer();\n            } catch (Exception var2) {\n                PortInUseException.ifCausedBy(var2, ChannelBindException.class, (bindException) -> {\n                    if (bindException.localPort() > 0 && !this.isPermissionDenied(bindException.getCause())) {\n                        throw new PortInUseException(bindException.localPort(), var2);\n                    }\n                });\n                throw new WebServerException(\"Unable to start Netty\", var2);\n            }\n\n            this.startDaemonAwaitThread(disposableServer);\n        }\n\n        if (disposableServer != null) {\n            logger.info(\"Netty started\" + this.getStartedOnMessage(disposableServer) + \" with context path \" + contextPath);\n        }\n    }\n\n    @Override\n    public void stop() throws WebServerException {\n        if (!(this.handler instanceof ArkCompositeReactorHttpHandlerAdapter)) {\n            return;\n        }\n\n        ((ArkCompositeReactorHttpHandlerAdapter) this.handler).unregisterBizReactorHttpHandlerAdapter(contextPath);\n\n        if (disposableServer != null && this.httpServer == arkHttpServer) {\n            try {\n                if (this.lifecycleTimeout != null) {\n                    disposableServer.disposeNow(this.lifecycleTimeout);\n                } else {\n                    disposableServer.disposeNow();\n                }\n                awaitThread.stop();\n            } catch (IllegalStateException ignore) {\n\n            }\n\n            logger.info(\"Netty stoped\" + this.getStartedOnMessage(disposableServer));\n\n            disposableServer = null;\n        }\n\n    }\n\n    @Override\n    public int getPort() {\n        if (disposableServer != null) {\n            try {\n                return disposableServer.port();\n            } catch (UnsupportedOperationException var2) {\n                return -1;\n            }\n        } else {\n            return -1;\n        }\n    }\n\n\n    private String getStartedOnMessage(DisposableServer server) {\n        StringBuilder message = new StringBuilder();\n        this.tryAppend(message, \"port %s\", server::port);\n        this.tryAppend(message, \"host %s\", server::host);\n        return message.length() > 0 ? \" on \" + message : \"\";\n    }\n\n    private void tryAppend(StringBuilder message, String format, Supplier<Object> supplier) {\n        try {\n            Object value = supplier.get();\n            message.append(message.length() != 0 ? \" \" : \"\");\n            message.append(String.format(format, value));\n        } catch (UnsupportedOperationException var5) {\n        }\n\n    }\n\n    DisposableServer startHttpServer() {\n        HttpServer server = this.httpServer;\n        if (this.routeProviders.isEmpty()) {\n            server = server.handle(this.handler);\n        } else {\n            server = server.route(this::applyRouteProviders);\n        }\n\n        return this.lifecycleTimeout != null ? server.bindNow(this.lifecycleTimeout) : server.bindNow();\n    }\n\n    private boolean isPermissionDenied(Throwable bindExceptionCause) {\n        try {\n            if (bindExceptionCause instanceof Errors.NativeIoException) {\n                return ((Errors.NativeIoException)bindExceptionCause).expectedErr() == -13;\n            }\n        } catch (Throwable var3) {\n        }\n\n        return false;\n    }\n\n    private void applyRouteProviders(HttpServerRoutes routes) {\n        NettyRouteProvider provider;\n        for(Iterator var2 = this.routeProviders.iterator(); var2.hasNext(); routes = (HttpServerRoutes)provider.apply(routes)) {\n            provider = (NettyRouteProvider)var2.next();\n        }\n\n        routes.route(ALWAYS, this.handler);\n    }\n\n    private void startDaemonAwaitThread(DisposableServer disposableServer) {\n        awaitThread = new Thread(\"server\") {\n            public void run() {\n                disposableServer.onDispose().block();\n            }\n        };\n        awaitThread.setContextClassLoader(this.getClass().getClassLoader());\n        awaitThread.setDaemon(false);\n        awaitThread.start();\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/main/java/com/alipay/sofa/ark/springboot/web/ArkTomcatServletWebServerFactory.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot.web;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.common.util.AssertUtils;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.service.ArkInject;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.web.EmbeddedServerService;\nimport org.apache.catalina.Context;\nimport org.apache.catalina.Engine;\nimport org.apache.catalina.Host;\nimport org.apache.catalina.Lifecycle;\nimport org.apache.catalina.LifecycleEvent;\nimport org.apache.catalina.LifecycleListener;\nimport org.apache.catalina.LifecycleState;\nimport org.apache.catalina.Valve;\nimport org.apache.catalina.WebResourceRoot;\nimport org.apache.catalina.Wrapper;\nimport org.apache.catalina.connector.Connector;\nimport org.apache.catalina.core.StandardContext;\nimport org.apache.catalina.loader.WebappLoader;\nimport org.apache.catalina.startup.Tomcat;\nimport org.apache.catalina.webresources.StandardRoot;\nimport org.apache.tomcat.util.scan.StandardJarScanFilter;\nimport org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;\nimport org.springframework.boot.web.embedded.tomcat.TomcatWebServer;\nimport org.springframework.boot.web.server.WebServer;\nimport org.springframework.boot.web.servlet.ServletContextInitializer;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.StringUtils;\n\nimport javax.servlet.ServletContainerInitializer;\nimport java.io.File;\nimport java.net.URL;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.ROOT_WEB_CONTEXT_PATH;\n\n/**\n * @author Phillip Webb\n * @author Dave Syer\n * @author Brock Mills\n * @author Stephane Nicoll\n * @author Andy Wilkinson\n * @author Eddú Meléndez\n * @author Christoffer Sawicki\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class ArkTomcatServletWebServerFactory extends TomcatServletWebServerFactory {\n\n    private static final Charset  DEFAULT_CHARSET = StandardCharsets.UTF_8;\n\n    private final Object          lock            = new Object();\n\n    @ArkInject\n    private EmbeddedServerService embeddedServerService;\n\n    @ArkInject\n    private BizManagerService     bizManagerService;\n\n    private File                  baseDirectory;\n\n    private String                protocol        = DEFAULT_PROTOCOL;\n\n    private int                   backgroundProcessorDelay;\n\n    @Override\n    public WebServer getWebServer(ServletContextInitializer... initializers) {\n        if (embeddedServerService == null && ArkClient.getInjectionService() != null) {\n            // 非应用上下文 (例如: Spring Management Context) 没有经历 Start 生命周期, 不会被注入 ArkServiceInjectProcessor,\n            // 因此 @ArkInject 没有被处理, 需要手动处理\n            ArkClient.getInjectionService().inject(this);\n        }\n        if (embeddedServerService == null) {\n            // 原有的逻辑中也有这个空值判断, 不确定注入后是否还会有用例会导致 embeddedServerService 为空\n            // 因此仍保留此 if 空值判断\n            return super.getWebServer(initializers);\n        }\n        if (embeddedServerService.getEmbedServer(getPort()) == null) {\n            synchronized (lock) {\n                if (embeddedServerService.getEmbedServer(getPort()) == null) {\n                    embeddedServerService.putEmbedServer(getPort(), initEmbedTomcat());\n                }\n            }\n        }\n        Tomcat embedTomcat = (Tomcat) embeddedServerService.getEmbedServer(getPort());\n        prepareContext(embedTomcat.getHost(), initializers);\n        return getWebServer(embedTomcat);\n    }\n\n    @Override\n    public String getContextPath() {\n        String contextPath = super.getContextPath();\n        if (bizManagerService == null) {\n            return contextPath;\n        }\n        Biz biz = bizManagerService.getBizByClassLoader(Thread.currentThread()\n            .getContextClassLoader());\n        if (!StringUtils.isEmpty(contextPath)) {\n            return contextPath;\n        } else if (biz != null) {\n            if (StringUtils.isEmpty(biz.getWebContextPath())) {\n                return ROOT_WEB_CONTEXT_PATH;\n            }\n            return biz.getWebContextPath();\n        } else {\n            return ROOT_WEB_CONTEXT_PATH;\n        }\n    }\n\n    private Tomcat initEmbedTomcat() {\n        Tomcat tomcat = new Tomcat();\n        File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir(\"tomcat\");\n        tomcat.setBaseDir(baseDir.getAbsolutePath());\n        Connector connector = new Connector(this.protocol);\n        tomcat.getService().addConnector(connector);\n        customizeConnector(connector);\n        tomcat.setConnector(connector);\n        tomcat.getHost().setAutoDeploy(false);\n        configureEngine(tomcat.getEngine());\n        for (Connector additionalConnector : getAdditionalTomcatConnectors()) {\n            tomcat.getService().addConnector(additionalConnector);\n        }\n        return tomcat;\n    }\n\n    @Override\n    public void setBaseDirectory(File baseDirectory) {\n        this.baseDirectory = baseDirectory;\n    }\n\n    /**\n     * The Tomcat protocol to use when create the {@link Connector}.\n     *\n     * @param protocol the protocol\n     * @see Connector#Connector(String)\n     */\n    @Override\n    public void setProtocol(String protocol) {\n        AssertUtils.isFalse(StringUtils.isEmpty(protocol), \"Protocol must not be empty\");\n        this.protocol = protocol;\n    }\n\n    @Override\n    public void setBackgroundProcessorDelay(int delay) {\n        this.backgroundProcessorDelay = delay;\n    }\n\n    private void configureEngine(Engine engine) {\n        engine.setBackgroundProcessorDelay(this.backgroundProcessorDelay);\n        for (Valve valve : getEngineValves()) {\n            engine.getPipeline().addValve(valve);\n        }\n    }\n\n    @Override\n    protected void postProcessContext(Context context) {\n        ((WebappLoader) context.getLoader())\n            .setLoaderClass(\"com.alipay.sofa.ark.web.embed.tomcat.ArkTomcatEmbeddedWebappClassLoader\");\n    }\n\n    @Override\n    protected void prepareContext(Host host, ServletContextInitializer[] initializers) {\n        if (host.getState() == LifecycleState.NEW) {\n            super.prepareContext(host, initializers);\n        } else {\n            File documentRoot = getValidDocumentRoot();\n            StandardContext context = new StandardContext();\n            if (documentRoot != null) {\n                context.setResources(new StandardRoot(context));\n            }\n            context.setName(getContextPath());\n            context.setDisplayName(getDisplayName());\n            context.setPath(getContextPath());\n            File docBase = (documentRoot != null) ? documentRoot : createTempDir(\"tomcat-docbase\");\n            context.setDocBase(docBase.getAbsolutePath());\n            context.addLifecycleListener(new Tomcat.FixContextListener());\n            context.setParentClassLoader(Thread.currentThread().getContextClassLoader());\n            resetDefaultLocaleMapping(context);\n            addLocaleMappings(context);\n            context.setUseRelativeRedirects(false);\n            configureTldSkipPatterns(context);\n            WebappLoader loader = new WebappLoader();\n            loader\n                .setLoaderClass(\"com.alipay.sofa.ark.web.embed.tomcat.ArkTomcatEmbeddedWebappClassLoader\");\n            loader.setDelegate(true);\n            context.setLoader(loader);\n            if (isRegisterDefaultServlet()) {\n                addDefaultServlet(context);\n            }\n            if (shouldRegisterJspServlet()) {\n                addJspServlet(context);\n                addJasperInitializer(context);\n            }\n            context.addLifecycleListener(new StaticResourceConfigurer(context));\n            ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);\n            context.setParent(host);\n            configureContext(context, initializersToUse);\n            host.addChild(context);\n        }\n    }\n\n    /**\n     * Override Tomcat's default locale mappings to align with other servers. See\n     * {@code org.apache.catalina.util.CharsetMapperDefault.properties}.\n     *\n     * @param context the context to reset\n     */\n    private void resetDefaultLocaleMapping(StandardContext context) {\n        context.addLocaleEncodingMappingParameter(Locale.ENGLISH.toString(),\n            DEFAULT_CHARSET.displayName());\n        context.addLocaleEncodingMappingParameter(Locale.FRENCH.toString(),\n            DEFAULT_CHARSET.displayName());\n    }\n\n    private void addLocaleMappings(StandardContext context) {\n        for (Map.Entry<Locale, Charset> entry : getLocaleCharsetMappings().entrySet()) {\n            context.addLocaleEncodingMappingParameter(entry.getKey().toString(), entry.getValue()\n                .toString());\n        }\n    }\n\n    private void configureTldSkipPatterns(StandardContext context) {\n        StandardJarScanFilter filter = new StandardJarScanFilter();\n        filter.setTldSkip(StringUtils.collectionToCommaDelimitedString(getTldSkipPatterns()));\n        context.getJarScanner().setJarScanFilter(filter);\n    }\n\n    private void addDefaultServlet(Context context) {\n        Wrapper defaultServlet = context.createWrapper();\n        defaultServlet.setName(\"default\");\n        defaultServlet.setServletClass(\"org.apache.catalina.servlets.DefaultServlet\");\n        defaultServlet.addInitParameter(\"debug\", \"0\");\n        defaultServlet.addInitParameter(\"listings\", \"false\");\n        defaultServlet.setLoadOnStartup(1);\n        // Otherwise the default location of a Spring DispatcherServlet cannot be set\n        defaultServlet.setOverridable(true);\n        context.addChild(defaultServlet);\n        context.addServletMappingDecoded(\"/\", \"default\");\n    }\n\n    private void addJspServlet(Context context) {\n        Wrapper jspServlet = context.createWrapper();\n        jspServlet.setName(\"jsp\");\n        jspServlet.setServletClass(getJsp().getClassName());\n        jspServlet.addInitParameter(\"fork\", \"false\");\n        for (Map.Entry<String, String> entry : getJsp().getInitParameters().entrySet()) {\n            jspServlet.addInitParameter(entry.getKey(), entry.getValue());\n        }\n        jspServlet.setLoadOnStartup(3);\n        context.addChild(jspServlet);\n        context.addServletMappingDecoded(\"*.jsp\", \"jsp\");\n        context.addServletMappingDecoded(\"*.jspx\", \"jsp\");\n    }\n\n    private void addJasperInitializer(StandardContext context) {\n        try {\n            ServletContainerInitializer initializer = (ServletContainerInitializer) ClassUtils\n                .forName(\"org.apache.jasper.servlet.JasperInitializer\", null).newInstance();\n            context.addServletContainerInitializer(initializer, null);\n        } catch (Exception ex) {\n            // Probably not Tomcat 8\n        }\n    }\n\n    final class StaticResourceConfigurer implements LifecycleListener {\n\n        private final Context context;\n\n        private StaticResourceConfigurer(Context context) {\n            this.context = context;\n        }\n\n        @Override\n        public void lifecycleEvent(LifecycleEvent event) {\n            if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {\n                addResourceJars(getUrlsOfJarsWithMetaInfResources());\n            }\n        }\n\n        private void addResourceJars(List<URL> resourceJarUrls) {\n            for (URL url : resourceJarUrls) {\n                String path = url.getPath();\n                if (path.endsWith(\".jar\") || path.endsWith(\".jar!/\")) {\n                    String jar = url.toString();\n                    if (!jar.startsWith(\"jar:\")) {\n                        // A jar file in the file system. Convert to Jar URL.\n                        jar = \"jar:\" + jar + \"!/\";\n                    }\n                    addResourceSet(jar);\n                } else {\n                    addResourceSet(url.toString());\n                }\n            }\n        }\n\n        private void addResourceSet(String resource) {\n            try {\n                if (isInsideNestedJar(resource)) {\n                    // It's a nested jar but we now don't want the suffix because Tomcat\n                    // is going to try and locate it as a root URL (not the resource\n                    // inside it)\n                    resource = resource.substring(0, resource.length() - 2);\n                }\n                URL url = new URL(resource);\n                String path = \"/META-INF/resources\";\n                this.context.getResources().createWebResourceSet(\n                    WebResourceRoot.ResourceSetType.RESOURCE_JAR, \"/\", url, path);\n            } catch (Exception ex) {\n                // Ignore (probably not a directory)\n            }\n        }\n\n        private boolean isInsideNestedJar(String dir) {\n            return dir.indexOf(\"!/\") < dir.lastIndexOf(\"!/\");\n        }\n    }\n\n    /**\n     * Factory method called to create the {@link TomcatWebServer}. Subclasses can\n     * override this method to return a different {@link TomcatWebServer} or apply\n     * additional processing to the Tomcat server.\n     *\n     * @param tomcat the Tomcat server.\n     * @return a new {@link TomcatWebServer} instance\n     */\n    protected WebServer getWebServer(Tomcat tomcat) {\n        return new ArkTomcatWebServer(tomcat, getPort() >= 0, tomcat);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/main/java/com/alipay/sofa/ark/springboot/web/ArkTomcatWebServer.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot.web;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport javax.naming.NamingException;\n\nimport org.apache.catalina.Container;\nimport org.apache.catalina.Context;\nimport org.apache.catalina.Engine;\nimport org.apache.catalina.Lifecycle;\nimport org.apache.catalina.LifecycleException;\nimport org.apache.catalina.LifecycleState;\nimport org.apache.catalina.Service;\nimport org.apache.catalina.connector.Connector;\nimport org.apache.catalina.startup.Tomcat;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.apache.naming.ContextBindings;\n\nimport org.springframework.boot.web.embedded.tomcat.ConnectorStartFailedException;\nimport org.springframework.boot.web.embedded.tomcat.TomcatWebServer;\nimport org.springframework.boot.web.server.WebServer;\nimport org.springframework.boot.web.server.WebServerException;\nimport org.springframework.util.Assert;\n\n/**\n * NOTE: Tomcat instance will start immediately when create ArkTomcatWebServer object.\n *\n * @author Brian Clozel\n * @author Kristine Jetzke\n * @author 0.6.0\n * @since 2.0.0\n */\npublic class ArkTomcatWebServer implements WebServer {\n\n    private static final Log                logger            = LogFactory\n                                                                  .getLog(TomcatWebServer.class);\n\n    private static final AtomicInteger      containerCounter  = new AtomicInteger(-1);\n\n    private final Object                    monitor           = new Object();\n\n    private final Map<Service, Connector[]> serviceConnectors = new HashMap<>();\n\n    private final Tomcat                    tomcat;\n\n    private final boolean                   autoStart;\n\n    private volatile boolean                started;\n\n    private Thread                          awaitThread;\n\n    private Tomcat                          arkEmbedTomcat;\n\n    /**\n     * Create a new {@link ArkTomcatWebServer} instance.\n     * @param tomcat the underlying Tomcat server\n     */\n    public ArkTomcatWebServer(Tomcat tomcat) {\n        this(tomcat, true);\n    }\n\n    /**\n     * Create a new {@link TomcatWebServer} instance.\n     * @param tomcat the underlying Tomcat server\n     * @param autoStart if the server should be started\n     */\n    public ArkTomcatWebServer(Tomcat tomcat, boolean autoStart) {\n        Assert.notNull(tomcat, \"Tomcat Server must not be null\");\n        this.tomcat = tomcat;\n        this.autoStart = autoStart;\n        initialize();\n    }\n\n    public ArkTomcatWebServer(Tomcat tomcat, boolean autoStart, Tomcat arkEmbedTomcat) {\n        this(tomcat, autoStart);\n        this.arkEmbedTomcat = arkEmbedTomcat;\n    }\n\n    private void initialize() throws WebServerException {\n        logger.info(\"Tomcat initialized with port(s): \" + getPortsDescription(false));\n        synchronized (this.monitor) {\n            try {\n                addInstanceIdToEngineName();\n\n                Context context = findContext();\n                context.addLifecycleListener((event) -> {\n                    if (context.equals(event.getSource())\n                            && Lifecycle.START_EVENT.equals(event.getType())) {\n                        // Remove service connectors so that protocol binding doesn't\n                        // happen when the service is started.\n                        removeServiceConnectors();\n                    }\n                });\n\n                // Start the server to trigger initialization listeners\n                this.tomcat.start();\n\n                // We can re-throw failure exception directly in the main thread\n                rethrowDeferredStartupExceptions();\n\n                try {\n                    ContextBindings.bindClassLoader(context, context.getNamingToken(),\n                            Thread.currentThread().getContextClassLoader());\n                }\n                catch (NamingException ex) {\n                    // Naming is not enabled. Continue\n                }\n\n                // Unlike Jetty, all Tomcat threads are daemon threads. We create a\n                // blocking non-daemon to stop immediate shutdown\n                startDaemonAwaitThread();\n            }\n            catch (Exception ex) {\n                stopSilently();\n                throw new WebServerException(\"Unable to start embedded Tomcat\", ex);\n            }\n        }\n    }\n\n    private Context findContext() {\n        for (Container child : this.tomcat.getHost().findChildren()) {\n            if (child instanceof Context) {\n                if (child.getParentClassLoader().equals(\n                    Thread.currentThread().getContextClassLoader())) {\n                    return (Context) child;\n                }\n            }\n        }\n        throw new IllegalStateException(\"The host does not contain a Context\");\n    }\n\n    private void addInstanceIdToEngineName() {\n        int instanceId = containerCounter.incrementAndGet();\n        if (instanceId > 0) { // We already have a tomcat container, so just return the existing tomcat.\n            Engine engine = this.tomcat.getEngine();\n            engine.setName(engine.getName() + \"-\" + instanceId);\n        }\n    }\n\n    private void removeServiceConnectors() {\n        for (Service service : this.tomcat.getServer().findServices()) {\n            Connector[] connectors = service.findConnectors().clone();\n            this.serviceConnectors.put(service, connectors);\n            for (Connector connector : connectors) {\n                service.removeConnector(connector);\n            }\n        }\n    }\n\n    private void rethrowDeferredStartupExceptions() throws Exception {\n        Container[] children = this.tomcat.getHost().findChildren();\n        for (Container container : children) {\n            // just to check current biz status\n            if (container.getParentClassLoader() == Thread.currentThread().getContextClassLoader()) {\n                if (!LifecycleState.STARTED.equals(container.getState())) {\n                    throw new IllegalStateException(container + \" failed to start\");\n                }\n            }\n        }\n    }\n\n    private void startDaemonAwaitThread() {\n        awaitThread = new Thread(\"container-\" + (containerCounter.get())) {\n\n            @Override\n            public void run() {\n                getTomcat().getServer().await();\n            }\n\n        };\n        awaitThread.setContextClassLoader(Thread.currentThread().getContextClassLoader());\n        awaitThread.setDaemon(false);\n        awaitThread.start();\n    }\n\n    @Override\n    public void start() throws WebServerException {\n        synchronized (this.monitor) {\n            if (this.started) {\n                return;\n            }\n\n            Context context = findContext();\n            try {\n                addPreviouslyRemovedConnectors();\n                this.tomcat.getConnector();\n                checkThatConnectorsHaveStarted();\n                this.started = true;\n                logger.info(\"Tomcat started on port(s): \" + getPortsDescription(true)\n                            + \" with context path '\" + getContextPath() + \"'\");\n            } catch (ConnectorStartFailedException ex) {\n                stopSilently();\n                throw ex;\n            } catch (Exception ex) {\n                throw new WebServerException(\"Unable to start embedded Tomcat server\", ex);\n            } finally {\n                ContextBindings.unbindClassLoader(context, context.getNamingToken(), getClass()\n                    .getClassLoader());\n            }\n        }\n    }\n\n    void checkThatConnectorsHaveStarted() {\n        checkConnectorHasStarted(this.tomcat.getConnector());\n        for (Connector connector : this.tomcat.getService().findConnectors()) {\n            checkConnectorHasStarted(connector);\n        }\n    }\n\n    private void checkConnectorHasStarted(Connector connector) {\n        if (LifecycleState.FAILED.equals(connector.getState())) {\n            throw new ConnectorStartFailedException(connector.getPort());\n        }\n    }\n\n    public void stopSilently() {\n        stopContext();\n        try {\n            stopTomcatIfNecessary();\n        } catch (LifecycleException ex) {\n            // Ignore\n        }\n    }\n\n    private void stopContext() {\n        Context context = findContext();\n        getTomcat().getHost().removeChild(context);\n    }\n\n    private void stopTomcatIfNecessary() throws LifecycleException {\n        if (tomcat != arkEmbedTomcat) {\n            tomcat.destroy();\n        }\n        awaitThread.stop();\n    }\n\n    void addPreviouslyRemovedConnectors() {\n        Service[] services = this.tomcat.getServer().findServices();\n        for (Service service : services) {\n            Connector[] connectors = this.serviceConnectors.get(service);\n            if (connectors != null) {\n                for (Connector connector : connectors) {\n                    service.addConnector(connector);\n                    if (!this.autoStart) {\n                        stopProtocolHandler(connector);\n                    }\n                }\n                this.serviceConnectors.remove(service);\n            }\n        }\n    }\n\n    private void stopProtocolHandler(Connector connector) {\n        try {\n            connector.getProtocolHandler().stop();\n        } catch (Exception ex) {\n            logger.error(\"Cannot pause connector: \", ex);\n        }\n    }\n\n    Map<Service, Connector[]> getServiceConnectors() {\n        return this.serviceConnectors;\n    }\n\n    @Override\n    public void stop() throws WebServerException {\n        synchronized (this.monitor) {\n            boolean wasStarted = this.started;\n            try {\n                this.started = false;\n                try {\n                    stopContext();\n                    stopTomcatIfNecessary();\n                } catch (Throwable ex) {\n                    // swallow and continue\n                }\n            } catch (Exception ex) {\n                throw new WebServerException(\"Unable to stop embedded Tomcat\", ex);\n            } finally {\n                if (wasStarted) {\n                    containerCounter.decrementAndGet();\n                }\n            }\n        }\n    }\n\n    private String getPortsDescription(boolean localPort) {\n        StringBuilder ports = new StringBuilder();\n        for (Connector connector : this.tomcat.getService().findConnectors()) {\n            if (ports.length() != 0) {\n                ports.append(' ');\n            }\n            int port = localPort ? connector.getLocalPort() : connector.getPort();\n            ports.append(port).append(\" (\").append(connector.getScheme()).append(')');\n        }\n        return ports.toString();\n    }\n\n    @Override\n    public int getPort() {\n        Connector connector = this.tomcat.getConnector();\n        if (connector != null) {\n            return connector.getLocalPort();\n        }\n        return 0;\n    }\n\n    private String getContextPath() {\n        return findContext().getPath();\n    }\n\n    /**\n     * Returns access to the underlying Tomcat server.\n     * @return the Tomcat server\n     */\n    public Tomcat getTomcat() {\n        return this.tomcat;\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
    "content": "com.alipay.sofa.ark.springboot.ArkServletAutoConfiguration\ncom.alipay.sofa.ark.springboot.ArkServletLegacyAutoConfiguration\ncom.alipay.sofa.ark.springboot.ArkReactiveAutoConfiguration\ncom.alipay.sofa.ark.springboot.ArkAutoProcessorConfiguration"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/main/resources/META-INF/spring.factories",
    "content": "org.springframework.context.ApplicationListener=\\\ncom.alipay.sofa.ark.springboot.listener.ArkApplicationStartListener,\\\ncom.alipay.sofa.ark.springboot.listener.ArkDeployStaticBizListener,\\\ncom.alipay.sofa.ark.springboot.listener.PropertiesResetListener\norg.springframework.boot.autoconfigure.EnableAutoConfiguration=\\\ncom.alipay.sofa.ark.springboot.ArkServletAutoConfiguration,\\\ncom.alipay.sofa.ark.springboot.ArkServletLegacyAutoConfiguration,\\\ncom.alipay.sofa.ark.springboot.ArkReactiveAutoConfiguration,\\\ncom.alipay.sofa.ark.springboot.ArkAutoProcessorConfiguration"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/test/java/com/alipay/sofa/ark/springboot/listener/ArkDeployStaticBizListenerTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot.listener;\n\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.support.startup.EmbedSofaArkBootstrap;\nimport org.junit.Test;\nimport org.mockito.MockedStatic;\nimport org.mockito.Mockito;\nimport org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;\nimport org.springframework.context.annotation.AnnotationConfigApplicationContext;\nimport org.springframework.context.event.ContextRefreshedEvent;\nimport org.springframework.context.event.ContextStartedEvent;\n\nimport static org.mockito.Mockito.*;\n\n/**\n * @author gaowh\n * @version 1.0\n * @time 2024/12/30\n */\npublic class ArkDeployStaticBizListenerTest {\n\n    /**\n     * classloader不匹配的场景\n     */\n    @Test\n    public void testDiffClassLoader() {\n        ArkDeployStaticBizListener listener = new ArkDeployStaticBizListener();\n        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try (MockedStatic<EmbedSofaArkBootstrap> bootstrap = mockStatic(EmbedSofaArkBootstrap.class);\n             MockedStatic<ArkConfigs> arkConfigs = mockStatic(ArkConfigs.class)) {\n            arkConfigs.when(ArkConfigs::isEmbedEnable).thenReturn(true);\n            arkConfigs.when(ArkConfigs::isEmbedStaticBizEnable).thenReturn(true);\n            Thread.currentThread().setContextClassLoader(new ClassLoader() {\n            });\n            listener.onApplicationEvent(new ContextRefreshedEvent(new AnnotationConfigApplicationContext()));\n            bootstrap.verify(Mockito.times(0), EmbedSofaArkBootstrap::deployStaticBizAfterEmbedMasterBizStarted);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    /**\n     * applicationEvent 不是 ContextRefreshedEvent 的场景\n     */\n    @Test\n    public void testNonContextRefreshedEvent() {\n        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try (MockedStatic<EmbedSofaArkBootstrap> bootstrap = mockStatic(EmbedSofaArkBootstrap.class);\n             MockedStatic<ArkConfigs> arkConfigs = mockStatic(ArkConfigs.class)) {\n            ClassLoader classLoader = ArkDeployStaticBizListener.class.getClassLoader();\n            Thread.currentThread().setContextClassLoader(classLoader);\n            arkConfigs.when(ArkConfigs::isEmbedEnable).thenReturn(true);\n            arkConfigs.when(ArkConfigs::isEmbedStaticBizEnable).thenReturn(true);\n            ArkDeployStaticBizListener listener = new ArkDeployStaticBizListener();\n            listener.onApplicationEvent(new ContextStartedEvent(new AnnotationConfigApplicationContext()));\n            bootstrap.verify(Mockito.times(0), EmbedSofaArkBootstrap::deployStaticBizAfterEmbedMasterBizStarted);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    /**\n    *  applicationEvent不是 spring 根上下文的场景\n    */\n    @Test\n    public void testNonSpringRootEvent() {\n        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try (MockedStatic<EmbedSofaArkBootstrap> bootstrap = mockStatic(EmbedSofaArkBootstrap.class);\n            MockedStatic<ArkConfigs> arkConfigs = mockStatic(ArkConfigs.class)) {\n            ClassLoader classLoader = ArkDeployStaticBizListener.class.getClassLoader();\n            Thread.currentThread().setContextClassLoader(classLoader);\n            arkConfigs.when(ArkConfigs::isEmbedEnable).thenReturn(true);\n            arkConfigs.when(ArkConfigs::isEmbedStaticBizEnable).thenReturn(true);\n            ArkDeployStaticBizListener listener = new ArkDeployStaticBizListener();\n            listener.onApplicationEvent(new ContextStartedEvent(new AnnotationConfigServletWebServerApplicationContext()));\n            bootstrap.verify(Mockito.times(0), EmbedSofaArkBootstrap::deployStaticBizAfterEmbedMasterBizStarted);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n\n    /**\n     * 事件重复发送的场景\n     */\n    @Test\n    public void testDeployed() {\n        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        try (MockedStatic<EmbedSofaArkBootstrap> bootstrap = mockStatic(EmbedSofaArkBootstrap.class);\n             MockedStatic<ArkConfigs> arkConfigs = mockStatic(ArkConfigs.class)) {\n            ClassLoader classLoader = ArkDeployStaticBizListener.class.getClassLoader();\n            Thread.currentThread().setContextClassLoader(classLoader);\n            arkConfigs.when(ArkConfigs::isEmbedEnable).thenReturn(true);\n            arkConfigs.when(ArkConfigs::isEmbedStaticBizEnable).thenReturn(true);\n            ArkDeployStaticBizListener listener = new ArkDeployStaticBizListener();\n            listener.onApplicationEvent(new ContextRefreshedEvent(new AnnotationConfigApplicationContext()));\n            // 容器刷新事件已经发送过，重复发送不会重复部署\n            listener.onApplicationEvent(new ContextRefreshedEvent(new AnnotationConfigApplicationContext()));\n            bootstrap.verify(Mockito.times(1), EmbedSofaArkBootstrap::deployStaticBizAfterEmbedMasterBizStarted);\n        } finally {\n            Thread.currentThread().setContextClassLoader(contextClassLoader);\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/test/java/com/alipay/sofa/ark/springboot/loader/CachedLaunchedURLClassLoaderTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot.loader;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.springframework.boot.loader.archive.ExplodedArchive;\n\nimport java.io.File;\nimport java.lang.reflect.Field;\nimport java.net.URL;\nimport java.util.Map;\n\nimport static com.alipay.sofa.ark.springboot.loader.JarLauncher.main;\nimport static org.junit.Assert.*;\n\npublic class CachedLaunchedURLClassLoaderTest {\n\n    private CachedLaunchedURLClassLoader cachedLaunchedURLClassLoader;\n\n    private File                         resourcesDir = new File(\"src/test/resources/\");\n\n    @Before\n    public void setUp() throws Exception {\n        cachedLaunchedURLClassLoader = new CachedLaunchedURLClassLoader(true, new ExplodedArchive(\n            resourcesDir), new URL[] { new URL(\"file:///\" + resourcesDir.getAbsolutePath()) }, this\n            .getClass().getClassLoader());\n    }\n\n    @Test\n    public void testLoadClass() throws Exception {\n\n        Field field = CachedLaunchedURLClassLoader.class.getDeclaredField(\"classCache\");\n        field.setAccessible(true);\n        assertEquals(0, ((Map) field.get(cachedLaunchedURLClassLoader)).size());\n\n        try {\n            cachedLaunchedURLClassLoader.loadClass(\"a\", true);\n            assertTrue(false);\n        } catch (ClassNotFoundException cnfe) {\n        }\n\n        try {\n            cachedLaunchedURLClassLoader.loadClass(\"a\", true);\n            assertTrue(false);\n        } catch (ClassNotFoundException cnfe) {\n        }\n\n        assertEquals(CachedLaunchedURLClassLoaderTest.class,\n            cachedLaunchedURLClassLoader.loadClass(\n                \"com.alipay.sofa.ark.springboot.loader.CachedLaunchedURLClassLoaderTest\", true));\n        assertEquals(CachedLaunchedURLClassLoaderTest.class,\n            cachedLaunchedURLClassLoader.loadClass(\n                \"com.alipay.sofa.ark.springboot.loader.CachedLaunchedURLClassLoaderTest\", true));\n        assertEquals(1, ((Map) field.get(cachedLaunchedURLClassLoader)).size());\n    }\n\n    @Test\n    public void testFindResource() throws Exception {\n\n        Field field = CachedLaunchedURLClassLoader.class.getDeclaredField(\"resourceUrlCache\");\n        field.setAccessible(true);\n        assertEquals(0, ((Map) field.get(cachedLaunchedURLClassLoader)).size());\n\n        assertEquals(null, cachedLaunchedURLClassLoader.findResource(\"c\"));\n        assertEquals(null, cachedLaunchedURLClassLoader.findResource(\"c\"));\n        assertEquals(null, cachedLaunchedURLClassLoader.findResource(\"d\"));\n        assertEquals(2, ((Map) field.get(cachedLaunchedURLClassLoader)).size());\n    }\n\n    @Test\n    public void testFindResources() throws Exception {\n\n        Field field = CachedLaunchedURLClassLoader.class.getDeclaredField(\"resourcesUrlCache\");\n        field.setAccessible(true);\n        assertEquals(0, ((Map) field.get(cachedLaunchedURLClassLoader)).size());\n\n        assertEquals(false, cachedLaunchedURLClassLoader.findResources(\"b\").hasMoreElements());\n        assertEquals(null, cachedLaunchedURLClassLoader.findResources(\"b\"));\n        assertEquals(1, ((Map) field.get(cachedLaunchedURLClassLoader)).size());\n    }\n\n    @Test\n    public void testJarLauncher() throws Exception {\n        try {\n            main(new String[] {});\n        } catch (Exception e) {\n        }\n        assertNotNull(new JarLauncher().createClassLoader(new URL[] {}));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/test/java/com/alipay/sofa/ark/springboot/web/ArkTomcatServletWebServerFactoryTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot.web;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.container.service.biz.BizManagerServiceImpl;\nimport com.alipay.sofa.ark.spi.service.injection.InjectionService;\nimport com.alipay.sofa.ark.springboot.web.ArkTomcatServletWebServerFactory.StaticResourceConfigurer;\nimport org.apache.catalina.Context;\nimport org.apache.catalina.LifecycleException;\nimport org.apache.catalina.core.StandardContext;\nimport org.apache.catalina.core.StandardHost;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.springframework.boot.web.embedded.tomcat.TomcatWebServer;\nimport org.springframework.boot.web.servlet.ServletContextInitializer;\nimport org.springframework.boot.web.servlet.server.Jsp;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.ROOT_WEB_CONTEXT_PATH;\nimport static com.alipay.sofa.ark.spi.model.BizState.RESOLVED;\nimport static java.lang.Thread.currentThread;\nimport static org.junit.Assert.assertEquals;\n\npublic class ArkTomcatServletWebServerFactoryTest {\n\n    private ArkTomcatServletWebServerFactory arkTomcatServletWebServerFactory = new ArkTomcatServletWebServerFactory();\n\n    private ClassLoader                      currentThreadContextClassLoader;\n    private InjectionService                 injectionService;\n\n    @Before\n    public void setUp() {\n        injectionService = ArkClient.getInjectionService();\n        ArkClient.setInjectionService(null);\n        currentThreadContextClassLoader = currentThread().getContextClassLoader();\n    }\n\n    @After\n    public void tearDown() {\n        ArkClient.setInjectionService(injectionService);\n        currentThread().setContextClassLoader(currentThreadContextClassLoader);\n    }\n\n    @Test\n    public void testGetWebServerWithEmbeddedServerServiceNull() {\n        assertEquals(TomcatWebServer.class, arkTomcatServletWebServerFactory.getWebServer()\n            .getClass());\n    }\n\n    @Test\n    public void testGetContextPath() throws Exception {\n\n        assertEquals(\"\", arkTomcatServletWebServerFactory.getContextPath());\n\n        BizManagerServiceImpl bizManagerService = new BizManagerServiceImpl();\n        Field field = ArkTomcatServletWebServerFactory.class.getDeclaredField(\"bizManagerService\");\n        field.setAccessible(true);\n        field.set(arkTomcatServletWebServerFactory, bizManagerService);\n        assertEquals(ROOT_WEB_CONTEXT_PATH, arkTomcatServletWebServerFactory.getContextPath());\n\n        BizModel biz = new BizModel();\n        biz.setBizName(\"bbb\");\n        biz.setBizState(RESOLVED);\n        biz.setBizVersion(\"ccc\");\n        biz.setClassLoader(this.getClass().getClassLoader());\n        bizManagerService.registerBiz(biz);\n        assertEquals(ROOT_WEB_CONTEXT_PATH, arkTomcatServletWebServerFactory.getContextPath());\n\n        biz.setWebContextPath(\"/ddd\");\n        currentThread().setContextClassLoader(biz.getBizClassLoader());\n        assertEquals(\"/ddd\", arkTomcatServletWebServerFactory.getContextPath());\n\n        arkTomcatServletWebServerFactory.setContextPath(\"/aaa\");\n        assertEquals(\"/aaa\", arkTomcatServletWebServerFactory.getContextPath());\n    }\n\n    @Test\n    public void testPrepareContext() throws LifecycleException {\n\n        StandardHost host = new StandardHost();\n        host.init();\n\n        assertEquals(0, host.getChildren().length);\n        arkTomcatServletWebServerFactory.setRegisterDefaultServlet(true);\n        currentThread().setContextClassLoader(this.getClass().getClassLoader());\n        Jsp jsp = new Jsp();\n        jsp.setRegistered(true);\n        // Otherwise JSP won't be loaded by ApplicationClassLoader, so JSP-relative initialization code won't be executed.\n        jsp.setClassName(\"com.alipay.sofa.ark.springboot.web.ArkTomcatServletWebServerFactoryTest\");\n        arkTomcatServletWebServerFactory.setJsp(jsp);\n        arkTomcatServletWebServerFactory.prepareContext(host, new ServletContextInitializer[] {});\n        assertEquals(1, host.getChildren().length);\n    }\n\n    @Test\n    public void testOtherMethods() {\n        arkTomcatServletWebServerFactory.setBackgroundProcessorDelay(10);\n        arkTomcatServletWebServerFactory.setBaseDirectory(null);\n        arkTomcatServletWebServerFactory.setProtocol(\"8888\");\n    }\n\n    @Test\n    public void testStaticResourceConfigurer() throws Exception {\n\n        List<URL> urls = new ArrayList<>();\n        urls.add(new URL(\"file:///aaa.jar!/\"));\n        urls.add(new URL(\"jar:file:///aaa.jar!/\"));\n        urls.add(new URL(\"file:///aaa\"));\n        urls.add(new URL(\"file:///!/aaa!/\"));\n\n        Constructor<StaticResourceConfigurer> declaredConstructor = StaticResourceConfigurer.class\n            .getDeclaredConstructor(ArkTomcatServletWebServerFactory.class, Context.class);\n        declaredConstructor.setAccessible(true);\n        StaticResourceConfigurer staticResourceConfigurer = declaredConstructor.newInstance(\n            arkTomcatServletWebServerFactory, new StandardContext());\n\n        Method addResourceJars = StaticResourceConfigurer.class.getDeclaredMethod(\n            \"addResourceJars\", List.class);\n        addResourceJars.setAccessible(true);\n        addResourceJars.invoke(staticResourceConfigurer, urls);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/test/java/com/alipay/sofa/ark/springboot/web/ArkTomcatWebServerTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.springboot.web;\n\nimport com.alipay.sofa.ark.web.embed.tomcat.EmbeddedServerServiceImpl;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.lang.reflect.Field;\n\npublic class ArkTomcatWebServerTest {\n\n    private ArkTomcatServletWebServerFactory arkTomcatServletWebServerFactory;\n\n    private ArkTomcatWebServer               arkTomcatWebServer;\n\n    @Before\n    public void setUp() throws Exception {\n        arkTomcatServletWebServerFactory = new ArkTomcatServletWebServerFactory();\n        Field field = ArkTomcatServletWebServerFactory.class\n            .getDeclaredField(\"embeddedServerService\");\n        field.setAccessible(true);\n        field.set(arkTomcatServletWebServerFactory, new EmbeddedServerServiceImpl());\n        arkTomcatWebServer = (ArkTomcatWebServer) arkTomcatServletWebServerFactory.getWebServer();\n    }\n\n    @After\n    public void tearDown() {\n    }\n\n    @Test\n    public void testGetWebServerWithEmbeddedServerServiceNull() {\n        //        NOTE: tomcat can not be stopped and restarted due to a Spring context destroy problem.\n        //        Spring community will fix this issue in the future, so catch all exception now.\n        try {\n            arkTomcatWebServer.stopSilently();\n        } catch (Exception e) {\n        }\n        try {\n            arkTomcatWebServer.stop();\n        } catch (Exception e) {\n        }\n    }\n\n    @Test\n    public void testOtherMethods() {\n        arkTomcatWebServer.getPort();\n        try {\n            arkTomcatWebServer.checkThatConnectorsHaveStarted();\n        } catch (Exception e) {\n        }\n        try {\n            arkTomcatWebServer.addPreviouslyRemovedConnectors();\n        } catch (Exception e) {\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/test/java/com/alipay/sofa/ark/test/ArkBootRunnerTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.test;\n\nimport com.alipay.sofa.ark.container.test.TestClassLoader;\nimport com.alipay.sofa.ark.spi.event.ArkEvent;\nimport com.alipay.sofa.ark.spi.service.ArkInject;\nimport com.alipay.sofa.ark.spi.service.event.EventAdminService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport com.alipay.sofa.ark.springboot.runner.ArkBootEmbedRunner;\nimport com.alipay.sofa.ark.test.springboot.BaseSpringApplication;\nimport com.alipay.sofa.ark.test.springboot.facade.SampleService;\nimport org.junit.Test;\nimport org.junit.runner.Description;\nimport org.junit.runner.RunWith;\nimport org.junit.runner.manipulation.Filter;\nimport org.junit.runner.manipulation.NoTestsRemainException;\nimport org.junit.runner.manipulation.Sorter;\nimport org.junit.runners.BlockJUnit4ClassRunner;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.lang.reflect.Field;\nimport java.util.Comparator;\n\nimport static com.alipay.sofa.ark.test.springboot.TestValueHolder.getTestValue;\nimport static org.junit.Assert.*;\nimport static org.springframework.util.ReflectionUtils.*;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\n@RunWith(ArkBootEmbedRunner.class)\n@SpringBootTest(classes = BaseSpringApplication.class)\npublic class ArkBootRunnerTest {\n\n    @Autowired\n    public SampleService        sampleService;\n\n    @ArkInject\n    public PluginManagerService pluginManagerService;\n\n    @ArkInject\n    public EventAdminService    eventAdminService;\n\n    @Test\n    public void test() throws NoTestsRemainException {\n\n        assertNotNull(sampleService);\n        assertNotNull(pluginManagerService);\n        assertEquals(\"SampleService\", sampleService.say());\n\n        ArkBootEmbedRunner runner = new ArkBootEmbedRunner(ArkBootRunnerTest.class);\n        Field field = findField(ArkBootEmbedRunner.class, \"runner\");\n        assertNotNull(field);\n\n        makeAccessible(field);\n        BlockJUnit4ClassRunner springRunner = (BlockJUnit4ClassRunner) getField(field, runner);\n        assertTrue(springRunner.getClass().getCanonicalName()\n            .equals(SpringRunner.class.getCanonicalName()));\n\n        ClassLoader loader = springRunner.getTestClass().getJavaClass().getClassLoader();\n        assertTrue(loader.getClass().getCanonicalName()\n            .equals(TestClassLoader.class.getCanonicalName()));\n\n        assertEquals(0, getTestValue());\n        eventAdminService.sendEvent(new ArkEvent() {\n            @Override\n            public String getTopic() {\n                return \"test-event-A\";\n            }\n        });\n        assertEquals(10, getTestValue());\n        eventAdminService.sendEvent(new ArkEvent() {\n            @Override\n            public String getTopic() {\n                return \"test-event-B\";\n            }\n        });\n        assertEquals(20, getTestValue());\n\n        runner.filter(new Filter() {\n            @Override\n            public boolean shouldRun(Description description) {\n                return true;\n            }\n\n            @Override\n            public String describe() {\n                return \"\";\n            }\n        });\n        runner.sort(new Sorter(new Comparator<Description>() {\n            @Override\n            public int compare(Description o1, Description o2) {\n                return 0;\n            }\n        }) {\n        });\n    }\n\n    /**\n     * issue#234\n     */\n    @Test\n    public void testLogClassCastBug() {\n        Throwable throwable = null;\n        try {\n            this.getClass().getClassLoader()\n                .loadClass(\"org.apache.logging.slf4j.Log4jLoggerFactory\").newInstance();\n        } catch (Throwable t) {\n            throwable = t;\n        }\n        assertNull(throwable);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/test/java/com/alipay/sofa/ark/test/ArkBootTestNGTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.test;\n\nimport com.alipay.sofa.ark.support.listener.TestNGOnArkEmbeded;\nimport com.alipay.sofa.ark.test.springboot.BaseSpringApplication;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.testng.AbstractTestNGSpringContextTests;\nimport org.testng.annotations.Test;\n\n/**\n * @author qilong.zql\n * @since 1.0.0\n */\n@TestNGOnArkEmbeded\n@SpringBootTest(classes = BaseSpringApplication.class)\npublic class ArkBootTestNGTest extends AbstractTestNGSpringContextTests {\n\n    @Test\n    public void testSpringTestNG() {\n        // Ignore\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/test/java/com/alipay/sofa/ark/test/MultiArkBootRunnerTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.test;\n\nimport com.alipay.sofa.ark.springboot.runner.ArkBootEmbedRunner;\nimport com.alipay.sofa.ark.springboot.runner.ArkBootRunner;\nimport com.alipay.sofa.ark.test.springboot.BaseSpringApplication;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.boot.test.mock.mockito.MockBean;\nimport org.springframework.context.ApplicationContext;\n\n/**\n * @author qilong.zql\n * @since 1.0.0\n */\n@RunWith(ArkBootEmbedRunner.class)\n@SpringBootTest(classes = BaseSpringApplication.class)\npublic class MultiArkBootRunnerTest {\n\n    @MockBean\n    private ApplicationContext applicationContext;\n\n    @Test\n    public void test() {\n        // just to test issue 252\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/test/java/com/alipay/sofa/ark/test/SpringbootRunnerTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.test;\n\nimport com.alipay.sofa.ark.spi.service.ArkInject;\nimport com.alipay.sofa.ark.spi.service.event.EventAdminService;\nimport com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;\nimport com.alipay.sofa.ark.test.springboot.facade.SampleService;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport static com.alipay.sofa.ark.api.ArkClient.getInjectionService;\nimport static com.alipay.sofa.ark.common.util.ClassLoaderUtils.pushContextClassLoader;\nimport static com.alipay.sofa.ark.spi.constant.Constants.EMBED_ENABLE;\nimport static com.alipay.sofa.ark.test.springboot.BaseSpringApplication.main;\nimport static com.alipay.sofa.ark.test.springboot.TestValueHolder.getTestValue;\nimport static java.lang.ClassLoader.getSystemClassLoader;\nimport static java.lang.System.setProperty;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\n\n/**\n * @author bingjie.lbj\n */\npublic class SpringbootRunnerTest {\n\n    @Autowired\n    public SampleService sampleService;\n\n    @ArkInject\n    PluginManagerService pluginManagerService;\n\n    @ArkInject\n    EventAdminService    eventAdminService;\n\n    @Before\n    public void before() {\n        pushContextClassLoader(getSystemClassLoader());\n        setProperty(EMBED_ENABLE, \"true\");\n    }\n\n    @After\n    public void after() {\n        setProperty(EMBED_ENABLE, \"\");\n    }\n\n    @Test\n    public void test() {\n        try {\n            main(new String[]{});\n            getInjectionService().inject(this);\n            assertNotNull(pluginManagerService);\n            assertEquals(0, getTestValue());\n            eventAdminService.sendEvent(() -> \"test-event-A\");\n            assertEquals(10, getTestValue());\n            eventAdminService.sendEvent(() -> \"test-event-B\");\n            assertEquals(20, getTestValue());\n        } catch (Exception e) {\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/test/java/com/alipay/sofa/ark/test/springboot/BaseSpringApplication.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.test.springboot;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.ImportResource;\n\nimport static org.springframework.boot.SpringApplication.exit;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\n@ImportResource({ \"classpath*:META-INF/sofa-ark-test/*.xml\" })\n@SpringBootApplication\npublic class BaseSpringApplication {\n\n    private static SpringApplication  springApplication;\n\n    private static ApplicationContext applicationContext;\n\n    public static void main(String[] args) {\n        springApplication = new SpringApplication(BaseSpringApplication.class);\n        applicationContext = springApplication.run(args);\n    }\n\n    public static void stop() {\n        exit(applicationContext, () -> 0);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/test/java/com/alipay/sofa/ark/test/springboot/RegisterMockEmbedTomcatService.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.test.springboot;\n\nimport com.alipay.sofa.ark.container.registry.ContainerServiceProvider;\nimport com.alipay.sofa.ark.spi.service.ArkInject;\nimport com.alipay.sofa.ark.spi.service.registry.RegistryService;\nimport com.alipay.sofa.ark.spi.web.EmbeddedServerService;\nimport com.alipay.sofa.ark.web.embed.tomcat.EmbeddedServerServiceImpl;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\n@Component\npublic class RegisterMockEmbedTomcatService implements BeanPostProcessor, InitializingBean {\n    @ArkInject\n    private RegistryService registryService;\n\n    @Override\n    public void afterPropertiesSet() throws Exception {\n        registryService.publishService(EmbeddedServerService.class,\n            new EmbeddedServerServiceImpl(), new ContainerServiceProvider());\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/test/java/com/alipay/sofa/ark/test/springboot/TestValueHolder.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.test.springboot;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class TestValueHolder {\n    private static int testValue = 0;\n\n    public static int getTestValue() {\n        return testValue;\n    }\n\n    public static void setTestValue(int testValue) {\n        TestValueHolder.testValue = testValue;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/test/java/com/alipay/sofa/ark/test/springboot/facade/SampleService.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.test.springboot.facade;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic interface SampleService {\n    /**\n     * a simple test facade\n     * @return\n     */\n    String say();\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/test/java/com/alipay/sofa/ark/test/springboot/impl/SampleServiceImpl.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.test.springboot.impl;\n\nimport com.alipay.sofa.ark.test.springboot.facade.SampleService;\n\n/**\n * @author qilong.zql\n * @since 0.3.0\n */\npublic class SampleServiceImpl implements SampleService {\n    @Override\n    public String say() {\n        return \"SampleService\";\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/test/java/com/alipay/sofa/ark/test/springboot/impl/TestBizEventHandler.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.test.springboot.impl;\n\nimport com.alipay.sofa.ark.spi.event.ArkEvent;\nimport com.alipay.sofa.ark.spi.service.event.EventHandler;\nimport com.alipay.sofa.ark.test.springboot.TestValueHolder;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class TestBizEventHandler implements EventHandler {\n    @Override\n    public void handleEvent(ArkEvent event) {\n        if (event.getTopic().equals(\"test-event-A\")) {\n            TestValueHolder.setTestValue(10);\n        } else if (event.getTopic().equals(\"test-event-B\")) {\n            TestValueHolder.setTestValue(20);\n        }\n    }\n\n    @Override\n    public int getPriority() {\n        return LOWEST_PRECEDENCE + 20;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/test/resources/META-INF/sofa-ark-test/sofa-ark-test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n    <bean id=\"sampleService\" class=\"com.alipay.sofa.ark.test.springboot.impl.SampleServiceImpl\"/>\n\n    <bean id=\"testBizEventHandler\" class=\"com.alipay.sofa.ark.test.springboot.impl.TestBizEventHandler\"/>\n\n</beans>"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/test/resources/config/application.properties",
    "content": "management.server.port=8888\nspring.application.name=test\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-springboot-integration/ark-springboot-starter/src/test/resources/logback.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration>\n\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder>\n            <pattern>\n                %d{dd-MM-yyyy HH:mm:ss.SSS} %magenta([%thread]) %highlight(%-5level) %logger{36}.%M - %msg%n\n            </pattern>\n        </encoder>\n    </appender>\n\n    <root level=\"info\">\n        <appender-ref ref=\"STDOUT\"/>\n    </root>\n\n</configuration>"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Module Overview\n\n**Artifact ID**: `sofa-ark-support-starter`\n**Package**: `com.alipay.sofa.ark.support`\n\nThis module provides startup support and test integration for SOFAArk applications.\n\n## Purpose\n\n- Bootstrap Ark container in IDE or test environments\n- Provide TestNG and JUnit runners for testing on Ark\n- Support embedded Ark execution\n\n## Key Classes\n\n### Startup (`startup/`)\n- `SofaArkBootstrap` - Bootstrap Ark container for testing/IDE development\n- `EmbedSofaArkBootstrap` - Embedded mode bootstrap\n- `EntryMethod` - Entry method execution handler\n\n### Test Runners (`runner/`)\n- `ArkJUnit4Runner` - JUnit 4 runner for tests on Ark\n- `ArkJUnit4EmbedRunner` - JUnit 4 runner for embedded Ark\n- `JUnitExecutionListener` - JUnit execution listener\n\n### TestNG Support (`listener/`)\n- `TestNGOnArk` - Annotation to run TestNG tests on Ark\n- `TestNGOnArkEmbeded` - Annotation for embedded mode\n- `ArkTestNGExecutionListener` - TestNG execution listener\n- `ArkTestNGInvokedMethodListener` - TestNG method listener\n- `ArkTestNGAlterSuiteListener` - TestNG suite listener\n\n### Common Utilities (`common/`)\n- `DelegateArkContainer` - Delegate to Ark container\n- `DelegateToMasterBizClassLoaderHook` - ClassLoader delegation hook\n- `AddBizInResourcesHook` - Hook to add biz from resources\n- `MasterBizEnvironmentHolder` - Hold master biz environment\n\n### Threading (`thread/`)\n- `IsolatedThreadGroup` - Thread group for isolated execution\n- `LaunchRunner` - Run launch in isolated thread\n\n## Usage for Testing\n\n### JUnit 4\n```java\n@RunWith(ArkJUnit4Runner.class)\npublic class MyTest {\n    @Test\n    public void test() { ... }\n}\n```\n\n### TestNG\n```java\n@TestNGOnArk\npublic class MyTest {\n    @Test\n    public void test() { ... }\n}\n```\n\n## Dependencies\n\n- `sofa-ark-container` - Container implementation\n- `sofa-ark-spi` - Service interfaces\n- `junit` / `testng` - Test frameworks\n\n## Used By\n\n- Application test code\n- IDE development mode"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>sofa-ark-support</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>sofa-ark-support-starter</artifactId>\n    <name>${project.groupId}:${project.artifactId}</name>\n\n    <properties>\n\n    </properties>\n\n    <dependencies>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot</artifactId>\n        </dependency>\n\n        <!--SOFAArk modules-->\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-common</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-spi</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-archive</artifactId>\n        </dependency>\n\n        <!--junit-->\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>provided</scope>\n        </dependency>\n\n        <!--testNg-->\n        <dependency>\n            <groupId>org.testng</groupId>\n            <artifactId>testng</artifactId>\n            <scope>provided</scope>\n        </dependency>\n\n        <!--test-->\n        <!--   fix https://github.com/sofastack/sofa-ark/issues/851     -->\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-all</artifactId>\n            <!--     please notice to use the old version to make ide import sofa-ark-all by jar rather than local classpath       -->\n            <version>${sofa.ark.version.old}</version>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>com.google.inject</groupId>\n            <artifactId>guice</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-core</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-inline</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <configuration>\n                    <threadCount>1</threadCount>\n                    <properties>\n                        <junit>false</junit>\n                    </properties>\n                </configuration>\n                <dependencies>\n                    <dependency>\n                        <groupId>org.apache.maven.surefire</groupId>\n                        <artifactId>surefire-junit47</artifactId>\n                        <version>${surefire.version}</version>\n                    </dependency>\n                    <dependency>\n                        <groupId>org.apache.maven.surefire</groupId>\n                        <artifactId>surefire-testng</artifactId>\n                        <version>${surefire.version}</version>\n                    </dependency>\n                </dependencies>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/main/java/com/alipay/sofa/ark/support/common/AddBizInResourcesHook.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support.common;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.loader.JarBizArchive;\nimport com.alipay.sofa.ark.loader.archive.JarFileArchive;\nimport com.alipay.sofa.ark.spi.archive.Archive;\nimport com.alipay.sofa.ark.spi.archive.BizArchive;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.service.biz.AddBizToStaticDeployHook;\nimport com.alipay.sofa.ark.spi.service.extension.Extension;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.JarURLConnection;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.SOFA_ARK_MODULE;\n\n/**\n * @author lianglipeng.llp@alibaba-inc.com\n * @version $Id: AddBizInResourcesHook.java, v 0.1 2024年07月06日 19:48 立蓬 Exp $\n */\n@Extension(\"add-biz-in-resources-to-deploy\")\npublic class AddBizInResourcesHook implements AddBizToStaticDeployHook {\n\n    @Override\n    public List<BizArchive> getStaticBizToAdd() throws Exception {\n        List<BizArchive> archives = new ArrayList<>();\n        if (ArkConfigs.isEmbedEnable() && isEmbedStaticBizInResourceEnable()) {\n            archives.addAll(getBizArchiveFromResources());\n        }\n        return archives;\n    }\n\n    private boolean isEmbedStaticBizInResourceEnable() {\n        return ArkConfigs.getBooleanValue(Constants.EMBED_STATIC_BIZ_IN_RESOURCE_ENABLE,\n            Boolean.TRUE);\n    }\n\n    protected List<BizArchive> getBizArchiveFromResources() throws Exception {\n        List<BizArchive> archives = new ArrayList<>();\n        URL bizDirURL = ArkClient.getMasterBiz().getBizClassLoader().getResource(SOFA_ARK_MODULE);\n        if (null == bizDirURL) {\n            return archives;\n        }\n\n        if (bizDirURL.getProtocol().equals(\"file\")) {\n            return getBizArchiveForFile(bizDirURL);\n        }\n\n        if (bizDirURL.getProtocol().equals(\"jar\")) {\n            return getBizArchiveForJar(bizDirURL);\n        }\n\n        return archives;\n    }\n\n    private List<BizArchive> getBizArchiveForFile(URL bizDirURL) throws Exception {\n        List<BizArchive> archives = new ArrayList<>();\n\n        File bizDir = org.apache.commons.io.FileUtils.toFile(bizDirURL);\n        if (!bizDir.exists() || !bizDir.isDirectory() || null == bizDir.listFiles()) {\n            return archives;\n        }\n\n        for (File bizFile : bizDir.listFiles()) {\n            archives.add(new JarBizArchive(new JarFileArchive(bizFile)));\n        }\n        return archives;\n    }\n\n    private List<BizArchive> getBizArchiveForJar(URL bizDirURL) throws Exception{\n        List<BizArchive> archives = new ArrayList<>();\n\n        JarFileArchive jarFileArchive = getJarFileArchiveFromUrl(bizDirURL);\n        String prefix = getEntryName(bizDirURL);\n        List<Archive>  archivesFromJar = jarFileArchive.getNestedArchives(entry -> !entry.isDirectory() && entry.getName().startsWith(prefix) && !entry.getName().equals(prefix));\n\n        for (Archive archiveFromJarEntry : archivesFromJar) {\n            archives.add(new JarBizArchive(archiveFromJarEntry));\n        }\n        return archives;\n    }\n\n    private JarFileArchive getJarFileArchiveFromUrl(URL url) throws Exception {\n        String jarPath = substringBefore(((JarURLConnection) url.openConnection()).getJarFile()\n            .getName(), \"!\");\n        return new JarFileArchive(com.alipay.sofa.ark.common.util.FileUtils.file(jarPath));\n    }\n\n    private String getEntryName(URL url) throws IOException {\n        String classPathEntryName = substringAfter(((JarURLConnection) url.openConnection())\n            .getJarFile().getName(), \"!/\");\n        String urlEntryNameFromClassPath = ((JarURLConnection) url.openConnection()).getJarEntry()\n            .getName();\n        return String.join(\"/\", classPathEntryName, urlEntryNameFromClassPath);\n    }\n\n    public static String substringBefore(String str, String separator) {\n        if (str != null && separator != null && str.length() != 0) {\n            if (separator.length() == 0) {\n                return \"\";\n            } else {\n                int pos = str.indexOf(separator);\n                return pos == -1 ? str : str.substring(0, pos);\n            }\n        } else {\n            return str;\n        }\n    }\n\n    public static String substringAfter(String str, String separator) {\n        if (str != null && str.length() != 0) {\n            if (separator == null) {\n                return \"\";\n            } else {\n                int pos = str.indexOf(separator);\n                return pos == -1 ? \"\" : str.substring(pos + separator.length());\n            }\n        } else {\n            return str;\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/main/java/com/alipay/sofa/ark/support/common/DelegateArkContainer.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support.common;\n\nimport com.alipay.sofa.ark.common.util.AssertUtils;\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.support.startup.SofaArkBootstrap;\n\nimport java.lang.reflect.Method;\n\n/**\n * wrap the {@literal com.alipay.sofa.ark.container.ArkContainer}\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class DelegateArkContainer {\n\n    private static final String         TEST_HELPER             = \"com.alipay.sofa.ark.container.test.TestHelper\";\n    private static final String         CREATE_TEST_CLASSLOADER = \"createTestClassLoader\";\n    private static final String         STOP_CONTAINER          = \"stop\";\n\n    private static Method               CREATE_TEST_CLASSLOADER_METHOD;\n    private static Method               STOP_CONTAINER_METHOD;\n\n    private static volatile Object      arkContainer;\n    private static Object               testHelper;\n    private static volatile ClassLoader testClassLoader;\n    private static final Object         LOCK                    = new Object();\n\n    /**\n     * Launch Ark Container when run tests\n     */\n    public static void launch(Class testClass) {\n        if (arkContainer == null) {\n            synchronized (LOCK) {\n                if (arkContainer == null) {\n                    Object container = SofaArkBootstrap.prepareContainerForTest(testClass);\n                    wrapping(container);\n                    arkContainer = container;\n                }\n            }\n        }\n\n        ClassLoaderUtils.pushContextClassLoader(DelegateArkContainer.getTestClassLoader());\n    }\n\n    /**\n     * wrap {@literal com.alipay.sofa.ark.container.ArkContainer}\n     */\n    protected static void wrapping(Object container) {\n        AssertUtils.assertNotNull(container, \"Ark Container must be not null.\");\n\n        try {\n            Class<?> testHelperClass = container.getClass().getClassLoader().loadClass(TEST_HELPER);\n            testHelper = testHelperClass.getConstructor(Object.class).newInstance(container);\n            CREATE_TEST_CLASSLOADER_METHOD = testHelperClass.getMethod(CREATE_TEST_CLASSLOADER);\n            STOP_CONTAINER_METHOD = container.getClass().getMethod(STOP_CONTAINER);\n        } catch (Exception ex) {\n            // impossible situation\n            throw new RuntimeException(ex);\n        }\n    }\n\n    /**\n     * Get {@literal com.alipay.sofa.ark.container.test.TestClassLoader}, used by\n     * loading test class\n     *\n     * @return\n     */\n    public static ClassLoader getTestClassLoader() {\n        if (testClassLoader == null) {\n            synchronized (LOCK) {\n                if (testClassLoader == null) {\n                    try {\n                        testClassLoader = (ClassLoader) CREATE_TEST_CLASSLOADER_METHOD\n                            .invoke(testHelper);\n                    } catch (Exception ex) {\n                        throw new RuntimeException(ex);\n                    }\n                }\n            }\n        }\n        return testClassLoader;\n    }\n\n    /**\n     * Check whether {@literal com.alipay.sofa.ark.container.ArkContainer} startup or not.\n     */\n    @SuppressWarnings(\"BooleanMethodIsAlwaysInverted\")\n    public static boolean isStarted() {\n        return arkContainer != null;\n    }\n\n    /**\n     * Load class using {@literal com.alipay.sofa.ark.container.test.TestClassLoader}\n     * @param name\n     * @return\n     */\n    public static Class loadClass(String name) {\n        try {\n            return getTestClassLoader().loadClass(name);\n        } catch (Exception ex) {\n            throw new RuntimeException();\n        }\n    }\n\n    public static void shutdown() {\n        if (arkContainer != null) {\n            try {\n                STOP_CONTAINER_METHOD.invoke(arkContainer);\n                arkContainer = null;\n            } catch (Exception ex) {\n                throw new RuntimeException(ex);\n            }\n        }\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/main/java/com/alipay/sofa/ark/support/common/DelegateToMasterBizClassLoaderHook.java",
    "content": "package com.alipay.sofa.ark.support.common;\n\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.service.classloader.ClassLoaderHook;\nimport com.alipay.sofa.ark.spi.service.classloader.ClassLoaderService;\nimport com.alipay.sofa.ark.spi.service.extension.Extension;\n\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Enumeration;\nimport java.util.List;\n\n/**\n * A default hook for biz classloader. Trying to post load class by master biz if not found\n *\n * @author bingjie.lbj\n */\n@Extension(\"biz-classloader-hook\")\npublic class DelegateToMasterBizClassLoaderHook implements ClassLoaderHook<Biz> {\n\n    private static String CGLIB_FLAG = \"CGLIB$$\";\n\n    @Override\n    public Class<?> preFindClass(String name, ClassLoaderService classLoaderService, Biz biz)\n                                                                                             throws ClassNotFoundException {\n        return null;\n    }\n\n    @Override\n    public Class<?> postFindClass(String name, ClassLoaderService classLoaderService, Biz biz)\n                                                                                              throws ClassNotFoundException {\n        ClassLoader masterClassLoader = ArkClient.getMasterBiz().getBizClassLoader();\n        if (biz == null || (biz.getBizClassLoader() == masterClassLoader)) {\n            return null;\n        }\n        // The cglib proxy class cannot be delegate to the master, it must be created by the biz's own defineClass\n        // see: spring 6, org.springframework.cglib.core.AbstractClassGenerator.generate\n        if (name.contains(CGLIB_FLAG)) {\n            return null;\n        }\n        // if Master Biz contains same class in multi jar, need to check each whether is provided\n        Class<?> clazz = masterClassLoader.loadClass(name);\n        if (clazz != null) {\n            if (biz.isDeclared(clazz.getProtectionDomain().getCodeSource().getLocation(), \"\")) {\n                return clazz;\n            }\n\n            try {\n                String classResourceName = name.replace('.', '/') + \".class\";\n                Enumeration<URL> urls = masterClassLoader.getResources(classResourceName);\n                while (urls.hasMoreElements()) {\n                    URL resourceUrl = urls.nextElement();\n                    if (resourceUrl != null && biz.isDeclared(resourceUrl, classResourceName)) {\n                        ArkLoggerFactory.getDefaultLogger().warn(\n                            String.format(\"find class %s from %s in multiple dependencies.\", name,\n                                resourceUrl.getFile()));\n                        return clazz;\n                    }\n                }\n            } catch (IOException e) {\n                return null;\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public URL preFindResource(String name, ClassLoaderService classLoaderService, Biz biz) {\n\n        return null;\n    }\n\n    @Override\n    public URL postFindResource(String name, ClassLoaderService classLoaderService, Biz biz) {\n        if (biz == null || (!biz.isDeclaredMode() && shouldSkip(name))) {\n            return null;\n        }\n\n        ClassLoader masterClassLoader = ArkClient.getMasterBiz().getBizClassLoader();\n        if (biz.getBizClassLoader() == masterClassLoader) {\n            return null;\n        }\n        try {\n            URL resourceUrl = masterClassLoader.getResource(name);\n            if (resourceUrl != null && biz.isDeclared(resourceUrl, name)) {\n                return resourceUrl;\n            }\n\n            Enumeration<URL> matchResourceUrls = postFindResources(name, classLoaderService, biz);\n\n            // get the first resource url when match multiple resources\n            if (matchResourceUrls != null && matchResourceUrls.hasMoreElements()) {\n                return matchResourceUrls.nextElement();\n            }\n        } catch (Exception e) {\n            return null;\n        }\n        return null;\n    }\n\n    @Override\n    public Enumeration<URL> preFindResources(String name, ClassLoaderService classLoaderService,\n                                             Biz biz) throws IOException {\n        return null;\n    }\n\n    @Override\n    public Enumeration<URL> postFindResources(String name, ClassLoaderService classLoaderService,\n                                              Biz biz) throws IOException {\n        if (biz == null || (!biz.isDeclaredMode() && shouldSkip(name))) {\n            return null;\n        }\n        ClassLoader masterClassLoader = ArkClient.getMasterBiz().getBizClassLoader();\n        if (biz.getBizClassLoader() == masterClassLoader) {\n            return null;\n        }\n        try {\n            Enumeration<URL> resourceUrls = masterClassLoader.getResources(name);\n            List<URL> matchedResourceUrls = new ArrayList<>();\n            while (resourceUrls.hasMoreElements()) {\n                URL resourceUrl = resourceUrls.nextElement();\n\n                if (resourceUrl != null && biz.isDeclared(resourceUrl, name)) {\n                    matchedResourceUrls.add(resourceUrl);\n                }\n            }\n            return Collections.enumeration(matchedResourceUrls);\n        } catch (Exception e) {\n            return null;\n        }\n    }\n\n    private boolean shouldSkip(String resourceName) {\n        return !resourceName.endsWith(\".class\");\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/main/java/com/alipay/sofa/ark/support/common/MasterBizEnvironmentHolder.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support.common;\n\nimport org.springframework.core.env.Environment;\n\n/**\n * @author lianglipeng.llp@alibaba-inc.com\n * @version $Id: SpringUtil.java, v 0.1 2024年07月09日 19:51 立蓬 Exp $\n */\n\npublic class MasterBizEnvironmentHolder {\n    private static Environment environment;\n\n    public static void setEnvironment(Environment environment) {\n        MasterBizEnvironmentHolder.environment = environment;\n    }\n\n    public static Environment getEnvironment() {\n        if (null == environment) {\n            throw new RuntimeException(\"master environment is null\");\n        }\n        return environment;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/main/java/com/alipay/sofa/ark/support/listener/ArkTestNGAlterSuiteListener.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support.listener;\n\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.support.common.DelegateArkContainer;\nimport org.testng.IAlterSuiteListener;\nimport org.testng.xml.XmlClass;\nimport org.testng.xml.XmlSuite;\nimport org.testng.xml.XmlTest;\n\nimport java.util.List;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.EMBED_ENABLE;\nimport static com.alipay.sofa.ark.spi.constant.Constants.MASTER_BIZ;\n\n/**\n * @author qilong.zql\n * @since 0.3.0\n */\npublic class ArkTestNGAlterSuiteListener implements IAlterSuiteListener {\n\n    @Override\n    public void alter(List<XmlSuite> suites) {\n        for (XmlSuite xmlSuite : suites) {\n            resetXmlSuite(xmlSuite);\n            resetChildrenXmlSuite(xmlSuite.getChildSuites());\n        }\n    }\n\n    protected void resetXmlSuite(XmlSuite suite) {\n        if (suite == null) {\n            return;\n        }\n        resetXmlSuite(suite.getParentSuite());\n        resetSingleXmlSuite(suite);\n    }\n\n    protected void resetChildrenXmlSuite(List<XmlSuite> childSuites) {\n        if (childSuites.isEmpty()) {\n            return;\n        }\n\n        for (XmlSuite xmlSuite : childSuites) {\n            resetChildrenXmlSuite(xmlSuite.getChildSuites());\n            resetSingleXmlSuite(xmlSuite);\n        }\n\n    }\n\n    protected void resetSingleXmlSuite(XmlSuite suite) {\n        for (XmlTest xmlTest : suite.getTests()) {\n            for (XmlClass xmlClass : xmlTest.getClasses()) {\n                Class testClass = xmlClass.getSupportClass();\n                if (testClass.getAnnotation(TestNGOnArk.class) != null) {\n                    if (!DelegateArkContainer.isStarted()) {\n                        DelegateArkContainer.launch(testClass);\n                    }\n\n                    try {\n                        xmlClass.setClass(DelegateArkContainer.getTestClassLoader().loadClass(\n                            testClass.getCanonicalName()));\n                    } catch (ClassNotFoundException ex) {\n                        throw new ArkRuntimeException(String.format(\n                            \"Load testNG test class %s failed.\", testClass.getCanonicalName()), ex);\n                    }\n                } else if (testClass.getAnnotation(TestNGOnArkEmbeded.class) != null) {\n                    if (!DelegateArkContainer.isStarted()) {\n                        System.setProperty(EMBED_ENABLE, \"true\");\n                        System.setProperty(MASTER_BIZ, \"test master biz\");\n                        DelegateArkContainer.launch(testClass);\n                        System.clearProperty(EMBED_ENABLE);\n                        System.clearProperty(MASTER_BIZ);\n                    }\n\n                    try {\n                        xmlClass.setClass(DelegateArkContainer.getTestClassLoader().loadClass(\n                            testClass.getCanonicalName()));\n                    } catch (ClassNotFoundException ex) {\n                        throw new ArkRuntimeException(String.format(\n                            \"Load testNG test class %s failed.\", testClass.getCanonicalName()), ex);\n                    }\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/main/java/com/alipay/sofa/ark/support/listener/ArkTestNGExecutionListener.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support.listener;\n\nimport com.alipay.sofa.ark.support.common.DelegateArkContainer;\nimport org.testng.IExecutionListener;\n\n/**\n * Shutdown ark container\n *\n * @author qilong.zql\n * @since 0.4.0\n */\npublic class ArkTestNGExecutionListener implements IExecutionListener {\n    @Override\n    public void onExecutionStart() {\n\n    }\n\n    @Override\n    public void onExecutionFinish() {\n        DelegateArkContainer.shutdown();\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/main/java/com/alipay/sofa/ark/support/listener/ArkTestNGInvokedMethodListener.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support.listener;\n\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.support.common.DelegateArkContainer;\nimport org.testng.IInvokedMethod;\nimport org.testng.IInvokedMethodListener;\nimport org.testng.ITestResult;\n\nimport java.lang.annotation.Annotation;\n\n/**\n * @author qilong.zql\n * @since 0.3.0\n */\npublic class ArkTestNGInvokedMethodListener implements IInvokedMethodListener {\n    @Override\n    public void beforeInvocation(IInvokedMethod method, ITestResult testResult) {\n        Class testClass = method.getTestMethod().getTestClass().getRealClass();\n        if (isTestOnArk(testClass)) {\n            ClassLoaderUtils.pushContextClassLoader(DelegateArkContainer.getTestClassLoader());\n        } else {\n            ClassLoaderUtils.pushContextClassLoader(ClassLoader.getSystemClassLoader());\n        }\n    }\n\n    @Override\n    public void afterInvocation(IInvokedMethod method, ITestResult testResult) {\n        ClassLoaderUtils.pushContextClassLoader(ClassLoader.getSystemClassLoader());\n    }\n\n    protected boolean isTestOnArk(Class testClass) {\n        for (Annotation annotation : testClass.getAnnotations()) {\n            String annotationType = annotation.annotationType().getCanonicalName();\n            if (annotationType.equals(TestNGOnArk.class.getCanonicalName())\n                || annotationType.equals(TestNGOnArkEmbeded.class.getCanonicalName())) {\n                return true;\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/main/java/com/alipay/sofa/ark/support/listener/TestNGOnArk.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support.listener;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * any test class annotated by {@link TestNGOnArk} represents\n * that it would run on ark container\n *\n * @author qilong.zql\n * @since 0.3.0\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\npublic @interface TestNGOnArk {\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/main/java/com/alipay/sofa/ark/support/listener/TestNGOnArkEmbeded.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support.listener;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * any test class annotated by {@link TestNGOnArkEmbeded} represents\n * that it would run on ark container\n *\n * used for test ark biz like koupleless, which run ark plugin in embed mode\n * please refer {@link com.alipay.sofa.ark.spi.service.plugin.PluginFactoryService#createEmbedPlugin(com.alipay.sofa.ark.spi.archive.PluginArchive, java.lang.ClassLoader)}\n *\n * @author lvjing2\n * @since 2.2.10\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\npublic @interface TestNGOnArkEmbeded {\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/main/java/com/alipay/sofa/ark/support/runner/ArkJUnit4EmbedRunner.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support.runner;\n\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.support.common.DelegateArkContainer;\nimport org.junit.runner.notification.RunNotifier;\nimport org.junit.runners.BlockJUnit4ClassRunner;\nimport org.junit.runners.model.InitializationError;\nimport org.junit.runners.model.TestClass;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.EMBED_ENABLE;\nimport static com.alipay.sofa.ark.spi.constant.Constants.MASTER_BIZ;\n\n/**\n * used for test ark biz like koupleless, which run ark plugin in embed mode\n * please refer {@link com.alipay.sofa.ark.spi.service.plugin.PluginFactoryService#createEmbedPlugin(com.alipay.sofa.ark.spi.archive.PluginArchive, java.lang.ClassLoader)}\n *\n * @author lvjing2\n * @since 2.2.10\n */\npublic class ArkJUnit4EmbedRunner extends BlockJUnit4ClassRunner {\n\n    /**\n     * Creates a BlockJUnit4ClassRunner to run {@code klass}\n     *\n     * @param klass\n     * @throws InitializationError if the test class is malformed.\n     */\n    public ArkJUnit4EmbedRunner(Class<?> klass) throws InitializationError {\n        super(klass);\n    }\n\n    @Override\n    protected TestClass createTestClass(Class<?> testClass) {\n        try {\n            if (!DelegateArkContainer.isStarted()) {\n                System.setProperty(EMBED_ENABLE, \"true\");\n                System.setProperty(MASTER_BIZ, \"test master biz\");\n                DelegateArkContainer.launch(testClass);\n                System.clearProperty(EMBED_ENABLE);\n                System.clearProperty(MASTER_BIZ);\n            }\n            ClassLoader testClassLoader = DelegateArkContainer.getTestClassLoader();\n            TestClass testKlazz = super.createTestClass(testClassLoader.loadClass(testClass\n                .getName()));\n            ClassLoaderUtils.pushContextClassLoader(ClassLoader.getSystemClassLoader());\n            return testKlazz;\n        } catch (Exception ex) {\n            throw new RuntimeException(ex);\n        }\n    }\n\n    @Override\n    public void run(RunNotifier notifier) {\n        notifier.addListener(JUnitExecutionListener.getRunListener());\n        super.run(notifier);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/main/java/com/alipay/sofa/ark/support/runner/ArkJUnit4Runner.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support.runner;\n\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.support.common.DelegateArkContainer;\nimport org.junit.runner.notification.RunNotifier;\nimport org.junit.runners.BlockJUnit4ClassRunner;\nimport org.junit.runners.model.InitializationError;\nimport org.junit.runners.model.TestClass;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class ArkJUnit4Runner extends BlockJUnit4ClassRunner {\n\n    /**\n     * Creates a BlockJUnit4ClassRunner to run {@code klass}\n     *\n     * @param klass\n     * @throws InitializationError if the test class is malformed.\n     */\n    public ArkJUnit4Runner(Class<?> klass) throws InitializationError {\n        super(klass);\n    }\n\n    @Override\n    protected TestClass createTestClass(Class<?> testClass) {\n        try {\n            if (!DelegateArkContainer.isStarted()) {\n                DelegateArkContainer.launch(testClass);\n            }\n            ClassLoader testClassLoader = DelegateArkContainer.getTestClassLoader();\n            TestClass testKlazz = super.createTestClass(testClassLoader.loadClass(testClass\n                .getName()));\n            ClassLoaderUtils.pushContextClassLoader(ClassLoader.getSystemClassLoader());\n            return testKlazz;\n        } catch (Exception ex) {\n            throw new RuntimeException(ex);\n        }\n    }\n\n    @Override\n    public void run(RunNotifier notifier) {\n        notifier.addListener(JUnitExecutionListener.getRunListener());\n        super.run(notifier);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/main/java/com/alipay/sofa/ark/support/runner/JUnitExecutionListener.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support.runner;\n\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.support.common.DelegateArkContainer;\nimport org.junit.runner.Description;\nimport org.junit.runner.Result;\nimport org.junit.runner.RunWith;\nimport org.junit.runner.notification.RunListener;\n\n/**\n * @author qilong.zql\n * @since 0.3.0\n */\npublic class JUnitExecutionListener extends RunListener {\n\n    private final static String         ARK_JUNIT4_RUNNER       = \"com.alipay.sofa.ark.support.runner.ArkJUnit4Runner\";\n    private final static String         ARK_JUNIT4_EMBED_RUNNER = \"com.alipay.sofa.ark.support.runner.ArkJUnit4EmbedRunner\";\n    private final static String         ARK_BOOT_RUNNER         = \"com.alipay.sofa.ark.springboot.runner.ArkBootRunner\";\n    private final static String         ARK_BOOT_EMBED_RUNNER   = \"com.alipay.sofa.ark.springboot.runner.ArkBootEmbedRunner\";\n    private final static Object         LOCK                    = new Object();\n    private static volatile RunListener singleton;\n\n    private JUnitExecutionListener() {\n    }\n\n    @Override\n    public void testStarted(Description description) throws Exception {\n        if (isTestOnArkContainer(description)) {\n            ClassLoaderUtils.pushContextClassLoader(DelegateArkContainer.getTestClassLoader());\n        } else {\n            ClassLoaderUtils.pushContextClassLoader(ClassLoader.getSystemClassLoader());\n        }\n        super.testStarted(description);\n    }\n\n    @Override\n    public void testFinished(Description description) throws Exception {\n        super.testStarted(description);\n        ClassLoaderUtils.pushContextClassLoader(ClassLoader.getSystemClassLoader());\n    }\n\n    protected boolean isTestOnArkContainer(Description description) {\n        RunWith runWith = description.getTestClass().getAnnotation(RunWith.class);\n\n        if (runWith == null) {\n            return false;\n        }\n\n        Class<?> runnerClass = runWith.value();\n        String className = runnerClass.getName();\n\n        return ARK_JUNIT4_RUNNER.equals(className) || ARK_JUNIT4_EMBED_RUNNER.equals(className)\n               || ARK_BOOT_RUNNER.equals(className) || ARK_BOOT_EMBED_RUNNER.equals(className);\n\n    }\n\n    public static RunListener getRunListener() {\n        if (singleton == null) {\n            synchronized (LOCK) {\n                if (singleton == null) {\n                    singleton = new JUnitExecutionListener();\n                }\n            }\n        }\n        return singleton;\n    }\n\n    @Override\n    public void testRunFinished(Result result) throws Exception {\n        super.testRunFinished(result);\n        DelegateArkContainer.shutdown();\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/main/java/com/alipay/sofa/ark/support/startup/EmbedSofaArkBootstrap.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support.startup;\n\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.bootstrap.ClasspathLauncher;\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.loader.EmbedClassPathArchive;\nimport com.alipay.sofa.ark.spi.argument.CommandArgument;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.support.common.DelegateToMasterBizClassLoaderHook;\nimport org.springframework.core.env.Environment;\n\nimport java.lang.reflect.Method;\nimport java.net.URL;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\n/**\n * Launch an embed ark container\n *\n * @author bingjie.lbj\n */\npublic class EmbedSofaArkBootstrap {\n    private static AtomicBoolean started = new AtomicBoolean(false);\n\n    static Object                arkContainer;\n\n    public static void launch(Environment environment) {\n        if (started.compareAndSet(false, true)) {\n            EntryMethod entryMethod = new EntryMethod(Thread.currentThread());\n\n            getOrSetDefault(\n                Constants.MASTER_BIZ,\n                environment.getProperty(Constants.MASTER_BIZ,\n                    environment.getProperty(\"spring.application.name\", \"master biz\")));\n            getOrSetDefault(Constants.BIZ_CLASS_LOADER_HOOK_DIR,\n                environment.getProperty(Constants.BIZ_CLASS_LOADER_HOOK_DIR));\n            getOrSetDefault(Constants.PLUGIN_EXPORT_CLASS_ENABLE,\n                environment.getProperty(Constants.PLUGIN_EXPORT_CLASS_ENABLE, \"false\"));\n            getOrSetDefault(Constants.BIZ_CLASS_LOADER_HOOK_DIR,\n                DelegateToMasterBizClassLoaderHook.class.getName());\n            try {\n                URL[] urls = getURLClassPath();\n                ClasspathLauncher launcher = new ClasspathLauncher(new EmbedClassPathArchive(\n                    entryMethod.getDeclaringClassName(), entryMethod.getMethod().getName(), urls));\n                arkContainer = launcher.launch(new String[] {}, getClasspath(urls),\n                    entryMethod.getMethod());\n            } catch (Throwable e) {\n                throw new RuntimeException(e);\n            }\n        }\n    }\n\n    private static void getOrSetDefault(String key, String defaultValue) {\n        String value = ArkConfigs.getStringValue(key);\n        if (value == null && defaultValue != null) {\n            ArkConfigs.setSystemProperty(key, defaultValue);\n        }\n    }\n\n    private static String getClasspath(URL[] urls) {\n        StringBuilder sb = new StringBuilder();\n        for (URL url : urls) {\n            sb.append(url.toExternalForm()).append(CommandArgument.CLASSPATH_SPLIT);\n        }\n        return sb.toString();\n    }\n\n    private static URL[] getURLClassPath() {\n        ClassLoader classLoader = EmbedSofaArkBootstrap.class.getClassLoader();\n        return ClassLoaderUtils.getURLs(classLoader);\n    }\n\n    /**\n     * 只会扫描classpath下静态biz包，并启动\n     */\n    public static void deployStaticBizAfterEmbedMasterBizStarted() {\n        if (null == arkContainer) {\n            throw new RuntimeException(\n                \"ArkContainer is null when deploying biz after embed master biz started.\");\n        }\n\n        try {\n            ClassLoader containerClassLoader = arkContainer.getClass().getClassLoader();\n            ClassLoader oldClassLoader = ClassLoaderUtils\n                .pushContextClassLoader(containerClassLoader);\n            Method deployBizMethod = arkContainer.getClass().getMethod(\n                \"deployBizAfterMasterBizReady\");\n            deployBizMethod.invoke(arkContainer);\n            ClassLoaderUtils.popContextClassLoader(oldClassLoader);\n        } catch (Exception e) {\n            throw new RuntimeException(\n                \"Meet exception when deploying biz after embed master biz started!\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/main/java/com/alipay/sofa/ark/support/startup/EntryMethod.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support.startup;\n\nimport com.alipay.sofa.ark.common.util.AssertUtils;\n\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\n\n/**\n * The \"main\" method located from a running thread.\n *\n * @author qilong.zql\n * @since 0.1.0\n * @author Phillip Webb\n */\npublic class EntryMethod {\n\n    private final Method method;\n\n    public EntryMethod() {\n        this(Thread.currentThread());\n    }\n\n    public EntryMethod(Thread thread) {\n        AssertUtils.assertNotNull(thread, \"Thread must not be null\");\n        this.method = getMainMethod(thread);\n    }\n\n    private Method getMainMethod(Thread thread) {\n        for (StackTraceElement element : thread.getStackTrace()) {\n            if (\"main\".equals(element.getMethodName())) {\n                Method method = getMainMethod(element);\n                if (method != null) {\n                    return method;\n                }\n            }\n        }\n        throw new IllegalStateException(\"Unable to find main method\");\n    }\n\n    private Method getMainMethod(StackTraceElement element) {\n        try {\n            Class<?> elementClass = Class.forName(element.getClassName());\n            Method method = elementClass.getDeclaredMethod(\"main\", String[].class);\n            if (Modifier.isStatic(method.getModifiers())) {\n                return method;\n            }\n        } catch (Throwable ex) {\n            // ignore\n        }\n        return null;\n    }\n\n    /**\n     * Returns the actual main method.\n     * @return the main method\n     */\n    public Method getMethod() {\n        return this.method;\n    }\n\n    /**\n     * Return the name of the declaring class.\n     * @return the declaring class name\n     */\n    public String getDeclaringClassName() {\n        return this.method.getDeclaringClass().getName();\n    }\n\n    public String getMethodName() {\n        return this.method.getName();\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/main/java/com/alipay/sofa/ark/support/startup/SofaArkBootstrap.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support.startup;\n\nimport com.alipay.sofa.ark.bootstrap.ClasspathLauncher;\nimport com.alipay.sofa.ark.bootstrap.ClasspathLauncher.ClassPathArchive;\nimport com.alipay.sofa.ark.common.util.AssertUtils;\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.spi.argument.CommandArgument;\nimport com.alipay.sofa.ark.support.thread.IsolatedThreadGroup;\nimport com.alipay.sofa.ark.support.thread.LaunchRunner;\n\nimport java.net.URL;\nimport java.net.URLClassLoader;\n\n/**\n * relaunch a started main method with bootstrapping ark container\n * {@literal org.springframework.boot.maven.RunMojo}\n *\n * @author qilong.zql\n * @author Phillip Webb\n * @since 0.1.0\n */\npublic class SofaArkBootstrap {\n\n    private static final String BIZ_CLASSLOADER = \"com.alipay.sofa.ark.container.service.classloader.BizClassLoader\";\n    private static final String MAIN_ENTRY_NAME = \"remain\";\n    private static EntryMethod  entryMethod;\n\n    public static void launch(String[] args) {\n        try {\n            if (!isSofaArkStarted()) {\n                entryMethod = new EntryMethod(Thread.currentThread());\n                IsolatedThreadGroup threadGroup = new IsolatedThreadGroup(\n                    entryMethod.getDeclaringClassName());\n                LaunchRunner launchRunner = new LaunchRunner(SofaArkBootstrap.class.getName(),\n                    MAIN_ENTRY_NAME, args);\n                Thread launchThread = new Thread(threadGroup, launchRunner,\n                    entryMethod.getMethodName());\n                launchThread.start();\n                LaunchRunner.join(threadGroup);\n                threadGroup.rethrowUncaughtException();\n                System.exit(0);\n            }\n        } catch (Throwable e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static Object prepareContainerForTest(Class testClass) {\n        try {\n            URL[] urls = getURLClassPath();\n            return new ClasspathLauncher(new ClassPathArchive(testClass.getCanonicalName(), null,\n                urls)).launch(getClasspath(urls), testClass);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private static void remain(String[] args) throws Exception {// NOPMD\n        AssertUtils.assertNotNull(entryMethod, \"No Entry Method Found.\");\n        URL[] urls = getURLClassPath();\n        new ClasspathLauncher(new ClassPathArchive(entryMethod.getDeclaringClassName(),\n            entryMethod.getMethodName(), urls)).launch(args, getClasspath(urls),\n            entryMethod.getMethod());\n    }\n\n    private static String getClasspath(URL[] urls) {\n        StringBuilder sb = new StringBuilder();\n        for (URL url : urls) {\n            sb.append(url.toExternalForm()).append(CommandArgument.CLASSPATH_SPLIT);\n        }\n        return sb.toString();\n    }\n\n    private static URL[] getURLClassPath() {\n        ClassLoader classLoader = ClassLoader.getSystemClassLoader();\n        //        return ((URLClassLoader) classLoader).getURLs();\n        return ClassLoaderUtils.getURLs(classLoader);\n    }\n\n    private static boolean isSofaArkStarted() {\n        Class<?> bizClassLoader = SofaArkBootstrap.class.getClassLoader().getClass();\n        return BIZ_CLASSLOADER.equals(bizClassLoader.getCanonicalName());\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/main/java/com/alipay/sofa/ark/support/thread/IsolatedThreadGroup.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support.thread;\n\n/**\n * Isolated {@link ThreadGroup} to capture uncaught exceptions.\n * {@literal org.springframework.boot.maven.IsolatedThreadGroup}\n *\n * @author qilong.zql\n * @author Phillip Webb\n * @since 0.1.0\n */\npublic class IsolatedThreadGroup extends ThreadGroup {\n\n    protected final Object monitor = new Object();\n\n    protected Throwable    exception;\n\n    public IsolatedThreadGroup(String name) {\n        super(name);\n    }\n\n    @Override\n    public void uncaughtException(Thread thread, Throwable ex) {\n        if (!(ex instanceof ThreadDeath)) {\n            synchronized (this.monitor) {\n                this.exception = (this.exception == null ? ex : this.exception);\n            }\n        }\n    }\n\n    public synchronized void rethrowUncaughtException() {\n        synchronized (this.monitor) {\n            if (this.exception != null) {\n                throw new RuntimeException(\"An exception occurred while running. \"\n                                           + this.exception.getMessage(), this.exception);\n            }\n        }\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/main/java/com/alipay/sofa/ark/support/thread/LaunchRunner.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support.thread;\n\n/**\n * Runner used to launch the application.\n *\n * @author qilong.zql\n * @since 0.1.0\n */\n\nimport java.lang.reflect.Method;\n\npublic class LaunchRunner implements Runnable {\n\n    private final String   startClassName;\n\n    private final String   startMethodName;\n\n    private final String[] args;\n\n    public LaunchRunner(String startClassName, String... args) {\n        this(startClassName, \"main\", args);\n    }\n\n    public LaunchRunner(String startClassName, String startMethodName, String... args) {\n        this.startClassName = startClassName;\n        this.startMethodName = startMethodName;\n        this.args = (args != null ? args : new String[] {});\n    }\n\n    @Override\n    public void run() {\n        Thread thread = Thread.currentThread();\n        ClassLoader classLoader = thread.getContextClassLoader();\n        try {\n            Class<?> startClass = classLoader.loadClass(this.startClassName);\n            Method entryMethod;\n            try {\n                entryMethod = startClass.getMethod(startMethodName, String[].class);\n            } catch (NoSuchMethodException ex) {\n                entryMethod = startClass.getDeclaredMethod(startMethodName, String[].class);\n            }\n            if (!entryMethod.isAccessible()) {\n                entryMethod.setAccessible(true);\n            }\n            entryMethod.invoke(null, new Object[] { this.args });\n        } catch (NoSuchMethodException ex) {\n\n            Exception wrappedEx = new Exception(\n                String.format(\n                    \"The specified entry class:%s doesn't contain an entry method:%s with appropriate signature.\",\n                    this.startClassName, this.startMethodName), ex);\n            thread.getThreadGroup().uncaughtException(thread, wrappedEx);\n        } catch (Throwable ex) {\n            thread.getThreadGroup().uncaughtException(thread, ex);\n        }\n    }\n\n    /**\n     * Ark container main thread can exit only other threads exit.\n     *\n     * @param threadGroup\n     */\n    public static void join(ThreadGroup threadGroup) {\n        boolean hasNonDaemonThreads;\n        do {\n            hasNonDaemonThreads = false;\n            Thread[] threads = new Thread[threadGroup.activeCount()];\n            threadGroup.enumerate(threads);\n            for (Thread thread : threads) {\n                if (thread != null && !thread.isDaemon()) {\n                    try {\n                        hasNonDaemonThreads = true;\n                        thread.join();\n                    } catch (InterruptedException ex) {\n                        Thread.currentThread().interrupt();\n                    }\n                }\n            }\n        } while (hasNonDaemonThreads);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/main/resources/META-INF/services/org.testng.ITestNGListener",
    "content": "com.alipay.sofa.ark.support.listener.ArkTestNGAlterSuiteListener\ncom.alipay.sofa.ark.support.listener.ArkTestNGInvokedMethodListener"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/main/resources/META-INF/services/sofa-ark/com.alipay.sofa.ark.spi.service.biz.AddBizToStaticDeployHook",
    "content": "com.alipay.sofa.ark.support.common.AddBizInResourcesHook"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/test/java/com/alipay/sofa/ark/support/AddBizInResourcesHookTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.spi.archive.BizArchive;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.BizState;\nimport com.alipay.sofa.ark.support.common.AddBizInResourcesHook;\nimport org.junit.Test;\nimport org.mockito.MockedStatic;\nimport org.mockito.Mockito;\nimport org.springframework.core.env.Environment;\n\nimport java.util.List;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.when;\n\n/**\n * @author lianglipeng.llp@alibaba-inc.com\n * @version $Id: AddBizInResourcesHookTest.java, v 0.1 2024年07月10日 19:56 立蓬 Exp $\n */\npublic class AddBizInResourcesHookTest {\n    AddBizInResourcesHook addBizInResourcesHook = new AddBizInResourcesHook();\n\n    @Test\n    public void testGetStaticBizToAdd() throws Exception {\n        // case1: ark is not embed\n        ArkConfigs.setEmbedEnable(false);\n        assertEquals(0,addBizInResourcesHook.getStaticBizToAdd().size());\n\n        // case2: ark is embed but set 'EMBED_STATIC_BIZ_IN_RESOURCE_ENABLE' as false\n        ArkConfigs.setEmbedEnable(true);\n        ArkConfigs.putStringValue(Constants.EMBED_STATIC_BIZ_IN_RESOURCE_ENABLE, \"false\");\n\n        // config master biz\n        Biz masterBiz = createTestBizModel(\"test\", \"1.0.0\", BizState.ACTIVATED, this.getClass().getClassLoader());\n\n        try (MockedStatic<ArkClient> mockedStatic = Mockito.mockStatic(ArkClient.class)){\n            mockedStatic.when(ArkClient::getMasterBiz).thenReturn(masterBiz);\n            List<BizArchive> bizArchives = addBizInResourcesHook.getStaticBizToAdd();\n            assertEquals(0, bizArchives.size());\n        }finally {\n            ArkConfigs.setEmbedEnable(false);\n        }\n\n\n        // case3: ark is embed and set 'EMBED_STATIC_BIZ_IN_RESOURCE_ENABLE' as true\n        ArkConfigs.setEmbedEnable(true);\n        ArkConfigs.putStringValue(Constants.EMBED_STATIC_BIZ_IN_RESOURCE_ENABLE, \"true\");\n\n        try (MockedStatic<ArkClient> mockedStatic = Mockito.mockStatic(ArkClient.class)){\n            mockedStatic.when(ArkClient::getMasterBiz).thenReturn(masterBiz);\n            List<BizArchive> bizArchives = addBizInResourcesHook.getStaticBizToAdd();\n            assertEquals(1, bizArchives.size());\n        }finally {\n            ArkConfigs.setEmbedEnable(false);\n        }\n    }\n\n    private BizModel createTestBizModel(String bizName, String bizVersion, BizState bizState,\n                                        ClassLoader classLoader) {\n        BizModel bizModel = new BizModel().setBizState(bizState);\n        bizModel.setBizName(bizName).setBizVersion(bizVersion);\n        bizModel.setClassLoader(classLoader);\n        return bizModel;\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/test/java/com/alipay/sofa/ark/support/DefaultClassLoaderHookTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.common.util.ClassLoaderUtils;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.container.model.BizModel;\nimport com.alipay.sofa.ark.container.pipeline.RegisterServiceStage;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainer;\nimport com.alipay.sofa.ark.container.service.ArkServiceContainerHolder;\nimport com.alipay.sofa.ark.container.service.classloader.BizClassLoader;\nimport com.alipay.sofa.ark.exception.ArkLoaderException;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.model.BizState;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport com.alipay.sofa.ark.spi.service.extension.ArkServiceLoader;\nimport com.alipay.sofa.ark.spi.service.extension.ExtensionLoaderService;\nimport com.alipay.sofa.ark.support.common.DelegateArkContainer;\nimport com.alipay.sofa.ark.support.common.DelegateToMasterBizClassLoaderHook;\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Enumeration;\nimport java.util.List;\n\npublic class DefaultClassLoaderHookTest {\n\n    private BizManagerService         bizManagerService;\n\n    private final ArkServiceContainer arkServiceContainer = new ArkServiceContainer(new String[] {});\n\n    @Before\n    public void before() {\n\n        arkServiceContainer.start();\n        arkServiceContainer.getService(RegisterServiceStage.class).process(null);\n        ArkServiceLoader.setExtensionLoaderService(arkServiceContainer\n            .getService(ExtensionLoaderService.class));\n\n        ArkConfigs.setSystemProperty(Constants.BIZ_CLASS_LOADER_HOOK_DIR,\n            DelegateToMasterBizClassLoaderHook.class.getName());\n\n        bizManagerService = ArkServiceContainerHolder.getContainer().getService(\n            BizManagerService.class);\n    }\n\n    @After\n    public void after() {\n        arkServiceContainer.stop();\n    }\n\n    public static BizModel createTestBizModel(String bizName, String bizVersion, BizState bizState,\n                                              URL[] urls) {\n        BizModel bizModel = new BizModel().setBizState(bizState);\n        bizModel.setBizName(bizName).setBizVersion(bizVersion);\n        BizClassLoader bizClassLoader = new BizClassLoader(bizModel.getIdentity(), urls);\n        bizClassLoader.setBizModel(bizModel);\n        bizModel.setClassPath(urls).setClassLoader(bizClassLoader);\n        return bizModel;\n    }\n\n    @Test\n    public void testLoadClassFromClassLoaderHook() throws Exception {\n        URL bizUrl = this.getClass().getClassLoader().getResource(\"sample-ark-1.0.0-ark-biz.jar\");\n        URL masterUrl1 = this.getClass().getClassLoader()\n            .getResource(\"sample-ark-plugin-common-0.5.1.jar\");\n        URL masterUrl2 = this.getClass().getClassLoader().getResource(\"sofa-ark-sample-springboot-ark-0.3.0.jar\");\n        URL masterUrl3 = this.getClass().getClassLoader().getResource(\"aopalliance-1.0.jar\");\n        URL masterUrl4 = this.getClass().getClassLoader()\n            .getResource(\"com.springsource.org.aopalliance-1.0.0.jar\");\n\n        BizModel bizModel = createTestBizModel(\"biz A\", \"1.0.0\", BizState.RESOLVED,\n            new URL[] { bizUrl });\n        bizModel.setDenyImportClasses(StringUtils.EMPTY_STRING);\n        bizModel.setDenyImportPackages(StringUtils.EMPTY_STRING);\n        bizModel.setDenyImportResources(StringUtils.EMPTY_STRING);\n        bizModel\n            .setDeclaredLibraries(\"sample-ark-plugin-common,com.springsource.org.aopalliance\");\n\n        List<URL> masterUrls = new ArrayList<>();\n        Enumeration<URL> urlEnumeration = this.getClass().getClassLoader().getResources(\"\");\n        while (urlEnumeration.hasMoreElements()) {\n            URL url = urlEnumeration.nextElement();\n            masterUrls.add(url);\n        }\n        masterUrls.add(masterUrl1);\n        masterUrls.add(masterUrl2);\n        masterUrls.add(masterUrl3);\n        masterUrls.add(masterUrl4);\n\n        BizModel masterBizModel = createTestBizModel(\"master biz\", \"1.0.0\", BizState.RESOLVED,\n            masterUrls.toArray(new URL[0]));\n        masterBizModel.setDenyImportClasses(StringUtils.EMPTY_STRING);\n        masterBizModel.setDenyImportPackages(StringUtils.EMPTY_STRING);\n        masterBizModel.setDenyImportResources(StringUtils.EMPTY_STRING);\n\n        bizManagerService.registerBiz(bizModel);\n        bizManagerService.registerBiz(masterBizModel);\n\n        ArkClient.setMasterBiz(masterBizModel);\n\n        // case 1: find class from multiple libs in master classloader\n        Class<?> adviceClazz = bizModel.getBizClassLoader().loadClass(\"org.aopalliance.aop.Advice\");\n        Assert.assertEquals(adviceClazz.getClassLoader(), masterBizModel.getBizClassLoader());\n\n        // case 2: find class from master but not set provided in biz model\n        Assert.assertThrows(ArkLoaderException.class, () -> bizModel.getBizClassLoader().loadClass(\"com.alipay.sofa.ark.sample.facade.SamplemasterService\"));\n\n        // case 3: find class from master in classpath\n        Assert.assertEquals(masterBizModel.getBizClassLoader(), bizModel.getBizClassLoader().loadClass(DelegateArkContainer.class.getName()).getClassLoader());\n\n        // case 4: find class from master in jar\n        Assert.assertEquals(masterBizModel.getBizClassLoader(), bizModel.getBizClassLoader().loadClass(\"com.alipay.sofa.ark.sample.common.SampleClassExported\").getClassLoader());\n\n        // case 5: find resource from master but not set provided in biz model\n        Assert.assertNull(bizModel.getBizClassLoader().getResource(\"org/slf4j/ILoggerFactory.class\"));\n\n        // case 6: find resource from master in classpath\n        Assert.assertNotNull(bizModel.getBizClassLoader().getResource(\n            \"sample-ark-1.0.0-ark-biz.jar\"));\n\n        // case 7: find resource from master in multiple jar\n        Assert.assertNull(bizModel.getBizClassLoader().getResource(\"Sample_Resource_Exported_A\"));\n\n        // case 8: find resources from master but not set provided in biz model\n        Assert.assertFalse(bizModel.getBizClassLoader().getResources(\"org/slf4j/ILoggerFactory.class\")\n            .hasMoreElements());\n\n        // case 9: find resources from master in classpath\n        Assert.assertTrue(bizModel.getBizClassLoader().getResources(\"sample-ark-1.0.0-ark-biz.jar\")\n            .hasMoreElements());\n\n        // case 10: find resources from master in jar\n        Assert.assertTrue(bizModel.getBizClassLoader().getResources(\"Sample_Resource_Exported\")\n            .hasMoreElements());\n    }\n\n    @Test\n    public void getAllResources() throws IOException {\n        URL bizUrl = this.getClass().getClassLoader().getResource(\"sample-ark-1.0.0-ark-biz.jar\");\n        URL resourceUrl = this.getClass().getClassLoader()\n            .getResource(\"sample-ark-plugin-common-0.5.1.jar\");\n\n        URL[] bizUrls = new URL[] { bizUrl, resourceUrl };\n        BizModel declaredBiz = createTestBizModel(\"biz A\", \"1.0.0\", BizState.RESOLVED, bizUrls);\n        declaredBiz.setDeclaredLibraries(\"sample-ark-plugin-common\");\n\n        BizModel notDeclaredBiz = createTestBizModel(\"biz B\", \"1.0.0\", BizState.RESOLVED, bizUrls);\n        notDeclaredBiz.setDeclaredLibraries(\"\");\n\n        List<URL> masterUrls = new ArrayList<>();\n        Enumeration<URL> urlEnumeration = this.getClass().getClassLoader().getResources(\"\");\n        while (urlEnumeration.hasMoreElements()) {\n            URL url = urlEnumeration.nextElement();\n            masterUrls.add(url);\n        }\n        masterUrls.add(resourceUrl);\n\n        BizModel masterBizModel = createTestBizModel(\"master biz\", \"1.0.0\", BizState.RESOLVED,\n            masterUrls.toArray(new URL[0]));\n        masterBizModel.setDenyImportClasses(StringUtils.EMPTY_STRING);\n        masterBizModel.setDenyImportPackages(StringUtils.EMPTY_STRING);\n        masterBizModel.setDenyImportResources(StringUtils.EMPTY_STRING);\n\n        bizManagerService.registerBiz(declaredBiz);\n        bizManagerService.registerBiz(notDeclaredBiz);\n        bizManagerService.registerBiz(masterBizModel);\n\n        ArkClient.setMasterBiz(masterBizModel);\n\n        Assert.assertTrue(masterBizModel.getBizClassLoader()\n            .getResources(\"Sample_Resource_Exported\").hasMoreElements());\n        // declaredMode: get all resources from biz and master biz\n        Enumeration<URL> resourcesFromBizAndMasterBiz = declaredBiz.getBizClassLoader()\n            .getResources(\"Sample_Resource_Exported\");\n        Assert.assertNotNull(resourcesFromBizAndMasterBiz.nextElement());\n\n        // declaredMode: get all resources from biz and master biz\n        Enumeration<URL> resourcesOnlyFromBiz = notDeclaredBiz.getBizClassLoader().getResources(\n            \"Sample_Resource_Exported\");\n        Assert.assertNotNull(resourcesOnlyFromBiz.nextElement());\n        Assert.assertFalse(resourcesOnlyFromBiz.hasMoreElements());\n    }\n\n    @Test\n    public void testPostFindCglibClass() throws ClassNotFoundException {\n        URL bizUrl = this.getClass().getClassLoader().getResource(\"sample-ark-1.0.0-ark-biz.jar\");\n        BizModel testBiz = createTestBizModel(\"biz A\", \"1.0.0\", BizState.RESOLVED, new URL[] { bizUrl });\n        testBiz.setDenyImportClasses(StringUtils.EMPTY_STRING);\n        testBiz.setDenyImportPackages(StringUtils.EMPTY_STRING);\n        testBiz.setDenyImportResources(StringUtils.EMPTY_STRING);\n\n        URL[] urLs = ClassLoaderUtils.getURLs(this.getClass().getClassLoader());\n        BizModel masterBiz = createTestBizModel(\"master biz\", \"1.0.0\", BizState.RESOLVED, urLs);\n        masterBiz.setClassLoader(this.getClass().getClassLoader());\n\n        bizManagerService.registerBiz(testBiz);\n        bizManagerService.registerBiz(masterBiz);\n        ArkClient.setMasterBiz(masterBiz);\n\n        Assert.assertThrows(ArkLoaderException.class, () -> testBiz.getBizClassLoader().loadClass(\"xxxxCGLIB$$\"));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/test/java/com/alipay/sofa/ark/support/MultiSuiteTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support;\n\nimport com.alipay.sofa.ark.support.common.DelegateArkContainer;\nimport com.alipay.sofa.ark.support.listener.ArkTestNGAlterSuiteListener;\nimport org.junit.Assert;\nimport org.testng.annotations.BeforeMethod;\nimport org.testng.annotations.Test;\nimport org.testng.xml.XmlClass;\nimport org.testng.xml.XmlSuite;\nimport org.testng.xml.XmlTest;\n\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * @author qilong.zql\n * @since 0.3.0\n */\npublic class MultiSuiteTest {\n\n    private XmlSuite clown  = new XmlSuite();\n    private XmlSuite parent = new XmlSuite();\n    private XmlSuite child  = new XmlSuite();\n\n    @BeforeMethod\n    public void init() {\n        clown.setParentSuite(parent);\n        clown.getChildSuites().add(child);\n\n        clown.setTests(generateXmlTest(MultiSuiteTest.class));\n        parent.setTests(generateXmlTest(TestNGOnArkTest.class));\n        child.setTests(generateXmlTest(TestNGCommonTest.class));\n    }\n\n    protected List<XmlTest> generateXmlTest(Class testClass) {\n        XmlTest xmlTest = new XmlTest();\n        XmlClass xmlClass = new XmlClass();\n        xmlClass.setClass(testClass);\n        xmlTest.setClasses(Collections.singletonList(xmlClass));\n        return Collections.singletonList(xmlTest);\n    }\n\n    @Test\n    public void test() {\n        new ArkTestNGAlterSuiteListener().alter(Collections.singletonList(clown));\n        Assert.assertTrue(clown.getTests().get(0).getClasses().get(0).getSupportClass()\n            .getClassLoader().equals(ClassLoader.getSystemClassLoader()));\n        Assert.assertTrue(parent.getTests().get(0).getClasses().get(0).getSupportClass()\n            .getClassLoader().equals(DelegateArkContainer.getTestClassLoader()));\n        Assert.assertTrue(child.getTests().get(0).getClasses().get(0).getSupportClass()\n            .getClassLoader().equals(ClassLoader.getSystemClassLoader()));\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/test/java/com/alipay/sofa/ark/support/TestNGCommonTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support;\n\nimport org.testng.Assert;\nimport org.testng.annotations.Test;\n\n/**\n * TestNGCommonTest\n * @author qilong.zql 18/4/26-上午8:58\n */\npublic class TestNGCommonTest {\n    @Test\n    public void test() {\n        ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader();\n        ClassLoader thisClassLoader = this.getClass().getClassLoader();\n\n        Assert.assertTrue(threadClassLoader.equals(ClassLoader.getSystemClassLoader()));\n        Assert.assertTrue(thisClassLoader.equals(ClassLoader.getSystemClassLoader()));\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/test/java/com/alipay/sofa/ark/support/TestNGOnArkTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support;\n\nimport com.alipay.sofa.ark.container.test.TestClassLoader;\nimport com.alipay.sofa.ark.support.listener.TestNGOnArk;\nimport org.testng.Assert;\nimport org.testng.annotations.Test;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\n@TestNGOnArk\npublic class TestNGOnArkTest {\n\n    @Test\n    public void test() {\n        ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader();\n        ClassLoader thisClassLoader = this.getClass().getClassLoader();\n\n        Assert.assertTrue(threadClassLoader.getClass().getCanonicalName()\n            .equals(TestClassLoader.class.getCanonicalName()));\n        Assert.assertTrue(thisClassLoader.getClass().getCanonicalName()\n            .equals(TestClassLoader.class.getCanonicalName()));\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/test/java/com/alipay/sofa/ark/support/runner/ArkJUnit4RunnerTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support.runner;\n\nimport com.alipay.sofa.ark.bootstrap.ContainerClassLoader;\nimport com.alipay.sofa.ark.container.test.TestClassLoader;\nimport org.junit.*;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.model.InitializationError;\n\n/**\n * @author qilong.zql\n * @since 0.3.0\n */\n@RunWith(ArkJUnit4Runner.class)\npublic class ArkJUnit4RunnerTest {\n\n    private static String state;\n\n    @BeforeClass\n    public static void beforeClass() {\n        state = \"@BeforeClass\";\n    }\n\n    @Before\n    public void before() {\n        state = \"@Before\";\n    }\n\n    @Test\n    public void test() {\n        ClassLoader testClassLoader = getClass().getClassLoader();\n        Assert.assertTrue(testClassLoader.getClass().getCanonicalName()\n            .equals(TestClassLoader.class.getCanonicalName()));\n\n        Assert.assertTrue(\"@Before\".equals(state));\n        state = \"@Test\";\n\n        ClassLoader testClCl = testClassLoader.getClass().getClassLoader();\n        Assert.assertTrue(testClCl.getClass().getCanonicalName()\n            .equals(ContainerClassLoader.class.getCanonicalName()));\n    }\n\n    @Test\n    public void testJUnitRunner() {\n        try {\n            Assert.assertTrue(\"@Before\".equals(state));\n            state = \"@Test\";\n\n            ArkJUnit4Runner runner = new ArkJUnit4Runner(ArkJUnit4RunnerTest.class);\n            ClassLoader loader = runner.getTestClass().getJavaClass().getClassLoader();\n            Assert.assertTrue(loader.getClass().getCanonicalName()\n                .equals(TestClassLoader.class.getCanonicalName()));\n        } catch (InitializationError error) {\n            Assert.fail(error.getMessage());\n        }\n    }\n\n    @After\n    public void after() {\n        Assert.assertTrue(\"@Test\".equals(state));\n        state = \"@After\";\n    }\n\n    @AfterClass\n    public static void afterClass() {\n        Assert.assertTrue(\"@After\".equals(state));\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/test/java/com/alipay/sofa/ark/support/runner/CommonJUnit4Test.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support.runner;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * @author qilong.zql\n * @since 0.3.0\n */\npublic class CommonJUnit4Test {\n\n    @Test\n    public void testClassLoader() {\n        Assert.assertTrue(Thread.currentThread().getContextClassLoader()\n            .equals(Test.class.getClassLoader()));\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/test/java/com/alipay/sofa/ark/support/startup/EmbedSofaArkBootstrapTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support.startup;\n\nimport com.alipay.sofa.ark.container.ArkContainer;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static com.alipay.sofa.ark.support.startup.EmbedSofaArkBootstrap.deployStaticBizAfterEmbedMasterBizStarted;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\npublic class EmbedSofaArkBootstrapTest {\n\n    private ArkContainer arkContainer = mock(ArkContainer.class);\n\n    private Object       originalArkContainer;\n\n    @Before\n    public void setUp() {\n        originalArkContainer = EmbedSofaArkBootstrap.arkContainer;\n    }\n\n    @After\n    public void tearDown() {\n        EmbedSofaArkBootstrap.arkContainer = originalArkContainer;\n    }\n\n    @Test\n    public void testDeployStaticBizAfterEmbedMasterBizStarted() throws Exception {\n        when(arkContainer.deployBizAfterMasterBizReady()).thenReturn(arkContainer);\n        EmbedSofaArkBootstrap.arkContainer = arkContainer;\n        deployStaticBizAfterEmbedMasterBizStarted();\n    }\n\n    @Test(expected = RuntimeException.class)\n    public void testDeployStaticBizAfterEmbedMasterBizStartedWithNull() throws Exception {\n        EmbedSofaArkBootstrap.arkContainer = null;\n        deployStaticBizAfterEmbedMasterBizStarted();\n    }\n\n    @Test\n    public void testEntryMethod() {\n        EntryMethod entryMethod = new EntryMethod();\n        assertTrue(entryMethod.getMethodName().contains(\"main\"));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/test/java/com/alipay/sofa/ark/support/thread/IsolatedThreadGroupTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support.thread;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * @author lylingzhen\n */\npublic class IsolatedThreadGroupTest {\n\n    private IsolatedThreadGroup isolatedThreadGroup = new IsolatedThreadGroup(\"group_1\");\n\n    @Test\n    public void testUncaughtException() {\n        Exception e = new Exception();\n        isolatedThreadGroup.uncaughtException(new Thread(), e);\n        assertEquals(e, isolatedThreadGroup.exception);\n    }\n\n    @Test\n    public void testRethrowUncaughtException() {\n        testUncaughtException();\n        try {\n            isolatedThreadGroup.rethrowUncaughtException();\n            assertTrue(false);\n        } catch (RuntimeException re) {\n            assertEquals(re.getCause(), isolatedThreadGroup.exception);\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-support-starter/src/test/java/com/alipay/sofa/ark/support/thread/LaunchRunnerTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.support.thread;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class LaunchRunnerTest {\n\n    public static int   count = 0;\n    public final Object lock  = new Object();\n\n    public static void add(String[] args) {\n        if (args.length > 0) {\n            LaunchRunnerTest.count += Integer.valueOf(args[0]);\n        }\n    }\n\n    @Before\n    public void init() {\n        LaunchRunnerTest.count = 0;\n    }\n\n    @Test\n    public void testMainWithNoParameters() {\n        synchronized (lock) {\n            LaunchRunner launchRunner = new LaunchRunner(MainClass.class.getName());\n            launchRunner.run();\n            Assert.assertTrue(LaunchRunnerTest.count == 0);\n        }\n    }\n\n    @Test\n    public void testMainWithParameters() {\n        synchronized (lock) {\n            LaunchRunner launchRunner = new LaunchRunner(MainClass.class.getName(),\n                new String[] { \"10\" });\n            launchRunner.run();\n            Assert.assertTrue(LaunchRunnerTest.count == 10);\n        }\n    }\n\n    @Test\n    public void testNotMainMethodWithNoParameters() {\n        synchronized (lock) {\n            LaunchRunner launchRunner = new LaunchRunner(LaunchRunnerTest.class.getName(), \"add\",\n                new String[] {});\n            launchRunner.run();\n            Assert.assertTrue(LaunchRunnerTest.count == 0);\n        }\n    }\n\n    @Test\n    public void testNotMainMethodWithParameters() {\n        synchronized (lock) {\n            LaunchRunner launchRunner = new LaunchRunner(LaunchRunnerTest.class.getName(), \"add\",\n                new String[] { \"10\" });\n            launchRunner.run();\n            Assert.assertTrue(LaunchRunnerTest.count == 10);\n        }\n    }\n\n    public static class MainClass {\n\n        private static void main(String[] args) {\n            if (args.length > 0) {\n                LaunchRunnerTest.count += Integer.valueOf(args[0]);\n            }\n        }\n    }\n\n    @Test\n    public void testRunWithException() {\n        new LaunchRunner(MainClass.class.getName(), \"a\", null).run();\n        new LaunchRunner(\"a\", null).run();\n    }\n\n    @Test\n    public void testJoin() {\n        ThreadGroup threadGroup = new ThreadGroup(\"test_thread_group\");\n        new Thread(threadGroup, \"thread_1\").start();\n        new Thread(threadGroup, \"thread_2\").start();\n        new LaunchRunner(MainClass.class.getName(), null).join(threadGroup);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Module Overview\n\n**Artifact ID**: `sofa-ark-tools`\n**Package**: `com.alipay.sofa.ark.tools`\n\nThis module provides the core repackaging utilities for creating Ark Fat JARs. It is used by both `ark-maven-plugin` and `ark-plugin-maven-plugin`.\n\n## Purpose\n\n- Repackage standard JARs into Ark Fat JAR format\n- Layout management for different archive types\n- Main class discovery\n- Git information embedding\n\n## Key Classes\n\n### `Repackager`\nCore class that transforms a regular JAR into an Ark package:\n- `repackage(File target, File module, Libraries libraries)` - Perform repackaging\n- `setBizName(String)` / `setBizVersion(String)` - Set module identity\n- `setMainClass(String)` - Set entry point class\n- `setDenyImportPackages/classes/Resources` - Configure classloader filtering\n- `setDeclaredMode(boolean)` - Enable declared dependency mode\n\n### Layouts (`Layouts.java`)\nDefine JAR structure for different archive types:\n- `ARK_EXECUTABLE` - Executable Ark JAR structure\n- `ARK_BIZ` - Business module JAR structure\n- `ARK_PLUGIN` - Plugin JAR structure\n\n### `JarWriter`\nLower-level JAR writing utility:\n- Write entries to JAR\n- Copy libraries\n- Write manifest\n\n### `MainClassFinder`\nScan class files to find main method entry point.\n\n### `git.JGitParser`\nParse Git repository information to embed in JAR:\n- Commit hash, branch, author\n- Build time information\n\n### `ArtifactItem`\nRepresents a Maven artifact with groupId, artifactId, version, classifier.\n\n## Dependencies\n\n- `sofa-ark-common` - Utilities\n- `spring-boot-loader` - JAR layout from Spring Boot\n- `org.eclipse.jgit` - Git information parsing\n\n## Used By\n\n- `ark-maven-plugin` - Uses Repackager for business modules\n- `ark-plugin-maven-plugin` - Uses Repackager for plugins"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <artifactId>sofa-ark-support</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n    </parent>\n\n    <artifactId>sofa-ark-tools</artifactId>\n    <name>${project.groupId}:${project.artifactId}</name>\n\n    <dependencies>\n\n        <!--SOFAArk modules-->\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-spi</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-common</artifactId>\n        </dependency>\n\n        <!--third party libraries-->\n        <dependency>\n            <groupId>org.ow2.asm</groupId>\n            <artifactId>asm</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-artifact</artifactId>\n        </dependency>\n\n        <!--test-->\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.eclipse.jgit</groupId>\n            <artifactId>org.eclipse.jgit</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/src/main/java/com/alipay/sofa/ark/tools/ArtifactItem.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.tools;\n\nimport com.alipay.sofa.ark.common.util.AssertUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.maven.artifact.Artifact;\n\nimport java.util.Objects;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class ArtifactItem {\n\n    private static final String GAV_SPLIT       = \":\";\n\n    private static final String DEFAULT_VERSION = \"?\";\n\n    private String              groupId;\n\n    private String              artifactId;\n\n    private String              version         = DEFAULT_VERSION;\n\n    private String              classifier;\n\n    private String              type            = \"jar\";\n\n    private String              scope           = \"compile\";\n\n    public String getGroupId() {\n        return groupId;\n    }\n\n    public void setGroupId(String groupId) {\n        this.groupId = groupId;\n    }\n\n    public String getArtifactId() {\n        return artifactId;\n    }\n\n    public void setArtifactId(String artifactId) {\n        this.artifactId = artifactId;\n    }\n\n    public String getVersion() {\n        return version;\n    }\n\n    public void setVersion(String version) {\n        this.version = version;\n    }\n\n    public String getClassifier() {\n        return classifier;\n    }\n\n    public void setClassifier(String classifier) {\n        this.classifier = classifier;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public String getScope() {\n        return scope;\n    }\n\n    public void setScope(String scope) {\n        this.scope = scope;\n    }\n\n    public String toString() {\n        if (this.classifier == null) {\n            return String.format(\"%s%s%s%s%s%s%s\", groupId, GAV_SPLIT, artifactId, GAV_SPLIT,\n                version, GAV_SPLIT, type);\n        } else {\n            return String.format(\"%s%s%s%s%s%s%s%s%s\", groupId, GAV_SPLIT, artifactId, GAV_SPLIT,\n                classifier, GAV_SPLIT, version, GAV_SPLIT, type);\n        }\n    }\n\n    public boolean isSameIgnoreVersion(ArtifactItem that) {\n        if (that == null) {\n            return false;\n        }\n\n        return isSameStr(this.getGroupId(), that.getGroupId())\n               && isSameStr(this.getArtifactId(), that.getArtifactId())\n               && isSameStr(this.getClassifier(), that.getClassifier());\n    }\n\n    public boolean isSameWithVersion(ArtifactItem that) {\n        if (that == null) {\n            return false;\n        }\n\n        return isSameStr(this.getGroupId(), that.getGroupId())\n               && isSameStr(this.getArtifactId(), that.getArtifactId())\n               && (StringUtils.equals(this.getVersion(), DEFAULT_VERSION) || isSameStr(\n                   this.getVersion(), that.getVersion()))\n               && isSameStr(this.getClassifier(), that.getClassifier());\n    }\n\n    protected boolean isSameStr(String left, String right) {\n        if (\"*\".equals(left) || \"*\".equals(right)) {\n            return true;\n        }\n        return StringUtils.equals(left, right);\n    }\n\n    /**\n     * parse string pattern {groupId:artifactId} or {groupId:artifactId:classifier}\n     * @param s location pattern\n     * @return\n     */\n    public static ArtifactItem parseArtifactItemIgnoreVersion(String s) {\n        String[] arr = new String[] {};\n\n        if (s != null && !s.isEmpty()) {\n            arr = s.split(GAV_SPLIT);\n        }\n\n        // groupId, artifactId and classifier(optional)\n        AssertUtils.isTrue(arr != null && arr.length >= 2 && arr.length <= 3,\n            \"artifact item format error: %s\", s);\n\n        ArtifactItem item = new ArtifactItem();\n        item.setGroupId(arr[0]);\n        item.setArtifactId(arr[1]);\n        if (arr.length == 3) {\n            item.setClassifier(arr[2]);\n        }\n        return item;\n    }\n\n    /**\n     * parse string pattern {groupId:artifactId:version} or {groupId:artifactId:version:classifier}\n     * @param s location pattern\n     * @return\n     */\n    public static ArtifactItem parseArtifactItemWithVersion(String s) {\n        String[] arr = new String[] {};\n\n        if (s != null && !s.isEmpty()) {\n            arr = s.split(GAV_SPLIT);\n        }\n\n        // groupId, artifactId, version and classifier(optional)\n        AssertUtils.isTrue(arr != null && arr.length >= 3 && arr.length <= 4,\n            \"artifact item format error: %s\", s);\n\n        ArtifactItem item = new ArtifactItem();\n        item.setGroupId(arr[0]);\n        item.setArtifactId(arr[1]);\n        item.setVersion(arr[2]);\n        if (arr.length == 4) {\n            item.setClassifier(arr[3]);\n        }\n        return item;\n    }\n\n    /**\n     * parse string pattern {groupId:artifactId} {groupId:artifactId:version} or {groupId:artifactId:version:classifier}\n     * @param s location pattern\n     * @return\n     */\n    public static ArtifactItem parseArtifactItem(String s) {\n        String[] arr = StringUtils.split(s, GAV_SPLIT);\n\n        // groupId, artifactId, version(optional) and classifier(optional)\n        AssertUtils.isTrue(arr != null && arr.length >= 2 && arr.length <= 4,\n            \"artifact item format error: %s\", s);\n\n        ArtifactItem item = new ArtifactItem();\n        item.setGroupId(arr[0]);\n        item.setArtifactId(arr[1]);\n        if (arr.length >= 3) {\n            item.setVersion(arr[2]);\n        }\n        if (arr.length == 4) {\n            item.setClassifier(arr[3]);\n        }\n        return item;\n    }\n\n    public static ArtifactItem parseArtifactItem(Artifact artifact) {\n        ArtifactItem artifactItem = new ArtifactItem();\n        artifactItem.setGroupId(artifact.getGroupId());\n        artifactItem.setArtifactId(artifact.getArtifactId());\n        artifactItem.setClassifier(artifact.getClassifier());\n        artifactItem.setVersion(artifact.getVersion());\n        artifactItem.setType(artifact.getType());\n        artifactItem.setScope(artifact.getScope());\n        return artifactItem;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) {\n            return true;\n        }\n        if (o == null || getClass() != o.getClass()) {\n            return false;\n        }\n\n        ArtifactItem artifactItem = (ArtifactItem) o;\n        return Objects.equals(this.groupId, artifactItem.getGroupId())\n               && Objects.equals(this.artifactId, artifactItem.getArtifactId())\n               && Objects.equals(this.type, artifactItem.getType())\n               && Objects.equals(this.version, artifactItem.getVersion())\n               && Objects.equals(this.classifier, artifactItem.getClassifier())\n               && Objects.equals(this.scope, artifactItem.getScope());\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects\n            .hash(this.groupId, this.artifactId, this.type, this.version, this.classifier);\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/src/main/java/com/alipay/sofa/ark/tools/JarWriter.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.tools;\n\nimport com.alipay.sofa.ark.common.util.FileUtils;\nimport com.alipay.sofa.ark.spi.constant.Constants;\n\nimport java.io.*;\nimport java.util.Arrays;\nimport java.util.Enumeration;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.jar.*;\nimport java.util.zip.CRC32;\nimport java.util.zip.ZipEntry;\n\n/**\n * Writes JAR content, ensuring valid directory entries are always create and duplicate\n *\n * @author Phillip Webb\n * @author Andy Wilkinson\n */\npublic class JarWriter implements LoaderClassesWriter {\n\n    private static final String   NESTED_ARCHIVE_LOADER_JAR               = \"sofa-ark-archive\";\n    private static final String   NESTED_SPI_LOADER_JAR                   = \"sofa-ark-spi\";\n    private static final String   NESTED_COMMON_LOADER_JAR                = \"sofa-ark-common\";\n    private static final String   NESTED_ARCHIVE_LOADER_CLASS_PREFIX      = \"com/alipay/sofa/ark/bootstrap\";\n    private static final String   NESTED_ARCHIVE_BOOTSTRAP_CLASS_PREFIX   = \"com/alipay/sofa/ark/loader\";\n    private static final String   NESTED_SPI_ARCHIVE_LOADER_CLASS_PREFIX  = \"com/alipay/sofa/ark/spi/archive\";\n    private static final String   NESTED_ARCHIVE_STRING_UTIL_CLASS_PREFIX = \"com/alipay/sofa/ark/common/util/StringUtils\";\n    private static final String   NESTED_ARCHIVE_ASSERT_UTIL_CLASS_PREFIX = \"com/alipay/sofa/ark/common/util/AssertUtils\";\n    private static final String   NESTED_SPI_CONSTANT_CLASS_PREFIX        = \"com/alipay/sofa/ark/spi/constant\";\n\n    private static final int      BUFFER_SIZE                             = 32 * 1024;\n\n    private final JarOutputStream jarOutput;\n\n    private final Set<String>     writtenEntries                          = new HashSet<>();\n\n    /**\n     * Create a new {@link JarWriter} instance.\n     *\n     * @param file         the file to write\n     * @throws IOException           if the file cannot be opened\n     * @throws FileNotFoundException if the file cannot be found\n     */\n    public JarWriter(File file) throws IOException {\n        FileOutputStream fileOutputStream = new FileOutputStream(file);\n        this.jarOutput = new JarOutputStream(fileOutputStream);\n    }\n\n    /**\n     * Write the specified manifest.\n     *\n     * @param manifest the manifest to write\n     * @throws IOException of the manifest cannot be written\n     */\n    public void writeManifest(final Manifest manifest) throws IOException {\n        JarEntry entry = new JarEntry(\"META-INF/MANIFEST.MF\");\n        writeEntry(entry, new EntryWriter() {\n            @Override\n            public void write(OutputStream outputStream) throws IOException {\n                manifest.write(outputStream);\n            }\n        });\n    }\n\n    public void writeMarkEntry() throws IOException {\n        String str = \"a mark file included in sofa-ark module.\";\n        writeEntry(Constants.ARK_BIZ_MARK_ENTRY,\n            new ByteArrayInputStream(str.toString().getBytes()));\n    }\n\n    /**\n     * Write all entries from the specified jar file.\n     *\n     * @param jarFile the source jar file\n     * @throws IOException if the entries cannot be written\n     */\n    public void writeEntries(JarFile jarFile) throws IOException {\n        this.writeEntries(jarFile, new IdentityEntryTransformer());\n    }\n\n    public void writeBootstrapEntry(JarFile arkContainerJar) throws IOException {\n        Enumeration<JarEntry> entries = arkContainerJar.entries();\n        while (entries.hasMoreElements()) {\n            JarEntry entry = entries.nextElement();\n            if (entry.getName().contains(NESTED_ARCHIVE_LOADER_JAR)\n                || entry.getName().contains(NESTED_SPI_LOADER_JAR)\n                || entry.getName().contains(NESTED_COMMON_LOADER_JAR)) {\n                JarInputStream inputStream = new JarInputStream(new BufferedInputStream(\n                    arkContainerJar.getInputStream(entry)));\n                writeLoaderClasses(inputStream);\n            }\n        }\n    }\n\n    public void writeEntries(JarFile jarFile, EntryTransformer entryTransformer) throws IOException {\n        Enumeration<JarEntry> entries = jarFile.entries();\n        while (entries.hasMoreElements()) {\n            JarEntry entry = entries.nextElement();\n            ZipHeaderPeekInputStream inputStream = new ZipHeaderPeekInputStream(\n                jarFile.getInputStream(entry));\n            try {\n                if (inputStream.hasZipHeader() && entry.getMethod() != ZipEntry.STORED) {\n                    new CrcAndSize(inputStream).setupStoredEntry(entry);\n                    inputStream.close();\n                    inputStream = new ZipHeaderPeekInputStream(jarFile.getInputStream(entry));\n                }\n                EntryWriter entryWriter = new InputStreamEntryWriter(inputStream, true);\n                JarEntry transformedEntry = entryTransformer.transform(entry);\n                if (transformedEntry != null) {\n                    writeEntry(transformedEntry, entryWriter);\n                }\n            } finally {\n                inputStream.close();\n            }\n        }\n    }\n\n    /**\n         * Writes an entry. The {@code inputStream} is closed once the entry has been written\n         *\n         * @param entryName   The name of the entry\n         * @param inputStream The stream from which the entry's data can be read\n         * @throws IOException if the write fails\n         */\n    @Override\n    public void writeEntry(String entryName, InputStream inputStream) throws IOException {\n        JarEntry entry = new JarEntry(entryName);\n        writeEntry(entry, new InputStreamEntryWriter(inputStream, true));\n    }\n\n    /**\n     * Write a nested library.\n     *\n     * @param destination the destination of the library\n     * @param library     the library\n     * @throws IOException if the write fails\n     */\n    public void writeNestedLibrary(String destination, Library library) throws IOException {\n        File file = library.getFile();\n        JarEntry entry = new JarEntry(destination + library.getName());\n        entry.setTime(getNestedLibraryTime(file));\n        if (library.isUnpackRequired()) {\n            entry.setComment(\"UNPACK:\" + FileUtils.sha1Hash(file));\n        }\n        new CrcAndSize(file).setupStoredEntry(entry);\n        writeEntry(entry, new InputStreamEntryWriter(new FileInputStream(file), true));\n    }\n\n    private long getNestedLibraryTime(File file) {\n        try (JarFile jarFile = new JarFile(file)) {\n            Enumeration<JarEntry> entries = jarFile.entries();\n            while (entries.hasMoreElements()) {\n                JarEntry entry = entries.nextElement();\n                if (!entry.isDirectory()) {\n                    return entry.getTime();\n                }\n            }\n        } catch (Exception ex) {\n            // Ignore and just use the source file timestamp\n        }\n        return file.lastModified();\n    }\n\n    /**\n     *\n     * @param jarInputStream the inputStream of the resource containing the loader classes\n     * to be written\n     * @throws IOException\n     */\n    @Override\n    public void writeLoaderClasses(JarInputStream jarInputStream) throws IOException {\n        JarEntry entry;\n        while ((entry = jarInputStream.getNextJarEntry()) != null) {\n            if (entry.getName().endsWith(\".class\")\n                && (entry.getName().contains(NESTED_SPI_ARCHIVE_LOADER_CLASS_PREFIX)\n                    || entry.getName().contains(NESTED_ARCHIVE_BOOTSTRAP_CLASS_PREFIX)\n                    || entry.getName().contains(NESTED_ARCHIVE_LOADER_CLASS_PREFIX)\n                    || entry.getName().contains(NESTED_ARCHIVE_STRING_UTIL_CLASS_PREFIX)\n                    || entry.getName().contains(NESTED_ARCHIVE_ASSERT_UTIL_CLASS_PREFIX) || entry\n                    .getName().contains(NESTED_SPI_CONSTANT_CLASS_PREFIX))) {\n                writeEntry(entry, new InputStreamEntryWriter(jarInputStream, false));\n            }\n        }\n        jarInputStream.close();\n    }\n\n    /**\n     * Close the writer.\n     *\n     * @throws IOException if the file cannot be closed\n     */\n    public void close() throws IOException {\n        this.jarOutput.close();\n    }\n\n    /**\n     * Perform the actual write of a {@link JarEntry}. All other {@code write} method\n     * delegate to this one.\n     *\n     * @param entry       the entry to write\n     * @param entryWriter the entry writer or {@code null} if there is no content\n     * @throws IOException in case of I/O errors\n     */\n    private void writeEntry(JarEntry entry, EntryWriter entryWriter) throws IOException {\n        String parent = entry.getName();\n        if (parent.endsWith(\"/\")) {\n            parent = parent.substring(0, parent.length() - 1);\n        }\n        if (parent.lastIndexOf(\"/\") != -1) {\n            parent = parent.substring(0, parent.lastIndexOf(\"/\") + 1);\n            if (parent.length() > 0) {\n                writeEntry(new JarEntry(parent), null);\n            }\n        }\n\n        if (this.writtenEntries.add(entry.getName())) {\n            this.jarOutput.putNextEntry(entry);\n            if (entryWriter != null) {\n                entryWriter.write(this.jarOutput);\n            }\n            this.jarOutput.closeEntry();\n        }\n    }\n\n    /**\n     * Interface used to write jar entry date.\n     */\n    private interface EntryWriter {\n\n        /**\n         * Write entry data to the specified output stream.\n         *\n         * @param outputStream the destination for the data\n         * @throws IOException in case of I/O errors\n         */\n        void write(OutputStream outputStream) throws IOException;\n\n    }\n\n    /**\n     * {@link EntryWriter} that writes content from an {@link InputStream}.\n     */\n    private static class InputStreamEntryWriter implements EntryWriter {\n\n        private final InputStream inputStream;\n\n        private final boolean     close;\n\n        InputStreamEntryWriter(InputStream inputStream, boolean close) {\n            this.inputStream = inputStream;\n            this.close = close;\n        }\n\n        @Override\n        public void write(OutputStream outputStream) throws IOException {\n            byte[] buffer = new byte[BUFFER_SIZE];\n            int bytesRead;\n            while ((bytesRead = this.inputStream.read(buffer)) != -1) {\n                outputStream.write(buffer, 0, bytesRead);\n            }\n            outputStream.flush();\n            if (this.close) {\n                this.inputStream.close();\n            }\n        }\n\n    }\n\n    /**\n     * {@link InputStream} that can peek ahead at zip header bytes.\n     */\n    static class ZipHeaderPeekInputStream extends FilterInputStream {\n\n        private static final byte[]  ZIP_HEADER = new byte[] { 0x50, 0x4b, 0x03, 0x04 };\n\n        private final byte[]         header;\n\n        private ByteArrayInputStream headerStream;\n\n        protected ZipHeaderPeekInputStream(InputStream in) throws IOException {\n            super(in);\n            this.header = new byte[4];\n            int len = in.read(this.header);\n            this.headerStream = new ByteArrayInputStream(this.header, 0, len);\n        }\n\n        @Override\n        public int read() throws IOException {\n            int read = (this.headerStream == null ? -1 : this.headerStream.read());\n            if (read != -1) {\n                this.headerStream = null;\n                return read;\n            }\n            return super.read();\n        }\n\n        @Override\n        public int read(byte[] b) throws IOException {\n            return read(b, 0, b.length);\n        }\n\n        @Override\n        public int read(byte[] b, int off, int len) throws IOException {\n            int read = (this.headerStream == null ? -1 : this.headerStream.read(b, off, len));\n            if (read != -1) {\n                this.headerStream = null;\n                return read;\n            }\n            return super.read(b, off, len);\n        }\n\n        public boolean hasZipHeader() {\n            return Arrays.equals(this.header, ZIP_HEADER);\n        }\n\n    }\n\n    /**\n     * Data holder for CRC and Size.\n     */\n    private static class CrcAndSize {\n\n        private final CRC32 crc = new CRC32();\n\n        private long        size;\n\n        CrcAndSize(File file) throws IOException {\n            FileInputStream inputStream = new FileInputStream(file);\n            try {\n                load(inputStream);\n            } finally {\n                inputStream.close();\n            }\n        }\n\n        CrcAndSize(InputStream inputStream) throws IOException {\n            load(inputStream);\n        }\n\n        private void load(InputStream inputStream) throws IOException {\n            byte[] buffer = new byte[BUFFER_SIZE];\n            int bytesRead;\n            while ((bytesRead = inputStream.read(buffer)) != -1) {\n                this.crc.update(buffer, 0, bytesRead);\n                this.size += bytesRead;\n            }\n        }\n\n        public void setupStoredEntry(JarEntry entry) {\n            entry.setSize(this.size);\n            entry.setCompressedSize(this.size);\n            entry.setCrc(this.crc.getValue());\n            entry.setMethod(ZipEntry.STORED);\n        }\n\n    }\n\n    /**\n     * An {@code EntryTransformer} enables the transformation of {@link JarEntry jar\n     * entries} during the writing process.\n     */\n    public interface EntryTransformer {\n        /**\n         * Transform {@link JarEntry}\n         *\n         * @param jarEntry\n         * @return\n         */\n        JarEntry transform(JarEntry jarEntry);\n    }\n\n    /**\n     * An {@code EntryTransformer} that returns the entry unchanged.\n     */\n    public static final class IdentityEntryTransformer implements EntryTransformer {\n\n        @Override\n        public JarEntry transform(JarEntry jarEntry) {\n            return jarEntry;\n        }\n\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/src/main/java/com/alipay/sofa/ark/tools/Layout.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.tools;\n\n/**\n * Strategy interface used to determine the layout for a particular type of archive.\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic interface Layout {\n\n    /**\n     * Returns the launcher class name for this layout.\n     * @return the launcher class  name\n     */\n    String getLauncherClassName();\n\n    /**\n     * Returns the destination path for a given library.\n     * @param libraryName the name of the library (excluding any path)\n     * @param scope the scope of the library\n     * @return the destination relative to the root of the archive (should end with '/')\n     * or {@code null} if the library should not be included.\n     */\n    String getLibraryDestination(String libraryName, LibraryScope scope);\n\n    /**\n     * Returns if loader classes should be included to make the archive executable.\n     * @return if the layout is executable\n     */\n    boolean isExecutable();\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/src/main/java/com/alipay/sofa/ark/tools/Layouts.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.tools;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class Layouts {\n\n    private Layouts() {\n    }\n\n    /**\n     * Executable JAR layout.\n     */\n    public static class Jar implements RepackagingLayout {\n\n        @Override\n        public String getArkContainerLocation() {\n            return \"SOFA-ARK/container/\";\n        }\n\n        @Override\n        public String getArkPluginLocation() {\n            return \"SOFA-ARK/plugin/\";\n        }\n\n        @Override\n        public String getArkModuleLocation() {\n            return \"SOFA-ARK/biz/\";\n        }\n\n        @Override\n        public String getLauncherClassName() {\n            return \"com.alipay.sofa.ark.bootstrap.ArkLauncher\";\n        }\n\n        @Override\n        public String getLibraryDestination(String libraryName, LibraryScope scope) {\n            if (scope.equals(LibraryScope.PLUGIN)) {\n                return getArkPluginLocation();\n            } else if (scope.equals(LibraryScope.MODULE)) {\n                return getArkModuleLocation();\n            } else if (scope.equals(LibraryScope.CONTAINER)) {\n                return getArkContainerLocation();\n            }\n            return \"\";\n        }\n\n        @Override\n        public boolean isExecutable() {\n            return true;\n        }\n\n        public static Jar jar() {\n            return new Jar();\n        }\n    }\n\n    /**\n     * Module layout (designed to be used as a \"plug-in\")\n     */\n    public static class Module implements Layout {\n\n        @Override\n        public String getLauncherClassName() {\n            return \"\";\n        }\n\n        @Override\n        public String getLibraryDestination(String libraryName, LibraryScope scope) {\n            return \"lib/\";\n        }\n\n        @Override\n        public boolean isExecutable() {\n            return false;\n        }\n\n        public static Module module() {\n            return new Module();\n        }\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/src/main/java/com/alipay/sofa/ark/tools/Libraries.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.tools;\n\nimport java.io.IOException;\n\n/**\n * Encapsulates process about libraries that may be packed into the archive.\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic interface Libraries {\n\n    /**\n     * Iterate all relevant libraries.\n     * @param callback a callback for each relevant library.\n     * @throws IOException if the operation fails\n     */\n    void doWithLibraries(LibraryCallback callback) throws IOException;\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/src/main/java/com/alipay/sofa/ark/tools/Library.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.tools;\n\nimport java.io.File;\n\n/**\n * Encapsulates information about a single library that may be packed into the archive.\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class Library {\n\n    private final String  name;\n\n    private String        artifactId;\n\n    private final File    file;\n\n    private LibraryScope  scope;\n\n    private final boolean unpackRequired;\n\n    /**\n     * Create a new {@link Library}.\n     * @param file the source file\n     * @param scope the scope of the library\n     */\n    public Library(File file, LibraryScope scope) {\n        this(file, scope, false);\n    }\n\n    /**\n     * Create a new {@link Library}.\n     * @param file the source file\n     * @param scope the scope of the library\n     * @param unpackRequired if the library needs to be unpacked before it can be used\n     */\n    public Library(File file, LibraryScope scope, boolean unpackRequired) {\n        this(null, file, scope, unpackRequired);\n    }\n\n    /**\n     * Create a new {@link Library}.\n     * @param name the name of the library as it should be written or {@code null} to use\n     * the file name\n     * @param file the source file\n     * @param scope the scope of the library\n     * @param unpackRequired if the library needs to be unpacked before it can be used\n     */\n    public Library(String name, File file, LibraryScope scope, boolean unpackRequired) {\n        this.name = (name == null ? file.getName() : name);\n        this.file = file;\n        this.scope = scope;\n        this.unpackRequired = unpackRequired;\n    }\n\n    /**\n     * Return the name of file as it should be written.\n     * @return the name\n     */\n    public String getName() {\n        return this.name;\n    }\n\n    /**\n     * return the artifact id\n     * @return the artifactId\n     */\n    public String getArtifactId() {\n        return artifactId;\n    }\n\n    /**\n     * set artifactId\n     * @param artifactId\n     */\n    public void setArtifactId(String artifactId) {\n        this.artifactId = artifactId;\n    }\n\n    /**\n     * Return the library file.\n     * @return the file\n     */\n    public File getFile() {\n        return this.file;\n    }\n\n    /**\n     * Return the scope of the library.\n     * @return the scope\n     */\n    public LibraryScope getScope() {\n        return this.scope;\n    }\n\n    public void setScope(LibraryScope scope) {\n        this.scope = scope;\n    }\n\n    /**\n     * Return if the file cannot be used directly as a nested jar and needs to be\n     * unpacked.\n     * @return if unpack is required\n     */\n    public boolean isUnpackRequired() {\n        return this.unpackRequired;\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/src/main/java/com/alipay/sofa/ark/tools/LibraryCallback.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.tools;\n\nimport java.io.File;\nimport java.io.IOException;\n\n/**\n * Callback interface used to iterate {@link Libraries}.\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic interface LibraryCallback {\n\n    /**\n     * Callback for a single library backed by a {@link File}.\n     * NOTE: library name will be appended maven groupId as prefix, if duplicated artifactId+version was found\n     *\n     * @param library the library\n     * @throws IOException if the operation fails\n     */\n    void library(Library library) throws IOException;\n\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/src/main/java/com/alipay/sofa/ark/tools/LibraryScope.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.tools;\n\n/**\n * @author qilong.zql\n * @since 0.1.0\n */\npublic interface LibraryScope {\n\n    /**\n     * The library is used at compile time and runtime\n     */\n    LibraryScope COMPILE   = new LibraryScope() {\n\n                               @Override\n                               public String toString() {\n                                   return \"compile\";\n                               }\n\n                           };\n\n    /**\n     * The library is used at runtime but not needed for compile.\n     */\n    LibraryScope RUNTIME   = new LibraryScope() {\n\n                               @Override\n                               public String toString() {\n                                   return \"runtime\";\n                               }\n\n                           };\n\n    /**\n     * The library is needed for compile but is usually provided when running.\n     */\n    LibraryScope PROVIDED  = new LibraryScope() {\n\n                               @Override\n                               public String toString() {\n                                   return \"provided\";\n                               }\n\n                           };\n\n    /**\n     * Marker for sofa-ark plugin scope when custom configuration is used.\n     */\n    LibraryScope PLUGIN    = new LibraryScope() {\n\n                               @Override\n                               public String toString() {\n                                   return \"plugin\";\n                               }\n\n                           };\n\n    /**\n     * Marker for sofa-ark module scope when custom configuration is used.\n     */\n    LibraryScope MODULE    = new LibraryScope() {\n\n                               @Override\n                               public String toString() {\n                                   return \"module\";\n                               }\n\n                           };\n\n    /**\n     * Marker for sofa-ark container scope when custom configuration is used.\n     */\n    LibraryScope CONTAINER = new LibraryScope() {\n\n                               @Override\n                               public String toString() {\n                                   return \"container\";\n                               }\n\n                           };\n\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/src/main/java/com/alipay/sofa/ark/tools/LoaderClassesWriter.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.tools;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.jar.JarInputStream;\n\n/**\n * Writer to write classes into repackaged JAR.\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic interface LoaderClassesWriter {\n\n    /**\n     * Write custom required SOFA-ark-loader classes to the JAR.\n     * @param jarInputStream the inputStream of the resource containing the loader classes\n     * to be written\n     * @throws IOException if the classes cannot be written\n     */\n    void writeLoaderClasses(JarInputStream jarInputStream) throws IOException;\n\n    /**\n     * Write a single entry to the JAR.\n     * @param name the name of the entry\n     * @param inputStream the input stream content\n     * @throws IOException if the entry cannot be written\n     */\n    void writeEntry(String name, InputStream inputStream) throws IOException;\n\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/src/main/java/com/alipay/sofa/ark/tools/MainClassFinder.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.tools;\n\nimport org.objectweb.asm.*;\n\nimport java.io.*;\nimport java.util.*;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarFile;\n\n/**\n * Finds any class with a {@code public static main} method by performing a breadth first\n * search.\n *\n * @author Phillip Webb\n * @author Andy Wilkinson\n */\npublic class MainClassFinder {\n\n    private static final String DOT_CLASS         = \".class\";\n\n    private static final Type   STRING_ARRAY_TYPE = Type.getType(String[].class);\n\n    private static final Type   MAIN_METHOD_TYPE  = Type.getMethodType(Type.VOID_TYPE,\n                                                      STRING_ARRAY_TYPE);\n\n    private static final String MAIN_METHOD_NAME  = \"main\";\n\n    /**\n     * Find a single main class in a given jar file. A main class annotated with an\n     * annotation with the given {@code annotationName} will be preferred over a main\n     * class with no such annotation.\n     * @param jarFile the jar file to search\n     * @param classesLocation the location within the jar containing classes\n     * @param annotationName the name of the annotation that may be present on the main\n     * class\n     * @return the main class or {@code null}\n     * @throws IOException if the jar file cannot be read\n     */\n    public static String findSingleMainClass(JarFile jarFile, String classesLocation,\n                                             String annotationName) throws IOException {\n        SingleMainClassCallback callback = new SingleMainClassCallback(annotationName);\n        MainClassFinder.doWithMainClasses(jarFile, classesLocation, callback);\n        return callback.getMainClassName();\n    }\n\n    /**\n     * Perform the given callback operation on all main classes from the given jar.\n     * @param <T> the result type\n     * @param jarFile the jar file to search\n     * @param classesLocation the location within the jar containing classes\n     * @param callback the callback\n     * @return the first callback result or {@code null}\n     * @throws IOException in case of I/O errors\n     */\n    static <T> T doWithMainClasses(JarFile jarFile, String classesLocation,\n                                   MainClassCallback<T> callback) throws IOException {\n        List<JarEntry> classEntries = getClassEntries(jarFile, classesLocation);\n        Collections.sort(classEntries, new ClassEntryComparator());\n        for (JarEntry entry : classEntries) {\n            InputStream inputStream = new BufferedInputStream(jarFile.getInputStream(entry));\n            try {\n                ClassDescriptor classDescriptor = createClassDescriptor(inputStream);\n                if (classDescriptor != null && classDescriptor.isMainMethodFound()) {\n                    String className = convertToClassName(entry.getName(), classesLocation);\n                    T result = callback.doWith(new MainClass(className, classDescriptor\n                        .getAnnotationNames()));\n                    if (result != null) {\n                        return result;\n                    }\n                }\n            } finally {\n                inputStream.close();\n            }\n        }\n        return null;\n    }\n\n    private static String convertToClassName(String name, String prefix) {\n        name = name.replace('/', '.');\n        name = name.replace('\\\\', '.');\n        name = name.substring(0, name.length() - DOT_CLASS.length());\n        if (prefix != null) {\n            name = name.substring(prefix.length());\n        }\n        return name;\n    }\n\n    private static List<JarEntry> getClassEntries(JarFile source, String classesLocation) {\n        classesLocation = (classesLocation != null ? classesLocation : \"\");\n        Enumeration<JarEntry> sourceEntries = source.entries();\n        List<JarEntry> classEntries = new ArrayList<>();\n        while (sourceEntries.hasMoreElements()) {\n            JarEntry entry = sourceEntries.nextElement();\n            if (entry.getName().startsWith(classesLocation) && entry.getName().endsWith(DOT_CLASS)) {\n                classEntries.add(entry);\n            }\n        }\n        return classEntries;\n    }\n\n    private static ClassDescriptor createClassDescriptor(InputStream inputStream) {\n        try {\n            ClassReader classReader = new ClassReader(inputStream);\n            ClassDescriptor classDescriptor = new ClassDescriptor();\n            classReader.accept(classDescriptor, ClassReader.SKIP_CODE);\n            return classDescriptor;\n        } catch (IOException ex) {\n            return null;\n        }\n    }\n\n    private static class ClassEntryComparator implements Comparator<JarEntry> {\n\n        @Override\n        public int compare(JarEntry o1, JarEntry o2) {\n            Integer d1 = getDepth(o1);\n            Integer d2 = getDepth(o2);\n            int depthCompare = d1.compareTo(d2);\n            if (depthCompare != 0) {\n                return depthCompare;\n            }\n            return o1.getName().compareTo(o2.getName());\n        }\n\n        private int getDepth(JarEntry entry) {\n            return entry.getName().split(\"/\").length;\n        }\n\n    }\n\n    private static class ClassDescriptor extends ClassVisitor {\n\n        private final Set<String> annotationNames = new LinkedHashSet<>();\n\n        private boolean           mainMethodFound;\n\n        ClassDescriptor() {\n            super(Opcodes.ASM7);\n        }\n\n        @Override\n        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {\n            this.annotationNames.add(Type.getType(desc).getClassName());\n            return null;\n        }\n\n        @Override\n        public MethodVisitor visitMethod(int access, String name, String desc, String signature,\n                                         String[] exceptions) {\n            if (isAccess(access, Opcodes.ACC_PUBLIC, Opcodes.ACC_STATIC)\n                && MAIN_METHOD_NAME.equals(name) && MAIN_METHOD_TYPE.getDescriptor().equals(desc)) {\n                this.mainMethodFound = true;\n            }\n            return null;\n        }\n\n        private boolean isAccess(int access, int... requiredOpsCodes) {\n            for (int requiredOpsCode : requiredOpsCodes) {\n                if ((access & requiredOpsCode) == 0) {\n                    return false;\n                }\n            }\n            return true;\n        }\n\n        boolean isMainMethodFound() {\n            return this.mainMethodFound;\n        }\n\n        Set<String> getAnnotationNames() {\n            return this.annotationNames;\n        }\n\n    }\n\n    /**\n     * Callback for handling {@link MainClass MainClasses}.\n     *\n     * @param <T> the callback's return type\n     */\n    interface MainClassCallback<T> {\n\n        /**\n         * Handle the specified main class.\n         * @param mainClass the main class\n         * @return a non-null value if processing should end or {@code null} to continue\n         */\n        T doWith(MainClass mainClass);\n\n    }\n\n    /**\n     * A class with a {@code main} method.\n     */\n    static final class MainClass {\n\n        private final String      name;\n\n        private final Set<String> annotationNames;\n\n        /**\n         * Creates a new {@code MainClass} rather represents the main class with the given\n         * {@code name}. The class is annotated with the annotations with the given\n         * {@code annotationNames}.\n         * @param name the name of the class\n         * @param annotationNames the names of the annotations on the class\n         */\n        MainClass(String name, Set<String> annotationNames) {\n            this.name = name;\n            this.annotationNames = Collections.unmodifiableSet(new HashSet<>(annotationNames));\n        }\n\n        String getName() {\n            return this.name;\n        }\n\n        Set<String> getAnnotationNames() {\n            return this.annotationNames;\n        }\n\n        @Override\n        public String toString() {\n            return this.name;\n        }\n\n        @Override\n        public int hashCode() {\n            return this.name.hashCode();\n        }\n\n        @Override\n        public boolean equals(Object obj) {\n            if (this == obj) {\n                return true;\n            }\n            if (obj == null) {\n                return false;\n            }\n            if (getClass() != obj.getClass()) {\n                return false;\n            }\n            MainClass other = (MainClass) obj;\n            return this.name.equals(other.name);\n        }\n\n    }\n\n    /**\n     * Find a single main class, throwing an {@link IllegalStateException} if multiple\n     * candidates exist.\n     */\n    private static final class SingleMainClassCallback implements MainClassCallback<Object> {\n\n        private final Set<MainClass> mainClasses = new LinkedHashSet<>();\n\n        private final String         annotationName;\n\n        private SingleMainClassCallback(String annotationName) {\n            this.annotationName = annotationName;\n        }\n\n        @Override\n        public Object doWith(MainClass mainClass) {\n            this.mainClasses.add(mainClass);\n            return null;\n        }\n\n        private String getMainClassName() {\n            Set<MainClass> matchingMainClasses = new LinkedHashSet<>();\n            if (this.annotationName != null) {\n                for (MainClass mainClass : this.mainClasses) {\n                    if (mainClass.getAnnotationNames().contains(this.annotationName)) {\n                        matchingMainClasses.add(mainClass);\n                    }\n                }\n            }\n            if (matchingMainClasses.isEmpty()) {\n                matchingMainClasses.addAll(this.mainClasses);\n            }\n            if (matchingMainClasses.size() > 1) {\n                throw new IllegalStateException(\n                    \"Unable to find a single main class from the following candidates \"\n                            + matchingMainClasses);\n            }\n            return matchingMainClasses.isEmpty() ? null : matchingMainClasses.iterator().next()\n                .getName();\n        }\n\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/src/main/java/com/alipay/sofa/ark/tools/Repackager.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.tools;\n\nimport com.alipay.sofa.ark.common.util.AssertUtils;\nimport com.alipay.sofa.ark.common.util.FileUtils;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.tools.git.GitInfo;\nimport com.alipay.sofa.ark.tools.git.JGitParser;\n\nimport java.io.*;\nimport java.text.SimpleDateFormat;\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarFile;\nimport java.util.jar.Manifest;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.*;\n\n/**\n * Utility class that can be used to repackage an archive so that it can be executed using\n * {@literal java -jar}\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic class Repackager {\n\n    private static final String                   ARK_VERSION_ATTRIBUTE              = \"Sofa-Ark-Version\";\n\n    private static final String                   ARK_CONTAINER_ROOT                 = \"Ark-Container-Root\";\n\n    private static final byte[]                   ZIP_FILE_HEADER                    = new byte[] {\n            'P', 'K', 3, 4                                                          };\n\n    private static final long                     FIND_WARNING_TIMEOUT               = TimeUnit.SECONDS\n                                                                                         .toMillis(10);\n\n    private static final String                   SPRING_BOOT_APPLICATION_CLASS_NAME = \"org.springframework.boot.autoconfigure.SpringBootApplication\";\n\n    private List<MainClassTimeoutWarningListener> mainClassTimeoutListeners          = new ArrayList<>();\n\n    private String                                mainClass;\n\n    private String                                bizName;\n\n    private String                                bizVersion;\n\n    private String                                priority;\n\n    private LinkedHashSet<String>                 denyImportPackages;\n\n    private LinkedHashSet<String>                 denyImportClasses;\n\n    private LinkedHashSet<String>                 denyImportResources;\n\n    private LinkedHashSet<ArtifactItem>           injectPluginDependencies           = new LinkedHashSet<>();\n    private LinkedHashSet<String>                 injectPluginExportPackages         = new LinkedHashSet<>();\n    private LinkedHashSet<String>                 declaredLibraries                  = new LinkedHashSet<>();\n\n    private final File                            source;\n\n    private File                                  executableFatJar;\n\n    private File                                  pluginModuleJar;\n\n    private File                                  baseDir;\n\n    private boolean                               packageProvided;\n\n    private boolean                               skipArkExecutable;\n\n    private boolean                               keepArkBizJar;\n\n    private String                                webContextPath;\n\n    private boolean                               declaredMode;\n\n    private String                                arkVersion                         = null;\n\n    private Library                               arkContainerLibrary                = null;\n\n    private final List<Library>                   standardLibraries                  = new ArrayList<>();\n\n    private final List<Library>                   arkPluginLibraries                 = new ArrayList<>();\n\n    private final List<Library>                   arkModuleLibraries                 = new ArrayList<>();\n\n    private File                                  gitDirectory;\n\n    private GitInfo                               gitInfo;\n\n    public Repackager(File source) {\n        if (source == null) {\n            throw new IllegalArgumentException(\"Source file must be provided\");\n        }\n        if (!source.exists() || !source.isFile()) {\n            throw new IllegalArgumentException(\"Source must refer to an existing file, \" + \"got\"\n                                               + source.getAbsolutePath());\n        }\n        this.source = source.getAbsoluteFile();\n    }\n\n    /**\n     * Add a listener that will be triggered to display a warning if searching for the\n     * main class takes too long.\n     * @param listener the listener to add\n     */\n    public void addMainClassTimeoutWarningListener(MainClassTimeoutWarningListener listener) {\n        this.mainClassTimeoutListeners.add(listener);\n    }\n\n    /**\n     * Set the main class that should be run. If not specified the value from the\n     * MANIFEST will be used, or if no manifest entry is found the archive will be\n     * searched for a suitable class.\n     * @param mainClass\n     */\n    public void setMainClass(String mainClass) {\n        this.mainClass = mainClass;\n    }\n\n    /**\n     * Set the ark-biz name that represents a unique id during runtime, it can be\n     * bind uniquely to a ark-biz\n     *\n     * @param bizName\n     */\n    public void setBizName(String bizName) {\n        this.bizName = bizName;\n    }\n\n    public void setBizVersion(String bizVersion) {\n        this.bizVersion = bizVersion;\n    }\n\n    public void setArkVersion(String arkVersion) {\n        this.arkVersion = arkVersion;\n    }\n\n    public void setPriority(String priority) {\n        this.priority = priority;\n    }\n\n    public void setDenyImportPackages(LinkedHashSet<String> denyImportPackages) {\n        this.denyImportPackages = denyImportPackages;\n    }\n\n    public void setDenyImportClasses(LinkedHashSet<String> denyImportClasses) {\n        this.denyImportClasses = denyImportClasses;\n    }\n\n    public void setDenyImportResources(LinkedHashSet<String> denyImportResources) {\n        this.denyImportResources = denyImportResources;\n    }\n\n    public void setInjectPluginExportPackages(LinkedHashSet<String> injectPluginExportPackages) {\n        this.injectPluginExportPackages = injectPluginExportPackages;\n    }\n\n    public void setInjectPluginDependencies(LinkedHashSet<String> injectPluginDependencies) {\n        if (injectPluginDependencies == null) {\n            return;\n        }\n\n        for (String artifact : injectPluginDependencies) {\n            ArtifactItem item = new ArtifactItem();\n            String[] artifactWithVersion = artifact.split(STRING_COLON);\n            AssertUtils.isFalse(artifactWithVersion.length != 2,\n                \"injectPluginDependencies item must be follow format by name:version, current is:\"\n                        + artifact);\n            item.setArtifactId(artifactWithVersion[0]);\n            item.setVersion(artifactWithVersion[1]);\n            this.injectPluginDependencies.add(item);\n        }\n    }\n\n    public void setGitDirectory(File gitDirectory) {\n        this.gitDirectory = gitDirectory;\n    }\n\n    public void prepareDeclaredLibraries(Collection<ArtifactItem> artifactItems) {\n        if (!this.declaredMode) {\n            return;\n        }\n        if (artifactItems == null) {\n            return;\n        }\n        for (ArtifactItem artifactItem : artifactItems) {\n            if (artifactItem != null && artifactItem.getArtifactId() != null) {\n                declaredLibraries.add(artifactItem.getArtifactId());\n            }\n        }\n    }\n\n    /**\n     * Repackage to the given destination so that it can be launched using '\n     * {@literal java -jar}'.\n     *\n     * @param appDestination the executable fat jar's destination\n     * @param moduleDestination the 'plug-in' module jar's destination\n     * @param libraries the libraries required to run the archive\n     * @throws IOException if the file cannot be repackaged\n     */\n    public void repackage(File appDestination, File moduleDestination, Libraries libraries)\n                                                                                           throws IOException {\n\n        if (appDestination == null || appDestination.isDirectory() || moduleDestination == null\n            || moduleDestination.isDirectory()) {\n            throw new IllegalArgumentException(\"Invalid destination\");\n        }\n        if (libraries == null) {\n            throw new IllegalArgumentException(\"Libraries must not be null\");\n        }\n        if (alreadyRepackaged()) {\n            return;\n        }\n\n        executableFatJar = appDestination;\n        pluginModuleJar = moduleDestination;\n\n        libraries.doWithLibraries(new LibraryCallback() {\n            @Override\n            public void library(Library library) throws IOException {\n                if (LibraryScope.PROVIDED.equals(library.getScope()) && !isPackageProvided()) {\n                    return;\n                }\n\n                if (!isZip(library.getFile())) {\n                    return;\n                }\n\n                try (JarFile jarFile = new JarFile(library.getFile())) {\n                    if (isArkContainer(jarFile)) {\n                        if (arkContainerLibrary != null) {\n                            throw new RuntimeException(\"duplicate SOFAArk Container dependency\");\n                        }\n                        library.setScope(LibraryScope.CONTAINER);\n                        arkContainerLibrary = library;\n                    } else if (isArkModule(jarFile)) {\n                        library.setScope(LibraryScope.MODULE);\n                        arkModuleLibraries.add(library);\n                    } else if (isArkPlugin(jarFile)) {\n                        library.setScope(LibraryScope.PLUGIN);\n                        arkPluginLibraries.add(library);\n                    } else {\n                        standardLibraries.add(library);\n                    }\n                }\n            }\n        });\n\n        // 构建信息\n        gitInfo = JGitParser.parse(gitDirectory);\n\n        repackageModule();\n        repackageApp();\n        removeArkBizJar();\n    }\n\n    private void repackageModule() throws IOException {\n        File destination = pluginModuleJar.getAbsoluteFile();\n        destination.delete();\n\n        JarFile jarFileSource = new JarFile(source);\n        JarWriter writer = new JarWriter(destination);\n        Manifest manifest = buildModuleManifest(jarFileSource);\n\n        try {\n            writer.writeManifest(manifest);\n            writeConfDir(new File(baseDir, Constants.CONF_BASE_DIR), writer);\n            writer.writeEntries(jarFileSource);\n            writer.writeMarkEntry();\n            writeNestedLibraries(standardLibraries, Layouts.Module.module(), writer);\n        } finally {\n            jarFileSource.close();\n            try {\n                writer.close();\n            } catch (Exception ex) {\n                // Ignore\n            }\n        }\n    }\n\n    private List<Library> getModuleLibraries() {\n        List<Library> libraries = new ArrayList<>(arkModuleLibraries);\n        Library moduleLibrary = new Library(pluginModuleJar.getAbsoluteFile(), LibraryScope.MODULE);\n        libraries.add(moduleLibrary);\n        return libraries;\n    }\n\n    private void repackageApp() throws IOException {\n        if (skipArkExecutable) {\n            return;\n        }\n        File destination = executableFatJar.getAbsoluteFile();\n        destination.delete();\n\n        JarFile jarFileSource = new JarFile(arkContainerLibrary.getFile().getAbsoluteFile());\n        JarWriter writer = new JarWriter(destination);\n        Manifest manifest = buildAppManifest(new JarFile(pluginModuleJar));\n\n        try {\n            writer.writeManifest(manifest);\n            writeConfDir(new File(baseDir, Constants.CONF_BASE_DIR), writer);\n            writer.writeBootstrapEntry(jarFileSource);\n            writeNestedLibraries(Collections.singletonList(arkContainerLibrary), Layouts.Jar.jar(),\n                writer);\n            writeNestedLibraries(arkPluginLibraries, Layouts.Jar.jar(), writer);\n            writeNestedLibraries(getModuleLibraries(), Layouts.Jar.jar(), writer);\n        } finally {\n            jarFileSource.close();\n            try {\n                writer.close();\n            } catch (Exception ex) {\n                // Ignore\n            }\n        }\n    }\n\n    private void removeArkBizJar() {\n        if (!keepArkBizJar) {\n            pluginModuleJar.getAbsoluteFile().deleteOnExit();\n        }\n    }\n\n    private void writeConfDir(File confDir, JarWriter jarWriter) throws IOException {\n        if (!confDir.exists()) {\n            return;\n        }\n\n        for (File subFile : confDir.listFiles()) {\n            if (subFile.isDirectory()) {\n                writeConfDir(subFile, jarWriter);\n            } else {\n                String entryName = subFile.getPath().substring(baseDir.getPath().length());\n                if (entryName.startsWith(File.separator)) {\n                    entryName = entryName.substring(1);\n                }\n                jarWriter.writeEntry(FileUtils.getCompatiblePath(entryName), new FileInputStream(\n                    subFile));\n            }\n        }\n    }\n\n    private void writeNestedLibraries(List<Library> libraries, Layout layout, JarWriter writer)\n                                                                                               throws IOException {\n        Set<String> alreadySeen = new HashSet<>();\n        for (Library library : libraries) {\n            String destination = layout\n                .getLibraryDestination(library.getName(), library.getScope());\n            if (destination != null) {\n                if (!alreadySeen.add(destination + library.getName())) {\n                    throw new IllegalStateException(\"Duplicate library \" + library.getName());\n                }\n                boolean isWrite = false;\n                for (ArtifactItem item : injectPluginDependencies) {\n                    if (library.getName().equals(\n                        item.getArtifactId() + \"-\" + item.getVersion() + \".jar\")) {\n                        writer.writeNestedLibrary(destination + \"export/\", library);\n                        isWrite = true;\n                        break;\n                    }\n                }\n                if (!isWrite) {\n                    writer.writeNestedLibrary(destination, library);\n                }\n            }\n        }\n    }\n\n    @SuppressWarnings(\"BooleanMethodIsAlwaysInverted\")\n    public static boolean isZip(File file) {\n        try {\n            FileInputStream fileInputStream = new FileInputStream(file);\n            try {\n                return isZip(fileInputStream);\n            } finally {\n                fileInputStream.close();\n            }\n        } catch (IOException ex) {\n            return false;\n        }\n    }\n\n    private boolean isArkContainer(JarFile jarFile) {\n        return jarFile.getEntry(Constants.ARK_CONTAINER_MARK_ENTRY) != null;\n    }\n\n    private boolean isArkPlugin(JarFile jarFile) {\n        return jarFile.getEntry(Constants.ARK_PLUGIN_MARK_ENTRY) != null;\n    }\n\n    private boolean isArkModule(JarFile jarFile) {\n        return jarFile.getEntry(Constants.ARK_BIZ_MARK_ENTRY) != null;\n    }\n\n    public static boolean isZip(InputStream inputStream) throws IOException {\n        for (int i = 0; i < ZIP_FILE_HEADER.length; i++) {\n            if (inputStream.read() != ZIP_FILE_HEADER[i]) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    public Manifest buildModuleManifest(JarFile source) throws IOException {\n        Manifest manifest = source.getManifest();\n        if (manifest == null) {\n            manifest = new Manifest();\n            manifest.getMainAttributes().putValue(\"Manifest-Version\", \"1.0\");\n        }\n        manifest = new Manifest(manifest);\n        String startClass = this.mainClass;\n        if (startClass == null) {\n            startClass = manifest.getMainAttributes().getValue(MAIN_CLASS_ATTRIBUTE);\n        }\n\n        if (startClass == null) {\n            startClass = findMainMethodWithTimeoutWarning(source);\n        }\n\n        if (startClass == null) {\n            throw new IllegalStateException(\"Unable to find main class.\");\n        }\n\n        manifest.getMainAttributes().putValue(MAIN_CLASS_ATTRIBUTE, startClass);\n        manifest.getMainAttributes().putValue(START_CLASS_ATTRIBUTE, startClass);\n        manifest.getMainAttributes().putValue(ARK_BIZ_NAME, this.bizName);\n        manifest.getMainAttributes().putValue(ARK_BIZ_VERSION, this.bizVersion);\n        manifest.getMainAttributes().putValue(PRIORITY_ATTRIBUTE, priority);\n        manifest.getMainAttributes().putValue(WEB_CONTEXT_PATH, webContextPath);\n        manifest.getMainAttributes().putValue(DENY_IMPORT_PACKAGES,\n            StringUtils.setToStr(denyImportPackages, MANIFEST_VALUE_SPLIT));\n        manifest.getMainAttributes().putValue(DENY_IMPORT_CLASSES,\n            StringUtils.setToStr(denyImportClasses, MANIFEST_VALUE_SPLIT));\n        manifest.getMainAttributes().putValue(DENY_IMPORT_RESOURCES,\n            StringUtils.setToStr(denyImportResources, MANIFEST_VALUE_SPLIT));\n        manifest.getMainAttributes().putValue(INJECT_PLUGIN_DEPENDENCIES,\n            setToStr(injectPluginDependencies, MANIFEST_VALUE_SPLIT));\n        manifest.getMainAttributes().putValue(INJECT_EXPORT_PACKAGES,\n            StringUtils.setToStr(injectPluginExportPackages, MANIFEST_VALUE_SPLIT));\n        manifest.getMainAttributes().putValue(DECLARED_LIBRARIES,\n            StringUtils.setToStr(declaredLibraries, MANIFEST_VALUE_SPLIT));\n\n        return appendBuildInfo(manifest);\n    }\n\n    public static String setToStr(Set<ArtifactItem> artifactItemSet, String delimiter) {\n        if (artifactItemSet == null || artifactItemSet.isEmpty()) {\n            return \"\";\n        }\n        AssertUtils.assertNotNull(delimiter, \"Delimiter should not be null.\");\n        StringBuilder sb = new StringBuilder();\n        for (ArtifactItem item : artifactItemSet) {\n            sb.append(item.getArtifactId()).append(\"-\").append(item.getVersion()).append(delimiter);\n        }\n        return sb.substring(0, sb.length() - delimiter.length());\n    }\n\n    private Manifest buildAppManifest(JarFile source) throws IOException {\n        Manifest manifest = source.getManifest();\n        /* theoretically impossible */\n        if (manifest == null) {\n            manifest = new Manifest();\n            manifest.getMainAttributes().putValue(\"Manifest-Version\", \"1.0\");\n        }\n        manifest = new Manifest(manifest);\n        manifest.getMainAttributes().putValue(MAIN_CLASS_ATTRIBUTE,\n            Layouts.Jar.jar().getLauncherClassName());\n        manifest.getMainAttributes().putValue(START_CLASS_ATTRIBUTE,\n            Layouts.Jar.jar().getLauncherClassName());\n\n        if (arkVersion == null || arkVersion.isEmpty()) {\n            throw new IllegalStateException(\"must specify version of SOFAArk.\");\n        }\n\n        manifest.getMainAttributes().putValue(ARK_VERSION_ATTRIBUTE, arkVersion);\n        manifest.getMainAttributes().putValue(ARK_CONTAINER_ROOT,\n            Layouts.Jar.jar().getArkContainerLocation());\n\n        return appendBuildInfo(manifest);\n    }\n\n    private Manifest appendBuildInfo(Manifest manifest) {\n        manifest.getMainAttributes().putValue(BUILD_TIME,\n            new SimpleDateFormat(DATE_FORMAT).format(new Date()));\n\n        if (gitInfo != null) {\n            manifest.getMainAttributes().putValue(REMOTE_ORIGIN_URL, gitInfo.getRepository());\n            manifest.getMainAttributes().putValue(BRANCH, gitInfo.getBranchName());\n            manifest.getMainAttributes().putValue(COMMIT_ID, gitInfo.getLastCommitId());\n            manifest.getMainAttributes().putValue(COMMIT_AUTHOR_NAME, gitInfo.getLastCommitUser());\n            manifest.getMainAttributes()\n                .putValue(COMMIT_AUTHOR_EMAIL, gitInfo.getLastCommitEmail());\n            manifest.getMainAttributes().putValue(COMMIT_TIME, gitInfo.getLastCommitDateTime());\n            manifest.getMainAttributes().putValue(COMMIT_TIMESTAMP,\n                String.valueOf(gitInfo.getLastCommitTime()));\n            manifest.getMainAttributes().putValue(BUILD_USER, gitInfo.getBuildUser());\n            manifest.getMainAttributes().putValue(BUILD_EMAIL, gitInfo.getBuildEmail());\n        }\n\n        return manifest;\n    }\n\n    public String findMainMethodWithTimeoutWarning(JarFile source) throws IOException {\n        long startTime = System.currentTimeMillis();\n        String mainMethod = findMainMethod(source);\n        long duration = System.currentTimeMillis() - startTime;\n        if (duration > FIND_WARNING_TIMEOUT) {\n            for (MainClassTimeoutWarningListener listener : this.mainClassTimeoutListeners) {\n                listener.handleTimeoutWarning(duration, mainMethod);\n            }\n        }\n        return mainMethod;\n    }\n\n    private String findMainMethod(JarFile source) throws IOException {\n        return MainClassFinder\n            .findSingleMainClass(source, null, SPRING_BOOT_APPLICATION_CLASS_NAME);\n    }\n\n    private boolean alreadyRepackaged() throws IOException {\n        try (JarFile jarFile = new JarFile(this.source)) {\n            Manifest manifest = jarFile.getManifest();\n            return (manifest != null && manifest.getMainAttributes()\n                .getValue(ARK_VERSION_ATTRIBUTE) != null);\n        }\n    }\n\n    public final File getModuleTargetFile() {\n        return pluginModuleJar;\n    }\n\n    /**\n     * Callback interface used to present a warning when finding the main class takes too\n     * long.\n     */\n    public interface MainClassTimeoutWarningListener {\n\n        /**\n         * Handle a timeout warning.\n         * @param duration the amount of time it took to find the main method\n         * @param mainMethod the main method that was actually found\n         */\n        void handleTimeoutWarning(long duration, String mainMethod);\n\n    }\n\n    /**\n     * An {@code EntryTransformer} that renames entries by applying a prefix.\n     */\n    static final class RenamingEntryTransformer implements JarWriter.EntryTransformer {\n\n        private final String namePrefix;\n\n        RenamingEntryTransformer(String namePrefix) {\n            this.namePrefix = namePrefix;\n        }\n\n        @Override\n        public JarEntry transform(JarEntry entry) {\n            JarEntry renamedEntry = new JarEntry(this.namePrefix + entry.getName());\n            renamedEntry.setTime(entry.getTime());\n            renamedEntry.setSize(entry.getSize());\n            renamedEntry.setMethod(entry.getMethod());\n            if (entry.getComment() != null) {\n                renamedEntry.setComment(entry.getComment());\n            }\n            renamedEntry.setCompressedSize(entry.getCompressedSize());\n            renamedEntry.setCrc(entry.getCrc());\n            //setCreationTimeIfPossible(entry, renamedEntry);\n            if (entry.getExtra() != null) {\n                renamedEntry.setExtra(entry.getExtra());\n            }\n            return renamedEntry;\n        }\n\n    }\n\n    public boolean isPackageProvided() {\n        return packageProvided;\n    }\n\n    public void setPackageProvided(boolean packageProvided) {\n        this.packageProvided = packageProvided;\n    }\n\n    public void setSkipArkExecutable(boolean skipArkExecutable) {\n        this.skipArkExecutable = skipArkExecutable;\n    }\n\n    public void setKeepArkBizJar(boolean keepArkBizJar) {\n        this.keepArkBizJar = keepArkBizJar;\n    }\n\n    public void setBaseDir(File baseDir) {\n        this.baseDir = baseDir;\n    }\n\n    public void setWebContextPath(String webContextPath) {\n        this.webContextPath = webContextPath;\n    }\n\n    public void setDeclaredMode(boolean declaredMode) {\n        this.declaredMode = declaredMode;\n    }\n\n    public boolean isDeclaredMode() {\n        return declaredMode;\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/src/main/java/com/alipay/sofa/ark/tools/RepackagingLayout.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.tools;\n\n/**\n *\n * @author qilong.zql\n * @since 0.1.0\n */\npublic interface RepackagingLayout extends Layout {\n\n    /**\n     * Returns the location to which SOFAArk should be moved.\n     * @return the repackaged SOFAArk location\n     */\n    String getArkContainerLocation();\n\n    /**\n     * Returns the location to which SOFAArk plugin should be moved.\n     * @return the repackaged SOFAArk plugin location\n     */\n    String getArkPluginLocation();\n\n    /**\n     * Returns the location to which SOFA module should be moved\n     * @return the repackaged SOFAArk module location\n     */\n    String getArkModuleLocation();\n\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/src/main/java/com/alipay/sofa/ark/tools/git/GitInfo.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.tools.git;\n\npublic class GitInfo {\n\n    private String buildUser;\n    private String buildEmail;\n    private String lastCommitId;\n    private long   lastCommitTime;\n    private String lastCommitDateTime;\n    private String lastCommitUser;\n    private String lastCommitEmail;\n    private String branchName;\n    private String repository;\n\n    public String getBuildUser() {\n        return buildUser;\n    }\n\n    public void setBuildUser(String buildUser) {\n        this.buildUser = buildUser;\n    }\n\n    public String getBuildEmail() {\n        return buildEmail;\n    }\n\n    public void setBuildEmail(String buildEmail) {\n        this.buildEmail = buildEmail;\n    }\n\n    public String getLastCommitId() {\n        return lastCommitId;\n    }\n\n    public void setLastCommitId(String lastCommitId) {\n        this.lastCommitId = lastCommitId;\n    }\n\n    public long getLastCommitTime() {\n        return lastCommitTime;\n    }\n\n    public void setLastCommitTime(long lastCommitTime) {\n        this.lastCommitTime = lastCommitTime;\n    }\n\n    public String getLastCommitUser() {\n        return lastCommitUser;\n    }\n\n    public void setLastCommitUser(String lastCommitUser) {\n        this.lastCommitUser = lastCommitUser;\n    }\n\n    public String getLastCommitEmail() {\n        return lastCommitEmail;\n    }\n\n    public void setLastCommitEmail(String lastCommitEmail) {\n        this.lastCommitEmail = lastCommitEmail;\n    }\n\n    public String getBranchName() {\n        return branchName;\n    }\n\n    public void setBranchName(String branchName) {\n        this.branchName = branchName;\n    }\n\n    public String getRepository() {\n        return repository;\n    }\n\n    public void setRepository(String repository) {\n        this.repository = repository;\n    }\n\n    public String getLastCommitDateTime() {\n        return lastCommitDateTime;\n    }\n\n    public void setLastCommitDateTime(String lastCommitDateTime) {\n        this.lastCommitDateTime = lastCommitDateTime;\n    }\n\n    @Override\n    public String toString() {\n        return \"GitInfo{\" + \"buildUser='\" + buildUser + '\\'' + \", buildEmail='\" + buildEmail + '\\''\n               + \", lastCommitId='\" + lastCommitId + '\\'' + \", lastCommitTime=\" + lastCommitTime\n               + \", lastCommitDateTime='\" + lastCommitDateTime + '\\'' + \", lastCommitUser='\"\n               + lastCommitUser + '\\'' + \", lastCommitEmail='\" + lastCommitEmail + '\\''\n               + \", branchName='\" + branchName + '\\'' + \", repository='\" + repository + '\\'' + '}';\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/src/main/java/com/alipay/sofa/ark/tools/git/JGitParser.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.tools.git;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.eclipse.jgit.api.Git;\nimport org.eclipse.jgit.api.ListBranchCommand;\nimport org.eclipse.jgit.api.errors.GitAPIException;\nimport org.eclipse.jgit.internal.storage.file.FileRepository;\nimport org.eclipse.jgit.lib.ObjectId;\nimport org.eclipse.jgit.lib.Ref;\nimport org.eclipse.jgit.lib.Repository;\nimport org.eclipse.jgit.revwalk.RevCommit;\nimport org.eclipse.jgit.revwalk.RevWalk;\nimport org.eclipse.jgit.storage.file.FileBasedConfig;\nimport org.eclipse.jgit.storage.file.FileRepositoryBuilder;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.text.SimpleDateFormat;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.DATE_FORMAT;\nimport static org.eclipse.jgit.lib.Constants.MASTER;\n\npublic class JGitParser {\n\n    public static GitInfo parse(File gitDirectory) {\n        try (FileRepository repository = getGitRepository(gitDirectory)) {\n\n            GitInfo gitInfo = new GitInfo();\n\n            FileBasedConfig config = repository.getConfig();\n            String remoteUrl = config.getString(\"remote\", \"origin\", \"url\");\n            String userName = config.getString(\"user\", null, \"name\");\n            String userEmail = config.getString(\"user\", null, \"email\");\n            String branchName = repository.getBranch();\n            gitInfo.setRepository(remoteUrl);\n            gitInfo.setBuildUser(userName);\n            gitInfo.setBuildEmail(userEmail);\n            gitInfo.setBranchName(branchName);\n\n            RevCommit lastCommit = getLastCommit(repository);\n            if (lastCommit != null) {\n                String lastCommitId = lastCommit.getId().getName();\n                long lastCommitTime = lastCommit.getCommitterIdent().getWhen().getTime();\n                String lastCommitUser = lastCommit.getAuthorIdent().getName();\n                String lastCommitEmail = lastCommit.getAuthorIdent().getEmailAddress();\n                String commitDateTime = new SimpleDateFormat(DATE_FORMAT).format(lastCommitTime);\n\n                gitInfo.setLastCommitId(lastCommitId);\n                gitInfo.setLastCommitUser(lastCommitUser);\n                gitInfo.setLastCommitEmail(lastCommitEmail);\n                gitInfo.setLastCommitTime(lastCommitTime);\n                gitInfo.setLastCommitDateTime(commitDateTime);\n\n                if (lastCommitId.equals(branchName)) {\n                    gitInfo.setBranchName(StringUtils.join(\n                            getBranchesFromCommit(repository, lastCommitId), \",\"));\n                }\n            }\n\n            return gitInfo;\n        } catch (Exception exception) {\n            return null;\n        }\n    }\n\n    static List<String> getBranchesFromCommit(FileRepository repository, String lastCommitId) throws GitAPIException {\n\n        List<Ref> refs = Git.wrap(repository).branchList()\n                .setListMode(ListBranchCommand.ListMode.REMOTE)\n                .setContains(lastCommitId)\n                .call();\n        return refs.stream()\n                .filter(ref -> !ref.isSymbolic())\n                .map(Ref::getName)\n                .map(repository::shortenRemoteBranchName)\n                .filter(StringUtils::isNotBlank)\n                .distinct()\n                .sorted(MASTER_FIRST_COMPARATOR)\n                .collect(Collectors.toList());\n    }\n\n    public static final Comparator<String> MASTER_FIRST_COMPARATOR = (o1, o2) -> MASTER.equals(o1) ? -1 : 1;\n\n    private static RevCommit getLastCommit(Repository repository) throws Exception {\n        RevWalk revWalk = new RevWalk(repository);\n        Ref headCommitReference = repository.getRefDatabase().findRef(\"HEAD\");\n\n        ObjectId headObjectId;\n        if (headCommitReference != null) {\n            headObjectId = headCommitReference.getObjectId();\n        } else {\n            headObjectId = repository.resolve(\"HEAD\");\n        }\n\n        if (headObjectId == null) {\n            throw new Exception(\n                \"Could not get HEAD Ref, are you sure have commits in the gitDirectory?\");\n        }\n        RevCommit headCommit = revWalk.parseCommit(headObjectId);\n        revWalk.markStart(headCommit);\n        return headCommit;\n    }\n\n    private static FileRepository getGitRepository(File gitDirectory) throws Exception {\n        if (gitDirectory == null || !gitDirectory.exists()) {\n            throw new Exception(\"Could not create git repository. \" + gitDirectory\n                                + \" is not exists!\");\n        }\n        Repository repository;\n        try {\n            repository = new FileRepositoryBuilder().setGitDir(gitDirectory).readEnvironment() // scan environment GIT_* variables\n                .findGitDir() // scan up the file system tree\n                .build();\n        } catch (IOException e) {\n            throw new Exception(\"Could not initialize git repository...\", e);\n        }\n\n        if (repository == null) {\n            throw new Exception(\"Could not create git repository. Are you sure '\" + gitDirectory\n                                + \"' is the valid Git root for your project?\");\n        }\n\n        return (FileRepository) repository;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/src/test/java/com/alipay/sofa/ark/tools/ArtifactItemTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.tools;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * @author lianglipeng.llp@alibaba-inc.com\n * @version $Id: ArtifactItemTest.java, v 0.1 2024年08月07日 16:54 立蓬 Exp $\n */\npublic class ArtifactItemTest {\n\n    @Test\n    public void testParseArtifactItem() {\n        // case1: {groupId:artifactId}\n        ArtifactItem item = ArtifactItem.parseArtifactItem(\"com.alipay.sofa:sofa-ark-plugin\");\n        assertEquals(\"com.alipay.sofa\", item.getGroupId());\n        assertEquals(\"sofa-ark-plugin\", item.getArtifactId());\n\n        // case2: {groupId:artifactId:version}\n        item = ArtifactItem.parseArtifactItem(\"com.alipay.sofa:sofa-ark-plugin:1.0.0\");\n        assertEquals(\"com.alipay.sofa\", item.getGroupId());\n        assertEquals(\"sofa-ark-plugin\", item.getArtifactId());\n        assertEquals(\"1.0.0\", item.getVersion());\n\n        // case3: {groupId:artifactId:version:classifier}\n        item = ArtifactItem.parseArtifactItem(\"com.alipay.sofa:sofa-ark-plugin:1.0.0:jdk11\");\n        assertEquals(\"com.alipay.sofa\", item.getGroupId());\n        assertEquals(\"sofa-ark-plugin\", item.getArtifactId());\n        assertEquals(\"1.0.0\", item.getVersion());\n        assertEquals(\"jdk11\", item.getClassifier());\n    }\n\n    @Test\n    public void testIsSameWithVersion() {\n        // case1: dependencyWithoutClassifier\n        ArtifactItem dependencyWithoutClassifier = ArtifactItem\n            .parseArtifactItem(\"com.alipay.sofa:sofa-ark-plugin:1.0.0\");\n\n        ArtifactItem item = ArtifactItem.parseArtifactItem(\"com.alipay.sofa:sofa-ark-plugin\");\n        assertTrue(item.isSameWithVersion(dependencyWithoutClassifier));\n\n        item = ArtifactItem.parseArtifactItem(\"com.alipay.sofa:sofa-ark-plugin:1.0.0\");\n        assertTrue(item.isSameWithVersion(dependencyWithoutClassifier));\n\n        item = ArtifactItem.parseArtifactItem(\"com.alipay.sofa:sofa-ark-plugin:1.0.1\");\n        assertFalse(item.isSameWithVersion(dependencyWithoutClassifier));\n\n        // case2: dependencyWithClassifier\n        ArtifactItem dependencyWithClassifier = ArtifactItem\n            .parseArtifactItem(\"com.alipay.sofa:sofa-ark-plugin:1.0.0:jdk11\");\n\n        item = ArtifactItem.parseArtifactItem(\"com.alipay.sofa:sofa-ark-plugin:1.0.0:jdk11\");\n        assertTrue(item.isSameWithVersion(dependencyWithClassifier));\n\n        item = ArtifactItem.parseArtifactItem(\"com.alipay.sofa:sofa-ark-plugin:1.0.1:jdk11\");\n        assertFalse(item.isSameWithVersion(dependencyWithClassifier));\n\n        item = ArtifactItem.parseArtifactItem(\"com.alipay.sofa:sofa-ark-plugin:1.0.0:jdk12\");\n        assertFalse(item.isSameWithVersion(dependencyWithClassifier));\n\n        item = ArtifactItem.parseArtifactItem(\"com.alipay.sofa:sofa-ark-plugin:1.0.0\");\n        assertFalse(item.isSameWithVersion(dependencyWithClassifier));\n\n        item = ArtifactItem.parseArtifactItem(\"com.alipay.sofa:sofa-ark-plugin\");\n        assertFalse(item.isSameWithVersion(dependencyWithClassifier));\n    }\n\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/src/test/java/com/alipay/sofa/ark/tools/JarWriterTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.tools;\n\nimport com.alipay.sofa.ark.tools.JarWriter.ZipHeaderPeekInputStream;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.util.jar.JarFile;\nimport java.util.jar.JarInputStream;\nimport java.util.jar.Manifest;\n\nimport static com.alipay.sofa.ark.tools.LibraryScope.MODULE;\nimport static org.junit.Assert.assertEquals;\n\n/**\n * @author lylingzhen\n * @since 2.2.0\n */\npublic class JarWriterTest {\n\n    private JarWriter jarWriter;\n\n    private File      file        = new File(\"./JarWriterTest\");\n\n    private String    jarFilePath = this.getClass().getClassLoader().getResource(\"test-jar.jar\")\n                                      .getFile();\n\n    @Before\n    public void setUp() throws IOException {\n        file.createNewFile();\n        jarWriter = new JarWriter(file);\n    }\n\n    @After\n    public void tearDown() {\n        file.delete();\n    }\n\n    @Test\n    public void testWriteManifest() throws Exception {\n        Manifest manifest = new Manifest();\n        jarWriter.writeManifest(manifest);\n    }\n\n    @Test\n    public void testWriteMethods() throws IOException {\n\n        jarWriter.writeMarkEntry();\n\n        JarFile jarFile = new JarFile(jarFilePath);\n        jarWriter.writeBootstrapEntry(jarFile);\n\n        Library library = new Library(new File(jarFilePath), MODULE);\n        jarWriter.writeNestedLibrary(\"./\", library);\n        jarWriter.writeLoaderClasses(new JarInputStream(new FileInputStream(jarFilePath)));\n\n        ZipHeaderPeekInputStream zipHeaderPeekInputStream = new ZipHeaderPeekInputStream(\n            new FileInputStream(jarFilePath));\n        assertEquals(80, zipHeaderPeekInputStream.read());\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/src/test/java/com/alipay/sofa/ark/tools/LayoutsTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.tools;\n\nimport com.alipay.sofa.ark.tools.Layouts.Jar;\nimport com.alipay.sofa.ark.tools.Layouts.Module;\nimport org.junit.Test;\n\nimport static com.alipay.sofa.ark.tools.Layouts.Jar.jar;\nimport static com.alipay.sofa.ark.tools.Layouts.Module.module;\nimport static com.alipay.sofa.ark.tools.LibraryScope.*;\nimport static org.junit.Assert.assertEquals;\n\n/**\n * @author lylingzhen\n * @since 2.2.0\n */\npublic class LayoutsTest {\n\n    @Test\n    public void testLayouts() throws Exception {\n\n        Jar jar = jar();\n        assertEquals(\"SOFA-ARK/plugin/\", jar.getLibraryDestination(null, PLUGIN));\n        assertEquals(\"SOFA-ARK/biz/\", jar.getLibraryDestination(null, MODULE));\n        assertEquals(\"SOFA-ARK/container/\", jar.getLibraryDestination(null, CONTAINER));\n        assertEquals(\"\", jar.getLibraryDestination(null, PROVIDED));\n        assertEquals(\"com.alipay.sofa.ark.bootstrap.ArkLauncher\", jar.getLauncherClassName());\n        assertEquals(true, jar.isExecutable());\n        assertEquals(true, jar.isExecutable());\n\n        Module module = module();\n        assertEquals(\"\", module.getLauncherClassName());\n        assertEquals(\"lib/\", module.getLibraryDestination(null, null));\n        assertEquals(false, module.isExecutable());\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/src/test/java/com/alipay/sofa/ark/tools/MainClassFinderTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.tools;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.IOException;\nimport java.util.jar.JarFile;\n\nimport static com.alipay.sofa.ark.tools.MainClassFinder.findSingleMainClass;\nimport static org.junit.Assert.assertEquals;\n\npublic class MainClassFinderTest {\n\n    private String  jarFilePath = this.getClass().getClassLoader().getResource(\"test-jar.jar\")\n                                    .getFile();\n\n    private JarFile jarFile;\n\n    @Before\n    public void setUp() throws IOException {\n        jarFile = new JarFile(jarFilePath);\n    }\n\n    @Test\n    public void testFindSingleMainClass() throws IOException {\n        assertEquals(\"com.alipay.sofa.ark.sample.springbootdemo.SpringbootDemoApplication\",\n            findSingleMainClass(jarFile, \"\", \"\"));\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/src/test/java/com/alipay/sofa/ark/tools/RepackagerTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.tools;\n\nimport com.alipay.sofa.ark.tools.Repackager.RenamingEntryTransformer;\nimport com.alipay.sofa.ark.tools.git.GitInfo;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.lang.reflect.Field;\nimport java.net.URL;\nimport java.util.Collections;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarFile;\n\nimport static com.alipay.sofa.ark.tools.LibraryScope.*;\nimport static com.alipay.sofa.ark.tools.Repackager.isZip;\nimport static com.google.common.collect.Lists.newArrayList;\nimport static com.google.common.collect.Sets.newHashSet;\nimport static java.util.zip.ZipOutputStream.STORED;\nimport static org.junit.Assert.*;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class RepackagerTest {\n\n    private Repackager repackager;\n\n    private String     jarFilePath = this.getClass().getClassLoader().getResource(\"test-jar.jar\")\n                                       .getFile();\n\n    private File       jarFile     = new File(jarFilePath);\n\n    @Before\n    public void setUp() {\n        repackager = new Repackager(jarFile);\n    }\n\n    @Test\n    public void testZipFile() {\n        URL testJarUrl = this.getClass().getClassLoader().getResource(\"test-jar.jar\");\n        URL testPomUrl = this.getClass().getClassLoader().getResource(\"test-pom.xml\");\n        assertNotNull(testJarUrl);\n        assertNotNull(testPomUrl);\n        assertTrue(isZip(new File(testJarUrl.getFile())));\n        assertFalse(isZip(new File(testPomUrl.getFile())));\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testConstructWithNullFile() throws IllegalArgumentException {\n        new Repackager(null);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void testConstructWithFileNotExists() throws IllegalArgumentException {\n        new Repackager(new File(\"not_found\"));\n    }\n\n    @Test\n    public void testRepackage() throws Exception {\n\n        Field field = Repackager.class.getDeclaredField(\"arkContainerLibrary\");\n        field.setAccessible(true);\n        field.set(repackager, new Library(\"sofa-ark-2.0.jar\", jarFile, CONTAINER, true));\n\n        repackager.setArkVersion(\"2.0\");\n        repackager.setBaseDir(new File(this.getClass().getClassLoader().getResource(\"\").getFile()));\n\n        LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();\n        linkedHashSet.add(\"sofa-ark:2.0\");\n        repackager.setInjectPluginDependencies(linkedHashSet);\n\n        field = Repackager.class.getDeclaredField(\"gitInfo\");\n        field.setAccessible(true);\n        field.set(repackager, new GitInfo());\n\n        Library library = new Library(jarFile, PLUGIN);\n        repackager.repackage(new File(\"./target/dest\"), new File(\"./target/module\"), callback -> {\n            callback.library(library);\n        });\n\n        field = Repackager.class.getDeclaredField(\"arkModuleLibraries\");\n        field.setAccessible(true);\n        assertEquals(newArrayList(library), field.get(repackager));\n        assertEquals(MODULE, ((List<Library>) field.get(repackager)).get(0).getScope());\n        assertEquals(\"com.alipay.sofa.ark.sample.springbootdemo.SpringbootDemoApplication\",\n                repackager.findMainMethodWithTimeoutWarning(new JarFile(jarFile)));\n    }\n\n    @Test\n    public void testSetInjectPluginDependencies() throws Exception {\n\n        repackager.setInjectPluginDependencies(null);\n        LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();\n        linkedHashSet.add(\"sofa-ark:1.0\");\n        repackager.setInjectPluginDependencies(linkedHashSet);\n\n        Field field = Repackager.class.getDeclaredField(\"injectPluginDependencies\");\n        field.setAccessible(true);\n        ArtifactItem item = new ArtifactItem();\n        item.setArtifactId(\"sofa-ark\");\n        item.setVersion(\"1.0\");\n        assertEquals(newHashSet(item), field.get(repackager));\n    }\n\n    @Test\n    public void testPrepareDeclaredLibraries() throws Exception {\n\n        repackager.prepareDeclaredLibraries(null);\n        repackager.setDeclaredMode(true);\n        repackager.prepareDeclaredLibraries(null);\n\n        ArtifactItem artifactItem = new ArtifactItem();\n        artifactItem.setArtifactId(\"x\");\n        repackager.prepareDeclaredLibraries(newArrayList(artifactItem));\n\n        Field field = Repackager.class.getDeclaredField(\"declaredLibraries\");\n        field.setAccessible(true);\n        assertEquals(newHashSet(\"x\"), field.get(repackager));\n    }\n\n    @Test\n    public void testRenamingEntryTransformer() {\n        RenamingEntryTransformer renamingEntryTransformer = new RenamingEntryTransformer(\n            \"my-prefix\");\n        JarEntry jarEntry = new JarEntry(\"my-entry\");\n        jarEntry.setComment(\"xxx\");\n        jarEntry.setExtra(new byte[] {});\n        jarEntry.setSize(1);\n        jarEntry.setMethod(STORED);\n        jarEntry.setCrc(1);\n        jarEntry = renamingEntryTransformer.transform(jarEntry);\n        assertEquals(\"my-prefixmy-entry\", jarEntry.getName());\n    }\n\n    @Test\n    public void testOtherMethods() {\n        repackager.addMainClassTimeoutWarningListener(null);\n        repackager.setMainClass(null);\n        repackager.setBizName(null);\n        repackager.setBizVersion(null);\n        repackager.setPriority(null);\n        repackager.setDenyImportPackages(null);\n        repackager.setDenyImportClasses(null);\n        repackager.setDenyImportResources(null);\n        repackager.setInjectPluginExportPackages(null);\n    }\n}"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/src/test/java/com/alipay/sofa/ark/tools/git/JGitParserTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.tools.git;\n\nimport com.alipay.sofa.ark.tools.Repackager;\nimport org.eclipse.jgit.internal.storage.file.FileRepository;\nimport org.junit.Test;\n\nimport java.io.File;\n\nimport static com.alipay.sofa.ark.tools.git.JGitParser.getBranchesFromCommit;\nimport static com.alipay.sofa.ark.tools.git.JGitParser.parse;\nimport static org.junit.Assert.*;\n\npublic class JGitParserTest {\n\n    @Test\n    public void testParse() {\n\n        File gitFile = new File(\"../../../.git\");\n        GitInfo gitInfo = parse(gitFile);\n        assertNotNull(gitInfo);\n        gitInfo.getBuildUser();\n        gitInfo.getBuildEmail();\n        assertNotNull(gitInfo.getLastCommitId());\n        assertNotEquals(gitInfo.getLastCommitTime(), 0L);\n        assertNotNull(gitInfo.getLastCommitDateTime());\n        assertNotNull(gitInfo.getLastCommitUser());\n        assertNotNull(gitInfo.getLastCommitEmail());\n        assertNotNull(gitInfo.getBranchName());\n        assertNotNull(gitInfo.getRepository());\n        assertNotNull(gitInfo.toString());\n\n        Repackager repackager = new Repackager(new File(\"../../../pom.xml\"));\n        repackager.setGitDirectory(gitFile);\n    }\n\n    @Test\n    public void testGetBranchesFromCommit() {\n\n        try {\n            FileRepository fileRepository = new FileRepository(\"../../../.git\");\n            assertEquals(\"master\",\n                getBranchesFromCommit(fileRepository, \"3bb887feb99475b7d6bb40f926aa734fbe62e0f6\")\n                    .get(0));\n        } catch (Throwable e) {\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-parent/support/ark-tools/src/test/resources/test-pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>for-test</artifactId>\n    <groupId>com.alipay.sofa</groupId>\n    <version>0.0.1</version>\n\n</project>\n"
  },
  {
    "path": "sofa-ark-parent/support/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>sofa-ark-parent</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>sofa-ark-support</artifactId>\n    <name>${project.groupId}:${project.artifactId}</name>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>ark-plugin-maven-plugin</module>\n        <module>ark-maven-plugin</module>\n        <module>ark-support-starter</module>\n        <module>ark-tools</module>\n        <module>ark-springboot-integration/ark-springboot-starter</module>\n        <module>ark-springboot-integration/ark-compatible-springboot1</module>\n        <module>ark-springboot-integration/ark-compatible-springboot2</module>\n        <module>ark-springboot-integration/ark-common-springboot</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "sofa-ark-plugin/config-ark-plugin/CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Module Overview\n\n**Artifact ID**: `config-ark-plugin`\n**Package**: `com.alipay.sofa.ark.config`\n\nThis is a built-in Ark Plugin that provides dynamic configuration management for business modules using ZooKeeper or Apollo.\n\n## Purpose\n\n- Receive dynamic configuration from configuration centers\n- Control dynamic Biz install/uninstall based on configuration changes\n- Support ZooKeeper and Apollo as configuration sources\n\n## Key Classes\n\n### `ConfigBaseActivator`\nPlugin activator that initializes configuration listeners.\n\n### Configuration Listeners\n- Listen for configuration changes from ZK or Apollo\n- Parse configuration to determine Biz operations\n- Trigger install/uninstall/switch operations\n\n## Configuration Format\n\nThe plugin expects configuration in this format:\n```yaml\nbizs:\n  - name: my-biz\n    version: 1.0.0\n    url: http://example.com/my-biz-1.0.0.jar\n    operation: INSTALL\n```\n\n## Supported Operations\n\n- `INSTALL` - Install a new Biz\n- `UNINSTALL` - Uninstall a Biz\n- `SWITCH` - Switch active Biz version\n\n## Usage\n\nAdd plugin dependency:\n```xml\n<dependency>\n    <groupId>com.alipay.sofa</groupId>\n    <artifactId>config-ark-plugin</artifactId>\n    <classifier>ark-plugin</classifier>\n</dependency>\n```\n\nConfigure in `conf/ark/bootstrap.yml`:\n```yaml\nark:\n  config:\n    zk:\n      server: localhost:2181\n      path: /sofa-ark/config\n```\n\n## Dependencies\n\n- `sofa-ark-spi` - Service interfaces\n- `sofa-ark-api` - API for Biz operations\n- `curator-recipes` - ZooKeeper client\n- `apollo-client` - Apollo client (provided scope)\n\n## Used By\n\n- Applications requiring dynamic Biz management via configuration center"
  },
  {
    "path": "sofa-ark-plugin/config-ark-plugin/DEMO_SHOW.md",
    "content": "\n# 通过Apollo管理SOFAArk模块安装\n\n## 项目准备\n#### apollo-quick-start\n使用[sofa-ark-dynamic-guides](https://github.com/apolloconfig/apollo-quick-start)项目 来创建apollo本地配置中心\n#### sofa-ark-dynamic-guides\n使用[sofa-ark-dynamic-guides](https://github.com/sofastack-guides/sofa-ark-dynamic-guides)作为演示模块动态安装的目标业务项目\n\n## 配置与准备\n### sofa-ark-dynamic-guides项目配置\n\n- 修改dynamic-stock-mng中application-properties\n```\n#8080和apollo的config service服务端口冲突\nserver.port=8081  \n\n#新增apollo客户端配置\napp.id=SampleApp\napollo.meta=http://localhost:8080\n```\n\n- 参考 [SOFAArk 配置](https://www.sofastack.tech/projects/sofa-boot/sofa-ark-ark-config/)，在 dynamic-stock-mng目录下新增文件 `${baseDir}/conf/ark/bootstrap.properties`；并增加如下配置，设置使用apollo作为配置中心\n```\nsofa.ark.config.server.type=apollo\n```\n\n- 新增config-ark-plugin项目的pom.xml依赖\n\n```\n<dependency>\n  <groupId>com.alipay.sofa</groupId>\n  <artifactId>config-ark-plugin</artifactId>\n  <version>${sofa.ark.version}</version>\n</dependency>\n<dependency>\n    <groupId>com.ctrip.framework.apollo</groupId>\n    <artifactId>apollo-client</artifactId>\n    <version>2.1.0</version>\n</dependency>\n```\n- 增加dynamic-stock-mng的pom.xml依赖\n```\n <dependency>\n    <groupId>com.alipay.sofa</groupId>\n    <artifactId>config-ark-plugin</artifactId>\n</dependency>\n<dependency>\n    <groupId>com.ctrip.framework.apollo</groupId>\n    <artifactId>apollo-client</artifactId>\n</dependency>\n```\n### apollo后台启动配置\n\n- 按照文档apollo-quick-start文档启动apollo\n```\nzsk@MC267220 apollo-quick-start % ./demo.sh start\n==== starting service ====\nService logging file is ./service/apollo-service.log\nStarted [1225]\nWaiting for config service startup......\nConfig service started. You may visit http://localhost:8080 for service status now!\nWaiting for admin service startup.\nAdmin service started\n==== starting portal ====\nPortal logging file is ./portal/apollo-portal.log\nStarted [1343]\nWaiting for portal startup....\nPortal started. You can visit http://localhost:8070 now!\n```\n\n- 需要额外在Apollo后台为应用独立创建一个管理SOFAArk的Namespace\n    - Namespace的名称为sofa-ark\n- 创建key-value来管理SOFAArk 的模块加载，\n    -  key的名字必须为masterBiz， 类型为String\n    - value初始值为空\n  \n## 验证\n### 启动StockMngApplication基座\n参考 [启动宿主应用 ](https://github.com/sofastack-guides/sofa-ark-dynamic-guides#4%E6%89%93%E5%8C%85--%E5%90%AF%E5%8A%A8%E5%AE%BF%E4%B8%BB%E5%BA%94%E7%94%A8)\n### 连接 SOFAArk telnet\n```\n## 连接 SOFAArk telnet\n> telnet localhost 1234\n\n## 查看安装的模块信息\nsofa-ark>biz -a\nstock-mng:1.0.0:activated\nbiz count = 1\n```\n此时访问[http://localhost:8081/](http://localhost:8081/) 报错\n![image.png](images/err_page.png)\n\n### 通过apollo后台安装biz模块\n#### 安装dynamic-provider模块\n为上述Namespace为ark-biz 中的masterBiz设置一下值，然后进行发布。\n![image.png](images/master_biz.png)\n\n### 验证结果\n#### 检查日志\n找到logs/stock-mng/sofa-ark/config-message.log\n![image.png](images/message_log.png)\n\n可以看到apollo后台推送的指令已经被执行\n\n#### biz模块检查\n再次允许biz -a， 确认dynamic-provider模块已经安装成功\n\n```\nsofa-ark>biz -a \ndynamic-provider:2.0.0:activated\nstock-mng:1.0.0:activated\nbiz count = 2\n```\n\n#### 访问服务[http://localhost:8081/](http://localhost:8081/)\n再次刷新页面，页面现在可以正常访问。至此，整个演示结束。\n\n![image.png](images/ok_page.png)\n\n"
  },
  {
    "path": "sofa-ark-plugin/config-ark-plugin/README.md",
    "content": "\n# Apollo 配置\n在介绍 [Biz 生命周期](https://www.sofastack.tech/projects/sofa-boot/sofa-ark-ark-config/) 时，我们提到了有三种方式控制 Biz 的生命周期，并且介绍了使用客户端 API 实现 Biz 的安装、卸载、激活。在这一章节我们介绍如何使用 SOFAArk 提供的动态配置插件，通过 Apollo 下发指令，控制 Biz 的生命周期。\n\n另外，在[通过Apollo管理SOFAArk模块安装示例](DEMO_SHOW.md) 中演示了如何使用Apollo配置中心来动态管理ark模块的配置。\n\n### 引入依赖\nSOFAArk 提供了 config-ark-plugin 对接 Apollo 配置中心，用于运行时接受配置，达到控制 Biz 生命周期，引入如下依赖：\n\n```xml\n<dependency>\n    <groupId>com.alipay.sofa</groupId>\n    <artifactId>config-ark-plugin</artifactId>\n    <version>${sofa.ark.version}</version>\n</dependency>\n\n<dependency>\n    <groupId>com.ctrip.framework.apollo</groupId>\n    <artifactId>apollo-client</artifactId>\n    <version>2.1.0</version>\n</dependency>\n```\n\n### 配置\n参考 [SOFAArk 配置](https://www.sofastack.tech/projects/sofa-boot/sofa-ark-ark-config/)，在 SOFAArk 配置文件 `conf/ark/bootstrap.properties` 增加如下配置：\n\n```text\nsofa.ark.config.server.type=apollo\n```\n如果没有配置，默认使用Zookeeper作为配置中心，当然也可以通过设置sofa.ark.config.server.type=zookeeper来显式指定配置中心类型为Zookeeper\n\n### Apollo配置维度\n+ 需要额外在Apollo后台为应用独立创建一个管理SOFAArk的Namespace，Namespace的名称为sofa-ark\n+ 创建key-value来管理 SOFAArk 的模块加载， key的名字必须为masterBiz， 类型为String\n\n\n### 配置value的值\n下面介绍配置的形式，动态配置采用状态声明指令，SOFAArk 收到配置后，会根据状态描述解析出具体的指令（包括 install，uninstall, switch），指令格式如下：\n\n`bizName:bizVersion:bizState?k1=v1&k2=v2`\n\n多条指令使用 `;` 隔开，单条指令主要由 biz 名称，biz 版本，biz 预期状态及参数组成。简单记住一点，状态配置是描述指令推送之后，所有非宿主 Biz 的状态；\n\n例如当前 SOFAArk 容器部署了两个应用 A，B，版本均为 1.0，其中 A 应用为宿主应用，因为宿主应用不可卸载，因此不需要考虑宿主应用，可以简单认为当前容器的 Biz 状态声明为：\n\n> `B:1.0:Activated`\n\n如果此时你希望安装 C 应用，版本为 1.0，文件流地址为 urlC，那么推送指令应为：\n\n> `B:1.0:Activated;C:1.0:Activated?bizUrl=urlC`\n\n操作继续，如果你又希望安装安装 B 应用，版本为 2.0，文件流地址为 urlB，且希望 2.0 版本处于激活状态，那么你推送的指令应为：\n\n> `B:1.0:Deactivated;B:2.0:Actaivated?bizUrl=urlB;C:1.0:Activated`\n\n> 解释下为什么是这样配置指令，因为 SOFAArk 只允许应用一个版本处于激活状态，如果存在其他版本，则应处于非激活状态；所以当希望激活 B 应用 2.0 版本时，B 应用 1.0 版本应该声明为非激活状态。另外你可能注意到了 C 应用参数 urlC 不用声明了，原因是目前只有当安装新 Biz 时，才有可能需要配置参数 bizUrl，用于指定 biz 文件流地址，其他场景下，参数的解析没有意义。\n\n操作继续，如果你希望卸载 B 应用 2.0 版本，激活 B 应用 1.0 版本，卸载 C 应用，那么推送的指令声明为：\n\n> `B:1.0:Activated`\n\n\n从上面的操作描述看，在推送动态配置时，只需要声明期望的 Biz 状态即可，SOFAArk 会根据状态声明推断具体的执行指令，并尽可能保持服务的连续性，以上面最后一步操作为例，SOFAArk 推断的执行指令顺序如下：\n+ 执行 switch 指令，激活 B 应用 1.0 版本，钝化 B 应用 2.0 版本，保证服务连续性\n+ 执行 uninstall 指令，卸载 B 应用 2.0 版本\n+ 执行 uninstall 指令，卸载 C 应用 1.0 版本\n\n\n"
  },
  {
    "path": "sofa-ark-plugin/config-ark-plugin/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>sofa-ark-bom</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n        <relativePath>../../sofa-ark-bom</relativePath>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>config-ark-plugin</artifactId>\n    <name>${project.groupId}:${project.artifactId}</name>\n\n    <properties>\n        <curator.version>5.1.0</curator.version>\n    </properties>\n\n    <dependencies>\n        <!-- apollo client -->\n        <dependency>\n            <groupId>com.ctrip.framework.apollo</groupId>\n            <artifactId>apollo-client</artifactId>\n            <version>2.1.0</version>\n            <scope>provided</scope>\n        </dependency>\n\n        <!-- zk client -->\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-recipes</artifactId>\n            <version>${curator.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-test</artifactId>\n            <version>${curator.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-spi</artifactId>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-api</artifactId>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>log-sofa-boot-starter</artifactId>\n            <scope>provided</scope>\n            <exclusions>\n                <exclusion>\n                    <groupId>org.springframework.boot</groupId>\n                    <artifactId>spring-boot</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-common</artifactId>\n            <exclusions>\n                <exclusion>\n                    <groupId>com.google.inject</groupId>\n                    <artifactId>guice</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-core</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-inline</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>sofa-ark-plugin-maven-plugin</artifactId>\n                <version>${project.version}</version>\n                <executions>\n                    <execution>\n                        <id>default-cli</id>\n                        <goals>\n                            <goal>ark-plugin</goal>\n                        </goals>\n\n                        <configuration>\n                            <activator>com.alipay.sofa.ark.config.ConfigBaseActivator</activator>\n                            <excludes>\n                                <exclude>*:*:*</exclude>\n                                <exclude>com.alipay.sofa:config-ark-plugin</exclude>\n                            </excludes>\n                        </configuration>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <dependencies>\n                    <dependency>\n                        <groupId>org.apache.maven.surefire</groupId>\n                        <artifactId>surefire-junit47</artifactId>\n                        <version>${surefire.version}</version>\n                    </dependency>\n                </dependencies>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "sofa-ark-plugin/config-ark-plugin/src/main/java/com/alipay/sofa/ark/config/ConfigBaseActivator.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.config;\n\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.common.log.ArkLogger;\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.common.util.EnvironmentUtils;\nimport com.alipay.sofa.ark.config.apollo.ApolloConfigActivator;\nimport com.alipay.sofa.ark.config.zk.ZookeeperConfigActivator;\nimport com.alipay.sofa.ark.spi.model.PluginContext;\nimport com.alipay.sofa.ark.spi.service.PluginActivator;\nimport com.google.common.collect.ImmutableMap;\n\nimport java.util.Map;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.*;\n\n/**\n * @author zsk\n * @version $Id: ConfigBaseActivator.java, v 0.1 2023年09月28日 16:56 zsk Exp $\n */\npublic class ConfigBaseActivator implements PluginActivator {\n\n    private final static ArkLogger                                     LOGGER             = ArkLoggerFactory\n                                                                                              .getLogger(ConfigBaseActivator.class);\n\n    private boolean                                                    enableConfigServer = EnvironmentUtils\n                                                                                              .getProperty(\n                                                                                                  CONFIG_SERVER_ENABLE,\n                                                                                                  \"true\")\n                                                                                              .equalsIgnoreCase(\n                                                                                                  \"true\");\n\n    private String                                                     configCenterType   = ArkConfigs\n                                                                                              .getStringValue(CONFIG_SERVER_TYPE);\n\n    private Map<ConfigTypeEnum, LazyActivatorWrapper<PluginActivator>> configTypeMap      = ImmutableMap\n                                                                                              .of(ConfigTypeEnum.zookeeper,\n                                                                                                  new LazyActivatorWrapper(\n                                                                                                      ZookeeperConfigActivator.class),\n                                                                                                  ConfigTypeEnum.apollo,\n                                                                                                  new LazyActivatorWrapper(\n                                                                                                      ApolloConfigActivator.class));\n\n    protected PluginActivator getConfigActivator() {\n        ConfigTypeEnum configType = ConfigTypeEnum.getByNameWithDefault(configCenterType,\n            ConfigTypeEnum.zookeeper);\n        return configTypeMap.get(configType).getLazyActivator();\n    }\n\n    @Override\n    public void start(PluginContext context) {\n        if (!enableConfigServer) {\n            LOGGER.warn(\"config server is disabled.\");\n            return;\n        }\n\n        getConfigActivator().start(context);\n    }\n\n    @Override\n    public void stop(PluginContext context) {\n        if (!enableConfigServer) {\n            return;\n        }\n\n        getConfigActivator().stop(context);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-plugin/config-ark-plugin/src/main/java/com/alipay/sofa/ark/config/ConfigProcessor.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.config;\n\nimport com.alipay.sofa.ark.common.log.ArkLogger;\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.common.thread.CommonThreadPool;\nimport com.alipay.sofa.ark.config.util.OperationTransformer;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.BizState;\nimport com.alipay.sofa.ark.spi.model.PluginContext;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\n\nimport java.util.Deque;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class ConfigProcessor {\n    private final static ArkLogger LOGGER = ArkLoggerFactory.getLogger(ConfigProcessor.class);\n\n    private Deque<String>          configDeque;\n    private CommonThreadPool       commonThreadPool;\n    private PluginContext          pluginContext;\n\n    public static ConfigProcessor createConfigProcessor(PluginContext pluginContext,\n                                                        Deque<String> deque, String processorName) {\n        return new ConfigProcessor(pluginContext, deque, processorName);\n    }\n\n    public ConfigProcessor(PluginContext pluginContext, Deque configDeque, String processorName) {\n        this.pluginContext = pluginContext;\n        this.configDeque = configDeque;\n        this.commonThreadPool = new CommonThreadPool().setThreadPoolName(processorName)\n            .setCorePoolSize(1).setMaximumPoolSize(1).setDaemon(true);\n    }\n\n    public void start() {\n        commonThreadPool.getExecutor().execute(new ConfigTask());\n    }\n\n    public boolean isReadyProcessConfig() {\n        BizManagerService bizManagerService = pluginContext.referenceService(\n            BizManagerService.class).getService();\n        for (Biz biz : bizManagerService.getBizInOrder()) {\n            if (biz.getBizState() != BizState.ACTIVATED\n                && biz.getBizState() != BizState.DEACTIVATED) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    class ConfigTask implements Runnable {\n        @Override\n        public void run() {\n            while (true) {\n                if (!isReadyProcessConfig()) {\n                    sleep(200);\n                    continue;\n                }\n                String config = configDeque.poll();\n                if (config == null) {\n                    sleep(200);\n                    continue;\n                }\n                try {\n                    LOGGER.info(\"ConfigTask: {} start to process config: {}\",\n                        commonThreadPool.getThreadPoolName(), config);\n                    OperationProcessor.process(OperationTransformer.transformToBizOperation(config,\n                        pluginContext));\n                } catch (Throwable throwable) {\n                    LOGGER.error(String.format(\"ConfigTask: %s failed to process config: %s\",\n                        commonThreadPool.getThreadPoolName(), config), throwable);\n                }\n            }\n        }\n\n        private void sleep(long millis) {\n            try {\n                Thread.sleep(millis);\n            } catch (InterruptedException e) {\n                // ignore\n            }\n        }\n    }\n}"
  },
  {
    "path": "sofa-ark-plugin/config-ark-plugin/src/main/java/com/alipay/sofa/ark/config/ConfigTypeEnum.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.config;\n\n/**\n * @author zsk\n * @version $Id: ConfigTypeEnum.java, v 0.1 2023年09月28日 17:16 zsk Exp $\n */\npublic enum ConfigTypeEnum {\n    zookeeper, apollo, ;\n\n    public static ConfigTypeEnum getByNameWithDefault(String name, ConfigTypeEnum defaultValue) {\n        for (ConfigTypeEnum configTypeEnum : ConfigTypeEnum.values()) {\n            if (configTypeEnum.name().equalsIgnoreCase(name)) {\n                return configTypeEnum;\n            }\n        }\n        return defaultValue;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-plugin/config-ark-plugin/src/main/java/com/alipay/sofa/ark/config/LazyActivatorWrapper.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.config;\n\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.service.PluginActivator;\n\n/**\n * @author zsk\n * @version $Id: lazyInitActivator.java, v 0.1 2023年10月11日 17:57 zsk Exp $\n */\npublic class LazyActivatorWrapper<A extends PluginActivator> {\n\n    /**\n     * activator类型\n     */\n    private final Class<A> activatorClass;\n\n    /**\n     * 延时产生的实例\n     */\n    private volatile A     lazyActivator;\n\n    public LazyActivatorWrapper(Class<A> activatorClass) {\n        this.activatorClass = activatorClass;\n    }\n\n    public A getLazyActivator() {\n        if (null != lazyActivator) {\n            return lazyActivator;\n        }\n\n        synchronized (this) {\n            if (null == lazyActivator) {\n                try {\n                    lazyActivator = activatorClass.newInstance();\n                } catch (InstantiationException instantE) {\n                    throw new ArkRuntimeException(\n                        \"InstantiationException in create plugin activator \"\n                                + activatorClass.getName(), instantE);\n                } catch (IllegalAccessException illegalE) {\n                    throw new ArkRuntimeException(\n                        \"IllegalAccessException in create plugin activator \"\n                                + activatorClass.getName(), illegalE);\n                } catch (Throwable e) {\n                    throw e;\n                }\n            }\n        }\n        return lazyActivator;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-plugin/config-ark-plugin/src/main/java/com/alipay/sofa/ark/config/OperationProcessor.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.config;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.api.ClientResponse;\nimport com.alipay.sofa.ark.common.log.ArkLogger;\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.model.BizOperation;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author qilong.zq\n * @since 0.6.0\n */\npublic class OperationProcessor {\n\n    private final static ArkLogger LOGGER = ArkLoggerFactory.getLogger(OperationProcessor.class);\n\n    public static List<ClientResponse> process(List<BizOperation> bizOperations) {\n        List<ClientResponse> clientResponses = new ArrayList<>();\n        try {\n            for (BizOperation bizOperation : bizOperations) {\n                LOGGER.info(\"Execute biz operation: {} {}:{}\", bizOperation.getOperationType()\n                    .name(), bizOperation.getBizName(), bizOperation.getBizVersion());\n                switch (bizOperation.getOperationType()) {\n                    case INSTALL:\n                        clientResponses.add(ArkClient.installOperation(bizOperation));\n                        break;\n                    case UNINSTALL:\n                        clientResponses.add(ArkClient.uninstallOperation(bizOperation));\n                        break;\n                    case SWITCH:\n                        clientResponses.add(ArkClient.switchOperation(bizOperation));\n                        break;\n                    case CHECK:\n                        clientResponses.add(ArkClient.checkOperation(bizOperation));\n                        break;\n                    default:\n                        throw new ArkRuntimeException(String.format(\"Don't support operation: %s.\",\n                            bizOperation.getOperationType()));\n                }\n            }\n        } catch (Throwable throwable) {\n            throw new ArkRuntimeException(\"Failed to execute biz operations.\", throwable);\n        }\n        return clientResponses;\n    }\n}"
  },
  {
    "path": "sofa-ark-plugin/config-ark-plugin/src/main/java/com/alipay/sofa/ark/config/RegistryConfig.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.config;\n\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.spi.constant.Constants;\n\nimport java.util.Map;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class RegistryConfig {\n    /**\n     * protocol\n     */\n    private String              protocol;\n\n    /**\n     * config server address\n     */\n    private String              address;\n\n    /**\n     * config parameters\n     */\n    private Map<String, String> parameters;\n\n    public String getProtocol() {\n        return protocol;\n    }\n\n    public RegistryConfig setProtocol(String protocol) {\n        this.protocol = protocol;\n        return this;\n    }\n\n    public String getAddress() {\n        return address;\n    }\n\n    public RegistryConfig setAddress(String address) {\n        this.address = address;\n        return this;\n    }\n\n    public Map<String, String> getParameters() {\n        return parameters;\n    }\n\n    public RegistryConfig setParameters(Map<String, String> parameters) {\n        this.parameters = parameters;\n        return this;\n    }\n\n    public String getParameter(String key) {\n        return getParameters().get(key);\n    }\n\n    public String getParameter(String key, String defaultValue) {\n        String value = getParameter(key);\n        return value == null ? defaultValue : value;\n    }\n\n    public int getConnectTimeout() {\n        return ArkConfigs.getIntValue(Constants.CONFIG_CONNECT_TIMEOUT,\n            Constants.DEFAULT_CONFIG_CONNECT_TIMEOUT);\n    }\n}"
  },
  {
    "path": "sofa-ark-plugin/config-ark-plugin/src/main/java/com/alipay/sofa/ark/config/apollo/ApolloConfigActivator.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.config.apollo;\n\nimport com.alipay.sofa.ark.common.log.ArkLogger;\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.config.OperationProcessor;\nimport com.alipay.sofa.ark.config.util.OperationTransformer;\nimport com.alipay.sofa.ark.spi.model.PluginContext;\nimport com.alipay.sofa.ark.spi.service.PluginActivator;\nimport com.ctrip.framework.apollo.Config;\nimport com.ctrip.framework.apollo.ConfigChangeListener;\nimport com.ctrip.framework.apollo.ConfigService;\nimport com.ctrip.framework.apollo.model.ConfigChange;\nimport com.ctrip.framework.apollo.model.ConfigChangeEvent;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.APOLLO_MASTER_BIZ_KEY;\nimport static com.alipay.sofa.ark.spi.constant.Constants.CONFIG_APOLLO_NAMESPACE;\n\n/**\n * @author zsk\n * @version $Id: ApolloConfigActivator.java, v 0.1 2023年09月28日 17:24 zsk Exp $\n */\npublic class ApolloConfigActivator implements PluginActivator {\n\n    private final static ArkLogger LOGGER = ArkLoggerFactory.getLogger(ApolloConfigActivator.class);\n\n    private ConfigChangeListener   changeListener;\n\n    @Override\n    public void start(PluginContext context) {\n        LOGGER.info(\"start apollo config activator\");\n\n        Config config = ConfigService.getConfig(CONFIG_APOLLO_NAMESPACE);\n        changeListener = new ConfigChangeListener() {\n            @Override\n            public void onChange(ConfigChangeEvent changeEvent) {\n                for (String key : changeEvent.changedKeys()) {\n                    if (APOLLO_MASTER_BIZ_KEY.equals(key)) {\n                        ConfigChange change = changeEvent.getChange(key);\n                        String value = change.getNewValue();\n                        if (StringUtils.isEmpty(value)) {\n                            LOGGER.info(\"ignore empty masterBiz value\");\n                            return;\n                        }\n                        LOGGER.info(\"Start to process masterBiz config: {}\", value);\n                        OperationProcessor.process(OperationTransformer.transformToBizOperation(\n                            value, context));\n                    } else {\n                        LOGGER\n                            .warn(\n                                \"only accept config key={} in nameSpace={}, ignore changeEvent of key={}\",\n                                APOLLO_MASTER_BIZ_KEY, CONFIG_APOLLO_NAMESPACE, key);\n                    }\n                }\n            }\n        };\n        config.addChangeListener(changeListener);\n    }\n\n    @Override\n    public void stop(PluginContext context) {\n        LOGGER.info(\"stop apollo config activator\");\n\n        Config config = ConfigService.getConfig(CONFIG_APOLLO_NAMESPACE);\n        if (null == changeListener || null == config) {\n            return;\n        }\n        config.removeChangeListener(changeListener);\n    }\n}\n"
  },
  {
    "path": "sofa-ark-plugin/config-ark-plugin/src/main/java/com/alipay/sofa/ark/config/util/NetUtils.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.config.util;\n\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\n\nimport java.net.InetAddress;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class NetUtils {\n    private static String localhost;\n\n    public static String getLocalHostAddress() {\n        try {\n            if (StringUtils.isEmpty(localhost)) {\n                localhost = InetAddress.getLocalHost().getHostAddress();\n            }\n            return localhost;\n        } catch (Throwable throwable) {\n            throw new ArkRuntimeException(throwable);\n        }\n    }\n}"
  },
  {
    "path": "sofa-ark-plugin/config-ark-plugin/src/main/java/com/alipay/sofa/ark/config/util/OperationTransformer.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.config.util;\n\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.BizOperation;\nimport com.alipay.sofa.ark.spi.model.BizState;\nimport com.alipay.sofa.ark.spi.model.PluginContext;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class OperationTransformer {\n\n    /**\n     * transform config into biz operation\n     *\n     * @param config\n     * @return\n     */\n    public static List<BizOperation> transformToBizOperation(String config,\n                                                             PluginContext pluginContext)\n                                                                                         throws IllegalStateException {\n        BizManagerService bizManagerService = pluginContext.referenceService(\n            BizManagerService.class).getService();\n        Map<String, Map<String, BizState>> currentBizState = new HashMap<>();\n\n        for (Biz biz : bizManagerService.getBizInOrder()) {\n            if (!BizState.ACTIVATED.equals(biz.getBizState())\n                && !BizState.DEACTIVATED.equals(biz.getBizState())) {\n                throw new IllegalStateException(String.format(\n                    \"Exist illegal biz: %s, please wait.\", biz));\n            }\n            if (biz.getBizName().equals(ArkConfigs.getStringValue(Constants.MASTER_BIZ))) {\n                continue;\n            } else if (currentBizState.get(biz.getBizName()) == null) {\n                currentBizState.put(biz.getBizName(), new HashMap<String, BizState>());\n            }\n            currentBizState.get(biz.getBizName()).put(biz.getBizVersion(), biz.getBizState());\n        }\n        return doTransformToBizOperation(config, currentBizState);\n    }\n\n    public static List<BizOperation> doTransformToBizOperation(String config,\n                                                               Map<String, Map<String, BizState>> currentBizState)\n                                                                                                                  throws IllegalStateException {\n        List<BizOperation> bizOperations = new ArrayList<>();\n        Map<String, Map<String, BizState>> expectedBizState = new HashMap<>();\n        Map<String, Map<String, BizState>> progressBizState = cloneBizStateMap(currentBizState);\n        ArrayList<String> configOperation = adjustOperationOrder(config);\n\n        for (String operation : configOperation) {\n            int idx = operation.indexOf(Constants.QUESTION_MARK_SPLIT);\n            String[] configInfo = (idx == -1) ? operation.split(Constants.STRING_COLON) : operation\n                .substring(0, idx).split(Constants.STRING_COLON);\n            String bizName = configInfo[0];\n            String bizVersion = configInfo[1];\n            String stateStr = configInfo[2];\n            String parameterStr = (idx == -1) ? Constants.EMPTY_STR : operation.substring(idx + 1);\n            BizState bizState = BizState.of(stateStr);\n            Map<String, String> parameters = parseParameter(parameterStr);\n\n            if (expectedBizState.get(bizName) != null\n                && expectedBizState.get(bizName).get(bizVersion) == bizState) {\n                continue;\n            } else if (expectedBizState.get(bizName) != null\n                       && expectedBizState.get(bizName).get(bizVersion) != null\n                       && expectedBizState.get(bizName).get(bizVersion) != bizState) {\n                throw new IllegalStateException(String.format(\n                    \"Don't specify same biz with different bizState, config is %s.\", config));\n            } else if (expectedBizState.get(bizName) != null\n                       && expectedBizState.get(bizName).containsValue(BizState.ACTIVATED)\n                       && bizState == BizState.ACTIVATED) {\n                throw new IllegalStateException(String.format(\n                    \"Don't allow multi biz with same bizName to be active, config is %s. \", config));\n            }\n\n            if (bizState == BizState.ACTIVATED) {\n                if (progressBizState.get(bizName) != null\n                    && progressBizState.get(bizName).get(bizVersion) != null) {\n                    // switch operation\n                    if (progressBizState.get(bizName).get(bizVersion).equals(BizState.DEACTIVATED)) {\n                        BizOperation bizOperation = BizOperation.createBizOperation()\n                            .setBizName(bizName).setBizVersion(bizVersion)\n                            .setOperationType(BizOperation.OperationType.SWITCH)\n                            .setParameters(parameters);\n                        bizOperations.add(bizOperation);\n                        transformBizState(progressBizState, BizOperation.OperationType.SWITCH,\n                            bizName, bizVersion);\n                    }\n                } else {\n                    // install operation\n                    BizOperation bizOperation = BizOperation.createBizOperation()\n                        .setBizName(bizName).setBizVersion(bizVersion)\n                        .setOperationType(BizOperation.OperationType.INSTALL)\n                        .setParameters(parameters);\n                    bizOperations.add(bizOperation);\n                    if (progressBizState.get(bizName) != null\n                        && progressBizState.get(bizName).containsValue(BizState.ACTIVATED)) {\n                        // add switch\n                        bizOperations.add(BizOperation.createBizOperation()\n                            .setOperationType(BizOperation.OperationType.SWITCH)\n                            .setBizName(bizName).setBizVersion(bizVersion));\n                        transformBizState(progressBizState, BizOperation.OperationType.INSTALL,\n                            bizName, bizVersion);\n                        transformBizState(progressBizState, BizOperation.OperationType.SWITCH,\n                            bizName, bizVersion);\n                    } else {\n                        transformBizState(progressBizState, BizOperation.OperationType.INSTALL,\n                            bizName, bizVersion);\n                    }\n                }\n            } else {\n                if (progressBizState.get(bizName) != null\n                    && progressBizState.get(bizName).get(bizVersion) == null\n                    && progressBizState.get(bizName).containsValue(BizState.ACTIVATED)) {\n                    BizOperation bizOperation = BizOperation.createBizOperation()\n                        .setBizName(bizName).setBizVersion(bizVersion)\n                        .setOperationType(BizOperation.OperationType.INSTALL)\n                        .setParameters(parameters);\n                    bizOperations.add(bizOperation);\n                    transformBizState(progressBizState, BizOperation.OperationType.INSTALL,\n                        bizName, bizVersion);\n                } else if (progressBizState.get(bizName) == null\n                           || !BizState.DEACTIVATED.equals(progressBizState.get(bizName).get(\n                               bizVersion))) {\n                    throw new IllegalStateException(String.format(\n                        \"Biz(%s:%s) cant be transform to %s, config is %s.\", bizName, bizVersion,\n                        bizState, config));\n                }\n            }\n            if (expectedBizState.get(bizName) == null) {\n                expectedBizState.put(bizName, new HashMap<String, BizState>());\n            }\n            expectedBizState.get(bizName).put(bizVersion, bizState);\n        }\n\n        for (String bizName : currentBizState.keySet()) {\n            for (String bizVersion : currentBizState.get(bizName).keySet()) {\n                if (expectedBizState.get(bizName) == null\n                    || !expectedBizState.get(bizName).containsKey(bizVersion)) {\n                    bizOperations.add(BizOperation.createBizOperation()\n                        .setOperationType(BizOperation.OperationType.UNINSTALL).setBizName(bizName)\n                        .setBizVersion(bizVersion));\n                    transformBizState(progressBizState, BizOperation.OperationType.UNINSTALL,\n                        bizName, bizVersion);\n                }\n            }\n        }\n\n        // double check\n        if (!checkBizState(expectedBizState, progressBizState)) {\n            throw new IllegalStateException(String.format(\n                \"Failed to transform biz operation, config is %s.\", config));\n        }\n\n        return bizOperations;\n    }\n\n    /**\n     * config format is similar to bizName:bizVersion:bizState;bizName:bizVersion:bizState\n     *\n     * @param config\n     * @return\n     */\n    public static boolean isValidConfig(String config) {\n        for (String singleConfig : config.split(Constants.STRING_SEMICOLON)) {\n            int idx = singleConfig.indexOf(Constants.QUESTION_MARK_SPLIT);\n            String parameterStr = (idx == -1) ? \"\" : singleConfig.substring(idx + 1);\n            String nvs = (idx == -1) ? singleConfig : singleConfig.substring(0, idx);\n            String[] configInfo = nvs.split(Constants.STRING_COLON);\n            if (configInfo.length != 3) {\n                return false;\n            }\n\n            if (!BizState.ACTIVATED.name().equalsIgnoreCase(configInfo[2])\n                && !BizState.DEACTIVATED.name().equalsIgnoreCase(configInfo[2])) {\n                return false;\n            }\n\n            if (!isValidParameter(parameterStr)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    public static Map<String, String> parseParameter(String config) {\n        Map<String, String> parameters = new HashMap<>();\n        if (!StringUtils.isEmpty(config)) {\n            String[] keyValue = config.split(Constants.AMPERSAND_SPLIT);\n            for (String kv : keyValue) {\n                String[] paramSplit = kv.split(Constants.EQUAL_SPLIT);\n                parameters.put(paramSplit[0], paramSplit[1]);\n            }\n        }\n        return parameters;\n    }\n\n    public static boolean isValidParameter(String config) {\n        if (!StringUtils.isEmpty(config)) {\n            String[] keyValue = config.split(Constants.AMPERSAND_SPLIT);\n            for (String kv : keyValue) {\n                String[] paramSplit = kv.split(Constants.EQUAL_SPLIT);\n                if (paramSplit.length != 2) {\n                    return false;\n                }\n            }\n        }\n        return true;\n    }\n\n    public static void transformBizState(Map<String, Map<String, BizState>> progressBizState,\n                                         BizOperation.OperationType operationType, String bizName,\n                                         String bizVersion) {\n        if (BizOperation.OperationType.SWITCH.equals(operationType)) {\n            if (progressBizState.get(bizName) != null) {\n                for (String version : progressBizState.get(bizName).keySet()) {\n                    progressBizState.get(bizName).put(version, BizState.DEACTIVATED);\n                }\n            }\n            progressBizState.get(bizName).put(bizVersion, BizState.ACTIVATED);\n        } else if (BizOperation.OperationType.INSTALL.equals(operationType)) {\n            if (progressBizState.get(bizName) != null\n                && progressBizState.get(bizName).containsValue(BizState.ACTIVATED)) {\n                progressBizState.get(bizName).put(bizVersion, BizState.DEACTIVATED);\n            } else {\n                if (progressBizState.get(bizName) == null) {\n                    progressBizState.put(bizName, new HashMap<String, BizState>());\n                }\n                progressBizState.get(bizName).put(bizVersion, BizState.ACTIVATED);\n            }\n        } else if (BizOperation.OperationType.UNINSTALL.equals(operationType)) {\n            if (progressBizState.get(bizName) != null) {\n                progressBizState.get(bizName).remove(bizVersion);\n            }\n        }\n    }\n\n    /**\n     * prefer to handle activated state\n     *\n     * @param config\n     */\n    public static ArrayList<String> adjustOperationOrder(String config) {\n        ArrayList<String> activatedStateConfig = new ArrayList<>();\n        ArrayList<String> deactivatedStateConfig = new ArrayList<>();\n        for (String configOperation : config.split(Constants.STRING_SEMICOLON)) {\n            if (StringUtils.isEmpty(configOperation)) {\n                continue;\n            }\n            int idx = configOperation.indexOf(Constants.QUESTION_MARK_SPLIT);\n            String[] configInfo = (idx == -1) ? configOperation.split(Constants.STRING_COLON)\n                : configOperation.substring(0, idx).split(Constants.STRING_COLON);\n            BizState bizState = BizState.of(configInfo[2]);\n            if (BizState.ACTIVATED.equals(bizState)) {\n                activatedStateConfig.add(configOperation);\n            } else {\n                deactivatedStateConfig.add(configOperation);\n            }\n        }\n        activatedStateConfig.addAll(deactivatedStateConfig);\n        return activatedStateConfig;\n    }\n\n    public static boolean checkBizState(Map<String, Map<String, BizState>> expectedBizState,\n                                        Map<String, Map<String, BizState>> progressBizState) {\n        for (String bizName : expectedBizState.keySet()) {\n            if (progressBizState.get(bizName) == null\n                || progressBizState.get(bizName).keySet().size() != expectedBizState.get(bizName)\n                    .size()) {\n                return false;\n            }\n            for (String bizVersion : expectedBizState.get(bizName).keySet()) {\n                if (!expectedBizState.get(bizName).get(bizVersion)\n                    .equals(progressBizState.get(bizName).get(bizVersion))) {\n                    return false;\n                }\n            }\n        }\n        return true;\n    }\n\n    public static Map<String, Map<String, BizState>> cloneBizStateMap(Map<String, Map<String, BizState>> origin) {\n        if (origin == null) {\n            return null;\n        }\n        Map<String, Map<String, BizState>> duplicate = new HashMap<>();\n        for (String name : origin.keySet()) {\n            if (duplicate.get(name) == null) {\n                duplicate.put(name, new HashMap<String, BizState>());\n            }\n            for (String version : origin.get(name).keySet()) {\n                duplicate.get(name).put(version, origin.get(name).get(version));\n            }\n        }\n        return duplicate;\n    }\n}"
  },
  {
    "path": "sofa-ark-plugin/config-ark-plugin/src/main/java/com/alipay/sofa/ark/config/zk/ZookeeperConfigActivator.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.config.zk;\n\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.common.log.ArkLogger;\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.common.util.AssertUtils;\nimport com.alipay.sofa.ark.common.util.EnvironmentUtils;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.config.ConfigProcessor;\nimport com.alipay.sofa.ark.config.util.OperationTransformer;\nimport com.alipay.sofa.ark.config.OperationProcessor;\nimport com.alipay.sofa.ark.config.RegistryConfig;\nimport com.alipay.sofa.ark.config.util.NetUtils;\nimport com.alipay.sofa.ark.exception.ArkRuntimeException;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.event.AfterFinishDeployEvent;\nimport com.alipay.sofa.ark.spi.event.AfterFinishStartupEvent;\nimport com.alipay.sofa.ark.spi.model.PluginContext;\nimport com.alipay.sofa.ark.spi.service.PluginActivator;\nimport com.alipay.sofa.ark.spi.service.event.EventAdminService;\nimport com.alipay.sofa.ark.spi.service.event.EventHandler;\nimport org.apache.curator.RetryPolicy;\nimport org.apache.curator.framework.AuthInfo;\nimport org.apache.curator.framework.CuratorFramework;\nimport org.apache.curator.framework.CuratorFrameworkFactory;\nimport org.apache.curator.framework.api.ACLProvider;\nimport org.apache.curator.framework.recipes.cache.*;\nimport org.apache.curator.framework.state.ConnectionState;\nimport org.apache.curator.framework.state.ConnectionStateListener;\nimport org.apache.curator.retry.ExponentialBackoffRetry;\nimport org.apache.zookeeper.CreateMode;\nimport org.apache.zookeeper.KeeperException;\nimport org.apache.zookeeper.ZooDefs;\nimport org.apache.zookeeper.data.ACL;\n\nimport java.util.*;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.CONFIG_SERVER_ENABLE;\n\n/**\n * @author qilong.zql\n * @author GengZhang\n * @since 0.6.0\n */\npublic class ZookeeperConfigActivator implements PluginActivator {\n\n    private final static ArkLogger LOGGER          = ArkLoggerFactory\n                                                       .getLogger(ZookeeperConfigActivator.class);\n\n    private boolean                enableZkServer  = EnvironmentUtils.getProperty(\n                                                       CONFIG_SERVER_ENABLE, \"true\")\n                                                       .equalsIgnoreCase(\"true\");\n\n    /**\n     * Zookeeper zkClient\n     */\n    private CuratorFramework       zkClient;\n\n    /**\n     * Root path of zk registry resource\n     */\n    private String                 rootPath        = Constants.ZOOKEEPER_CONTEXT_SPLIT;\n\n    private String                 ipResourcePath  = buildIpConfigPath();\n\n    private String                 bizResourcePath = buildMasterBizConfigPath();\n\n    private Deque<String>          ipConfigDeque   = new ArrayDeque<>(5);\n\n    private Deque<String>          bizConfigDeque  = new ArrayDeque<>(5);\n\n    private CuratorCache           ipCuratorCache;\n    private CuratorCache           bizCuratorCache;\n\n    @Override\n    public void start(final PluginContext context) {\n        if (!enableZkServer) {\n            LOGGER.warn(\"config server is disabled.\");\n            return;\n        }\n        LOGGER.info(\"start zookeeper config activator\");\n\n        String config = ArkConfigs.getStringValue(Constants.CONFIG_SERVER_ADDRESS);\n        RegistryConfig registryConfig = ZookeeperConfigurator.buildConfig(config);\n        String address = registryConfig.getAddress();\n        int idx = address.indexOf(Constants.ZOOKEEPER_CONTEXT_SPLIT);\n        if (idx != -1) {\n            rootPath = address.substring(idx);\n            if (!rootPath.endsWith(Constants.ZOOKEEPER_CONTEXT_SPLIT)) {\n                rootPath += Constants.ZOOKEEPER_CONTEXT_SPLIT;\n            }\n            address = address.substring(0, idx);\n        }\n\n        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);\n        CuratorFrameworkFactory.Builder zkClientBuilder = CuratorFrameworkFactory.builder()\n            .connectString(address).sessionTimeoutMs(3 * registryConfig.getConnectTimeout())\n            .connectionTimeoutMs(registryConfig.getConnectTimeout()).canBeReadOnly(false)\n            .retryPolicy(retryPolicy).defaultData(null);\n\n        List<AuthInfo> authInfos = buildAuthInfo(registryConfig);\n        if (!authInfos.isEmpty()) {\n            zkClientBuilder = zkClientBuilder.aclProvider(getDefaultAclProvider()).authorization(\n                authInfos);\n        }\n\n        zkClient = zkClientBuilder.build();\n        zkClient.getConnectionStateListenable().addListener(new ConnectionStateListener() {\n            @Override\n            public void stateChanged(CuratorFramework client, ConnectionState newState) {\n                LOGGER.info(\"Reconnect to zookeeper, re-register config resource.\");\n                if (newState == ConnectionState.RECONNECTED) {\n                    unSubscribeIpConfig();\n                    registryResource(ipResourcePath, CreateMode.EPHEMERAL);\n                    subscribeIpConfig();\n                }\n            }\n        });\n\n        zkClient.start();\n        registryResource(bizResourcePath, CreateMode.PERSISTENT);\n        registryResource(ipResourcePath, CreateMode.EPHEMERAL);\n        subscribeIpConfig();\n        subscribeBizConfig();\n        registerEventHandler(context);\n    }\n\n    @Override\n    public void stop(PluginContext context) {\n\n        if (!enableZkServer) {\n            return;\n        }\n\n        if (Objects.nonNull(ipCuratorCache)) {\n            try {\n                ipCuratorCache.close();\n            } catch (Exception e) {\n                // ignore\n            }\n        }\n        if (Objects.nonNull(bizCuratorCache)) {\n            try {\n                bizCuratorCache.close();\n            } catch (Exception e) {\n                // ignore\n            }\n        }\n        zkClient.close();\n    }\n\n    protected void registerEventHandler(final PluginContext context) {\n        Optional<ChildData> currentData = this.bizCuratorCache.get(bizResourcePath);\n        if (currentData.isPresent() && Objects.nonNull(currentData.get().getData())) {\n            final String bizInitConfig = new String(currentData.get().getData());\n            EventAdminService eventAdminService = context.referenceService(EventAdminService.class)\n                .getService();\n            eventAdminService.register(new EventHandler<AfterFinishDeployEvent>() {\n                @Override\n                public void handleEvent(AfterFinishDeployEvent event) {\n                    LOGGER.info(\"Start to process init app config: {}\", bizInitConfig);\n                    OperationProcessor.process(OperationTransformer.transformToBizOperation(\n                        bizInitConfig, context));\n                }\n\n                @Override\n                public int getPriority() {\n                    return 0;\n                }\n            });\n            eventAdminService.register(new EventHandler<AfterFinishStartupEvent>() {\n                @Override\n                public void handleEvent(AfterFinishStartupEvent event) {\n                    ConfigProcessor.createConfigProcessor(context, ipConfigDeque,\n                        \"ip-zookeeper-config\").start();\n                    ConfigProcessor.createConfigProcessor(context, bizConfigDeque,\n                        \"app-zookeeper-config\").start();\n                }\n\n                @Override\n                public int getPriority() {\n                    return 0;\n                }\n            });\n        }\n\n    }\n\n    protected void subscribeIpConfig() {\n        this.ipCuratorCache = CuratorCache.builder(zkClient, ipResourcePath).build();\n        this.ipCuratorCache.listenable().addListener(new CuratorCacheListener() {\n            private int version = -1;\n\n            @Override\n            public void event(Type type, ChildData oldChildData, ChildData currentChildData) {\n                if (type == Type.NODE_CHANGED) {\n                    if (Objects.nonNull(currentChildData)\n                        && currentChildData.getStat().getVersion() > version) {\n                        version = currentChildData.getStat().getVersion();\n                        String configData = new String(currentChildData.getData());\n                        ipConfigDeque.add(configData);\n                        LOGGER.info(\"Receive ip config data: {}, version is {}.\", configData,\n                            version);\n                    }\n                }\n            }\n        });\n        try {\n            LOGGER.info(\"Subscribe ip config: {}.\", ipResourcePath);\n            ipCuratorCache.start();\n        } catch (Exception e) {\n            throw new ArkRuntimeException(\"Failed to subscribe ip resource path.\", e);\n        }\n    }\n\n    protected void unSubscribeIpConfig() {\n        if (Objects.nonNull(ipCuratorCache)) {\n            try {\n                LOGGER.info(\"Un-subscribe ip config: {}.\", ipResourcePath);\n                ipCuratorCache.close();\n            } catch (Throwable throwable) {\n                LOGGER.error(\"Failed to un-subscribe ip resource path.\");\n            }\n            ipCuratorCache = null;\n        }\n    }\n\n    protected void subscribeBizConfig() {\n        this.bizCuratorCache = CuratorCache.build(zkClient, bizResourcePath);\n\n        this.bizCuratorCache.listenable().addListener(new CuratorCacheListener() {\n            private int version = -1;\n\n            @Override\n            public void event(Type type, ChildData oldChildData, ChildData currentChildData) {\n                if (type == Type.NODE_CHANGED) {\n                    if (Objects.nonNull(currentChildData)\n                        && currentChildData.getStat().getVersion() > version) {\n                        version = currentChildData.getStat().getVersion();\n                        String configData = new String(currentChildData.getData());\n                        bizConfigDeque.add(configData);\n                        LOGGER.info(\"Receive app config data: {}, version is {}.\", configData,\n                            version);\n                    }\n                }\n            }\n        });\n\n        try {\n            bizCuratorCache.start();\n        } catch (Exception e) {\n            throw new ArkRuntimeException(\"Failed to subscribe resource path.\", e);\n        }\n    }\n\n    protected void registryResource(String path, CreateMode createMode) {\n        try {\n            LOGGER.info(\"Registry context path: {} with mode: {}.\", path, createMode);\n            zkClient.create().creatingParentContainersIfNeeded().withMode(createMode).forPath(path);\n        } catch (KeeperException.NodeExistsException nodeExistsException) {\n            if (LOGGER.isWarnEnabled()) {\n                LOGGER.warn(\"Context path has exists in zookeeper, path=\" + path);\n            }\n        } catch (Exception e) {\n            throw new ArkRuntimeException(\"Failed to register resource to zookeeper registry!\", e);\n        }\n    }\n\n    public String buildIpConfigPath() {\n        return buildMasterBizRootPath().append(Constants.ZOOKEEPER_CONTEXT_SPLIT)\n            .append(NetUtils.getLocalHostAddress()).toString();\n    }\n\n    public String buildMasterBizConfigPath() {\n        return buildMasterBizRootPath().toString();\n    }\n\n    private StringBuilder buildMasterBizRootPath() {\n        StringBuilder masterBizRootPath = new StringBuilder(rootPath);\n        String masterBizName = ArkConfigs.getStringValue(Constants.MASTER_BIZ);\n        AssertUtils.isFalse(StringUtils.isEmpty(masterBizName), \"Master biz should be specified.\");\n        String configEnvironment = ArkConfigs.getStringValue(Constants.CONFIG_SERVER_ENVIRONMENT,\n            \"sofa-ark\");\n        masterBizRootPath.append(configEnvironment).append(Constants.ZOOKEEPER_CONTEXT_SPLIT)\n            .append(masterBizName);\n        return masterBizRootPath;\n    }\n\n    /**\n     * build auth info\n     *\n     * @return\n     */\n    private List<AuthInfo> buildAuthInfo(RegistryConfig registryConfig) {\n        List<AuthInfo> info = new ArrayList<AuthInfo>();\n\n        String scheme = registryConfig.getParameter(\"scheme\");\n\n        //addAuth=user1:password1,user2:password2\n        String addAuth = registryConfig.getParameter(\"addAuth\");\n\n        if (!StringUtils.isEmpty(addAuth)) {\n            String[] authList = addAuth.split(\",\");\n            for (String singleAuthInfo : authList) {\n                info.add(new AuthInfo(scheme, singleAuthInfo.getBytes()));\n            }\n        }\n        return info;\n    }\n\n    /**\n     * Get default AclProvider\n     *\n     * @return\n     */\n    private ACLProvider getDefaultAclProvider() {\n        return new ACLProvider() {\n            @Override\n            public List<ACL> getDefaultAcl() {\n                return ZooDefs.Ids.CREATOR_ALL_ACL;\n            }\n\n            @Override\n            public List<ACL> getAclForPath(String path) {\n                return ZooDefs.Ids.CREATOR_ALL_ACL;\n            }\n        };\n    }\n}"
  },
  {
    "path": "sofa-ark-plugin/config-ark-plugin/src/main/java/com/alipay/sofa/ark/config/zk/ZookeeperConfigurator.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.config.zk;\n\nimport com.alipay.sofa.ark.common.util.AssertUtils;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.config.RegistryConfig;\nimport com.alipay.sofa.ark.spi.constant.Constants;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author qilong.zql\n * @author LiWei\n * @since 0.6.0\n */\npublic class ZookeeperConfigurator {\n    public static RegistryConfig buildConfig(String config) {\n        String zkAddress = parseAddress(config);\n        Map<String, String> parameters = parseParam(config);\n        return new RegistryConfig().setAddress(zkAddress)\n            .setProtocol(Constants.CONFIG_PROTOCOL_ZOOKEEPER).setParameters(parameters);\n    }\n\n    /**\n     * parse address\n     *\n     * @param config config value\n     */\n    public static String parseAddress(String config) {\n        AssertUtils.isFalse(StringUtils.isEmpty(config), \"Zookeeper config should not be empty.\");\n        AssertUtils.isTrue(config.startsWith(Constants.CONFIG_PROTOCOL_ZOOKEEPER_HEADER),\n            \"Zookeeper config should start with 'zookeeper://'.\");\n        String value = config.substring(Constants.CONFIG_PROTOCOL_ZOOKEEPER_HEADER.length());\n        int idx = value.indexOf(Constants.QUESTION_MARK_SPLIT);\n        String address = (idx == -1) ? value : value.substring(0, idx);\n        AssertUtils.isFalse(StringUtils.isEmpty(address), \"Zookeeper address should not be empty\");\n        return address;\n    }\n\n    /**\n     * parse params\n     *\n     * @param config\n     * @return\n     */\n    public static Map<String, String> parseParam(String config) {\n        AssertUtils.assertNotNull(config, \"Params should not be null.\");\n        Map<String, String> map = new HashMap<>();\n        int idx = config.indexOf(Constants.QUESTION_MARK_SPLIT);\n        String paramString = (idx == -1) ? Constants.EMPTY_STR : config.substring(idx + 1);\n        String[] paramSplit = paramString.split(Constants.AMPERSAND_SPLIT);\n        for (String param : paramSplit) {\n            if (!StringUtils.isEmpty(param)) {\n                String[] kvSplit = param.split(Constants.EQUAL_SPLIT);\n                AssertUtils.isTrue(kvSplit.length == 2,\n                    String.format(\"Config parameter %s is invalid format.\", kvSplit));\n                map.put(kvSplit[0], kvSplit[1]);\n            }\n        }\n        return map;\n    }\n}"
  },
  {
    "path": "sofa-ark-plugin/config-ark-plugin/src/test/java/com/alipay/sofa/ark/config/ApolloConfigActivatorTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.config;\n\nimport com.alipay.sofa.ark.config.apollo.ApolloConfigActivator;\nimport com.alipay.sofa.ark.spi.model.PluginContext;\nimport com.ctrip.framework.apollo.ConfigChangeListener;\nimport com.ctrip.framework.apollo.ConfigService;\nimport com.ctrip.framework.apollo.enums.PropertyChangeType;\nimport com.ctrip.framework.apollo.model.ConfigChange;\nimport com.ctrip.framework.apollo.model.ConfigChangeEvent;\nimport com.google.common.collect.ImmutableMap;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.mockito.MockedStatic;\nimport org.mockito.Mockito;\n\nimport java.lang.reflect.Field;\nimport java.util.List;\nimport java.util.Map;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.APOLLO_MASTER_BIZ_KEY;\nimport static com.alipay.sofa.ark.spi.constant.Constants.CONFIG_APOLLO_NAMESPACE;\nimport static org.mockito.ArgumentMatchers.any;\n\n/**\n * @author zsk\n * @version $Id: ApolloConfigActivatorTest.java, v 0.1 2023年10月11日 18:22 zsk Exp $\n */\npublic class ApolloConfigActivatorTest {\n\n    @Test\n    public void testStartStop() throws NoSuchFieldException, IllegalAccessException {\n        MockApolloConfig mockConfig = new MockApolloConfig();\n        MockedStatic<ConfigService> mockService = Mockito.mockStatic(ConfigService.class);\n        mockService.when(() -> ConfigService.getConfig(CONFIG_APOLLO_NAMESPACE))\n                .thenReturn(mockConfig);\n\n        ApolloConfigActivator apolloActivator = new ApolloConfigActivator();\n        PluginContext pluginContext = Mockito.mock(PluginContext.class);\n        Mockito.doThrow(new RuntimeException(\"mock referenceService Failed\"))\n                .when(pluginContext).referenceService(any());\n        //test start\n        apolloActivator.start(pluginContext);\n        Field field = ApolloConfigActivator.class.getDeclaredField(\"changeListener\");\n        field.setAccessible(true);\n        Object changeListener = field.get(apolloActivator);\n        Assert.assertNotNull(changeListener);\n\n        List<ConfigChangeListener> list = mockConfig.getListeners();\n        Assert.assertTrue(1 == list.size());\n\n        String newValue = \"nameA:vA:activated;nameA:vB:deactivated\";\n        ConfigChange configChange = new ConfigChange(CONFIG_APOLLO_NAMESPACE, APOLLO_MASTER_BIZ_KEY, \"\", newValue, PropertyChangeType.MODIFIED);\n        Map<String, ConfigChange > changeMap = ImmutableMap.of(APOLLO_MASTER_BIZ_KEY, configChange);\n        ConfigChangeEvent changeEvent = new ConfigChangeEvent(CONFIG_APOLLO_NAMESPACE, changeMap);\n\n        //test apollo onChange event: match masterBiz key but OperationProcessor.process failed\n        Throwable throwable = null;\n        try {\n            list.get(0).onChange(changeEvent);\n        } catch (Throwable t) {\n            throwable = t;\n        }\n        Assert.assertNotNull(throwable);\n        Assert.assertTrue(throwable.getMessage().equals(\n                \"mock referenceService Failed\"));\n\n        //stop\n        apolloActivator.stop(null);\n        list = mockConfig.getListeners();\n        Assert.assertTrue(0 == list.size());\n    }\n}\n"
  },
  {
    "path": "sofa-ark-plugin/config-ark-plugin/src/test/java/com/alipay/sofa/ark/config/ConfigBaseActivatorTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.config;\n\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.common.util.EnvironmentUtils;\nimport com.alipay.sofa.ark.config.apollo.ApolloConfigActivator;\nimport com.alipay.sofa.ark.spi.service.PluginActivator;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport static com.alipay.sofa.ark.spi.constant.Constants.CONFIG_SERVER_ENABLE;\nimport static com.alipay.sofa.ark.spi.constant.Constants.CONFIG_SERVER_TYPE;\n\n/**\n * @author zsk\n * @version $Id: ConfigBaseActivatorTest.java, v 0.1 2023年10月11日 17:42 zsk Exp $\n */\npublic class ConfigBaseActivatorTest {\n\n    @Test\n    public void testGetConfigActivator() {\n        EnvironmentUtils.setProperty(CONFIG_SERVER_ENABLE, \"true\");\n        ArkConfigs.putStringValue(CONFIG_SERVER_TYPE, ConfigTypeEnum.apollo.name());\n        ConfigBaseActivator baseActivator = new ConfigBaseActivator();\n        PluginActivator activator = baseActivator.getConfigActivator();\n        Assert.assertTrue(activator instanceof ApolloConfigActivator);\n    }\n\n    @Test\n    public void testFailNewZookeeperConfigurator() {\n        EnvironmentUtils.setProperty(CONFIG_SERVER_ENABLE, \"true\");\n        ArkConfigs.putStringValue(CONFIG_SERVER_TYPE, ConfigTypeEnum.zookeeper.name());\n        ConfigBaseActivator baseActivator = new ConfigBaseActivator();\n        Throwable throwable = null;\n        try {\n            baseActivator.start(null);\n        } catch (Throwable t) {\n            throwable = t;\n        }\n        Assert.assertNotNull(throwable);\n        Assert.assertTrue(throwable.getMessage().equals(\"Master biz should be specified.\"));\n    }\n\n}\n"
  },
  {
    "path": "sofa-ark-plugin/config-ark-plugin/src/test/java/com/alipay/sofa/ark/config/MockApolloConfig.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.config;\n\nimport com.ctrip.framework.apollo.Config;\nimport com.ctrip.framework.apollo.ConfigChangeListener;\nimport com.ctrip.framework.apollo.enums.ConfigSourceType;\nimport com.google.common.base.Function;\nimport com.google.common.collect.Lists;\n\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Set;\n\n/**\n * @author zsk\n * @version $Id: MockApolloConfig.java, v 0.1 2023年10月11日 20:26 zsk Exp $\n */\npublic class MockApolloConfig implements Config {\n    private final List<ConfigChangeListener> m_listeners = Lists.newCopyOnWriteArrayList();\n\n    @Override\n    public String getProperty(String key, String defaultValue) {\n        return null;\n    }\n\n    @Override\n    public Integer getIntProperty(String key, Integer defaultValue) {\n        return null;\n    }\n\n    @Override\n    public Long getLongProperty(String key, Long defaultValue) {\n        return null;\n    }\n\n    @Override\n    public Short getShortProperty(String key, Short defaultValue) {\n        return null;\n    }\n\n    @Override\n    public Float getFloatProperty(String key, Float defaultValue) {\n        return null;\n    }\n\n    @Override\n    public Double getDoubleProperty(String key, Double defaultValue) {\n        return null;\n    }\n\n    @Override\n    public Byte getByteProperty(String key, Byte defaultValue) {\n        return null;\n    }\n\n    @Override\n    public Boolean getBooleanProperty(String key, Boolean defaultValue) {\n        return null;\n    }\n\n    @Override\n    public String[] getArrayProperty(String key, String delimiter, String[] defaultValue) {\n        return new String[0];\n    }\n\n    @Override\n    public Date getDateProperty(String key, Date defaultValue) {\n        return null;\n    }\n\n    @Override\n    public Date getDateProperty(String key, String format, Date defaultValue) {\n        return null;\n    }\n\n    @Override\n    public Date getDateProperty(String key, String format, Locale locale, Date defaultValue) {\n        return null;\n    }\n\n    @Override\n    public <T extends Enum<T>> T getEnumProperty(String key, Class<T> enumType, T defaultValue) {\n        return null;\n    }\n\n    @Override\n    public long getDurationProperty(String key, long defaultValue) {\n        return 0;\n    }\n\n    public List<ConfigChangeListener> getListeners() {\n        return m_listeners;\n    }\n\n    @Override\n    public void addChangeListener(ConfigChangeListener listener) {\n        addChangeListener(listener, null);\n    }\n\n    @Override\n    public void addChangeListener(ConfigChangeListener listener, Set<String> interestedKeys) {\n        addChangeListener(listener, interestedKeys, null);\n    }\n\n    @Override\n    public void addChangeListener(ConfigChangeListener listener, Set<String> interestedKeys,\n                                  Set<String> interestedKeyPrefixes) {\n        if (!m_listeners.contains(listener)) {\n            m_listeners.add(listener);\n        }\n    }\n\n    @Override\n    public boolean removeChangeListener(ConfigChangeListener listener) {\n        return m_listeners.remove(listener);\n    }\n\n    @Override\n    public Set<String> getPropertyNames() {\n        return null;\n    }\n\n    @Override\n    public <T> T getProperty(String key, Function<String, T> function, T defaultValue) {\n        return null;\n    }\n\n    @Override\n    public ConfigSourceType getSourceType() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-plugin/config-ark-plugin/src/test/java/com/alipay/sofa/ark/config/OperationTransformerTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.config;\n\nimport com.alipay.sofa.ark.api.ArkClient;\nimport com.alipay.sofa.ark.api.ArkConfigs;\nimport com.alipay.sofa.ark.common.util.StringUtils;\nimport com.alipay.sofa.ark.config.util.OperationTransformer;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport com.alipay.sofa.ark.spi.model.Biz;\nimport com.alipay.sofa.ark.spi.model.BizOperation;\nimport com.alipay.sofa.ark.spi.model.BizState;\nimport com.alipay.sofa.ark.spi.model.PluginContext;\nimport com.alipay.sofa.ark.spi.registry.ServiceReference;\nimport com.alipay.sofa.ark.spi.service.biz.BizManagerService;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.mockito.Mockito;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.when;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class OperationTransformerTest {\n\n    @Test\n    public void testBizFilePath() {\n        File file = ArkClient.createBizSaveFile(\"name\", \"version\");\n        Assert.assertTrue(StringUtils.isEmpty(ArkConfigs\n            .getStringValue(Constants.CONFIG_INSTALL_BIZ_DIR)));\n        Assert.assertTrue(file.getAbsolutePath().contains(\n            \"sofa-ark\" + File.separator + \"name-version-\"));\n    }\n\n    @Test\n    public void testTransformFormatError() {\n        Assert.assertFalse(OperationTransformer.isValidConfig(\"aaa\"));\n        Assert.assertFalse(OperationTransformer.isValidConfig(\"name:version:resolved\"));\n        Assert.assertFalse(OperationTransformer.isValidConfig(\"name:version:activated?url\"));\n        Assert.assertTrue(OperationTransformer\n            .isValidConfig(\"name:version:activated?url=http://xx\"));\n        Assert.assertTrue(OperationTransformer\n            .isValidConfig(\"name:version:activated?url=http://xx&param2=value2\"));\n        String config = \"k1=v1&k2=v2\";\n        Map<String, String> params = OperationTransformer.parseParameter(config);\n        Assert.assertEquals(2, params.size());\n        Assert.assertEquals(\"v1\", params.get(\"k1\"));\n        Assert.assertEquals(\"v2\", params.get(\"k2\"));\n    }\n\n    @Test\n    public void testTransformConfigOperationWithConflictState() {\n        Map<String, Map<String, BizState>> currentBizState = new HashMap<>();\n        Exception ex = null;\n        List<BizOperation> operations = null;\n        try {\n            operations = OperationTransformer.doTransformToBizOperation(\n                \"n1:v1:activated;n1:v1:deactivated\", currentBizState);\n        } catch (IllegalStateException e) {\n            ex = e;\n        }\n        Assert.assertNotNull(ex);\n        Assert.assertTrue(ex.getMessage()\n            .contains(\"Don't specify same biz with different bizState\"));\n    }\n\n    @Test\n    public void testTransformUninstallConfigOperation() {\n        List<BizOperation> bizOperations = OperationTransformer.doTransformToBizOperation(\"\",\n            mockBizState());\n        Assert.assertEquals(3, bizOperations.size());\n        Assert.assertTrue(bizOperations.contains(BizOperation.createBizOperation()\n            .setBizName(\"nameA\").setBizVersion(\"vA\")\n            .setOperationType(BizOperation.OperationType.UNINSTALL)));\n        Assert.assertTrue(bizOperations.contains(BizOperation.createBizOperation()\n            .setBizName(\"nameA\").setBizVersion(\"vB\")\n            .setOperationType(BizOperation.OperationType.UNINSTALL)));\n        Assert.assertTrue(bizOperations.contains(BizOperation.createBizOperation()\n            .setBizName(\"nameB\").setBizVersion(\"vA\")\n            .setOperationType(BizOperation.OperationType.UNINSTALL)));\n    }\n\n    @Test\n    public void testTransformConfigOperationWithMultiActivateState() {\n        Map<String, Map<String, BizState>> currentBizState = new HashMap<>();\n        Exception ex = null;\n        List<BizOperation> operations = null;\n        try {\n            operations = OperationTransformer.doTransformToBizOperation(\n                \"n1:v1:activated;n1:v2:activated\", currentBizState);\n        } catch (IllegalStateException e) {\n            ex = e;\n        }\n        Assert.assertNotNull(ex);\n        Assert.assertTrue(ex.getMessage().contains(\n            \"Don't allow multi biz with same bizName to be active\"));\n    }\n\n    @Test\n    public void testTransformConfigOperationNotAllowed() {\n        Map<String, Map<String, BizState>> currentBizState = new HashMap<>();\n        Exception ex = null;\n        List<BizOperation> operations = null;\n        try {\n            operations = OperationTransformer.doTransformToBizOperation(\n                \"n1:v1:deactivated;n1:v2:deactivated\", currentBizState);\n        } catch (IllegalStateException e) {\n            ex = e;\n        }\n        Assert.assertNotNull(ex);\n        Assert.assertTrue(ex.getMessage().contains(\"cant be transform to\"));\n    }\n\n    @Test\n    public void testTransformUnInstallOperation() {\n        List<BizOperation> bizOperations = OperationTransformer.doTransformToBizOperation(\n            \"nameA:vA:activated\", mockBizState());\n        Assert.assertEquals(2, bizOperations.size());\n        Assert.assertTrue(bizOperations.contains(BizOperation.createBizOperation()\n            .setBizName(\"nameA\").setBizVersion(\"vB\")\n            .setOperationType(BizOperation.OperationType.UNINSTALL)));\n        Assert.assertTrue(bizOperations.contains(BizOperation.createBizOperation()\n            .setBizName(\"nameB\").setBizVersion(\"vA\")\n            .setOperationType(BizOperation.OperationType.UNINSTALL)));\n    }\n\n    @Test\n    public void testTransformInstallOperation() {\n        List<BizOperation> bizOperations = OperationTransformer.doTransformToBizOperation(\n            \"nameA:vA:activated;nameA:vB:deactivated;nameB:vA:activated;nameB:vB:deactivated\",\n            mockBizState());\n        Assert.assertEquals(1, bizOperations.size());\n        Assert.assertTrue(bizOperations.contains(BizOperation.createBizOperation()\n            .setBizName(\"nameB\").setBizVersion(\"vB\")\n            .setOperationType(BizOperation.OperationType.INSTALL)));\n    }\n\n    @Test\n    public void testTransformInstallAndUninstallOperation() {\n        List<BizOperation> bizOperations = OperationTransformer.doTransformToBizOperation(\n            \"nameA:vA:activated;nameB:vA:activated;nameB:vB:deactivated\", mockBizState());\n        Assert.assertEquals(2, bizOperations.size());\n        Assert.assertTrue(bizOperations.contains(BizOperation.createBizOperation()\n            .setBizName(\"nameA\").setBizVersion(\"vB\")\n            .setOperationType(BizOperation.OperationType.UNINSTALL)));\n        Assert.assertTrue(bizOperations.contains(BizOperation.createBizOperation()\n            .setBizName(\"nameB\").setBizVersion(\"vB\")\n            .setOperationType(BizOperation.OperationType.INSTALL)));\n    }\n\n    @Test\n    public void testTransformSwitchOperation() {\n        List<BizOperation> bizOperations = OperationTransformer.doTransformToBizOperation(\n            \"nameA:vA:deactivated;nameA:vB:activated;nameB:vA:activated\", mockBizState());\n        Assert.assertEquals(1, bizOperations.size());\n        Assert.assertTrue(bizOperations.contains(BizOperation.createBizOperation()\n            .setBizName(\"nameA\").setBizVersion(\"vB\")\n            .setOperationType(BizOperation.OperationType.SWITCH)));\n    }\n\n    @Test\n    public void testTransformInstallAndSwitch() {\n        List<BizOperation> bizOperations = OperationTransformer.doTransformToBizOperation(\n            \"nameA:vA:activated;nameA:vB:deactivated;nameB:vA:deactivated;nameB:vB:activated\",\n            mockBizState());\n        Assert.assertEquals(2, bizOperations.size());\n        Assert.assertTrue(bizOperations.contains(BizOperation.createBizOperation()\n            .setBizName(\"nameB\").setBizVersion(\"vB\")\n            .setOperationType(BizOperation.OperationType.INSTALL)));\n        Assert.assertTrue(bizOperations.contains(BizOperation.createBizOperation()\n            .setBizName(\"nameB\").setBizVersion(\"vB\")\n            .setOperationType(BizOperation.OperationType.SWITCH)));\n    }\n\n    @Test\n    public void testTransformUninstallAndSwitch() {\n        List<BizOperation> bizOperations = OperationTransformer.doTransformToBizOperation(\n            \"nameA:vB:activated;nameB:vA:activated\", mockBizState());\n        Assert.assertEquals(2, bizOperations.size());\n        Assert.assertTrue(bizOperations.contains(BizOperation.createBizOperation()\n            .setBizName(\"nameA\").setBizVersion(\"vA\")\n            .setOperationType(BizOperation.OperationType.UNINSTALL)));\n        Assert.assertTrue(bizOperations.contains(BizOperation.createBizOperation()\n            .setBizName(\"nameA\").setBizVersion(\"vB\")\n            .setOperationType(BizOperation.OperationType.SWITCH)));\n    }\n\n    @Test\n    public void testTransformUninstallAndInstall() {\n        List<BizOperation> bizOperations = OperationTransformer.doTransformToBizOperation(\n            \"nameA:vA:activated;nameA:vB:deactivated;nameB:vB:activated\", mockBizState());\n        Assert.assertEquals(3, bizOperations.size());\n        Assert.assertTrue(bizOperations.contains(BizOperation.createBizOperation()\n            .setBizName(\"nameB\").setBizVersion(\"vB\")\n            .setOperationType(BizOperation.OperationType.INSTALL)));\n        Assert.assertTrue(bizOperations.contains(BizOperation.createBizOperation()\n            .setBizName(\"nameB\").setBizVersion(\"vB\")\n            .setOperationType(BizOperation.OperationType.SWITCH)));\n        Assert.assertTrue(bizOperations.contains(BizOperation.createBizOperation()\n            .setBizName(\"nameB\").setBizVersion(\"vA\")\n            .setOperationType(BizOperation.OperationType.UNINSTALL)));\n    }\n\n    @Test\n    public void testTransformUnstableState() {\n        Exception ex = null;\n        try {\n            List<Biz> bizList = new ArrayList<>();\n            Biz biz1 = Mockito.mock(Biz.class);\n            Biz biz2 = Mockito.mock(Biz.class);\n            Biz biz3 = Mockito.mock(Biz.class);\n            bizList.add(biz1);\n            bizList.add(biz2);\n            bizList.add(biz3);\n            when(biz1.getBizName()).thenReturn(\"nameA\");\n            when(biz1.getBizVersion()).thenReturn(\"vA\");\n            when(biz1.getBizState()).thenReturn(BizState.ACTIVATED);\n\n            when(biz1.getBizName()).thenReturn(\"nameA\");\n            when(biz1.getBizVersion()).thenReturn(\"vB\");\n            when(biz1.getBizState()).thenReturn(BizState.RESOLVED);\n\n            when(biz1.getBizName()).thenReturn(\"nameB\");\n            when(biz1.getBizVersion()).thenReturn(\"vA\");\n            when(biz1.getBizState()).thenReturn(BizState.ACTIVATED);\n\n            PluginContext pluginContext = Mockito.mock(PluginContext.class);\n            ServiceReference serviceReference = Mockito.mock(ServiceReference.class);\n            BizManagerService bizManagerService = Mockito.mock(BizManagerService.class);\n            when(serviceReference.getService()).thenReturn(bizManagerService);\n            when(pluginContext.referenceService(any(Class.class))).thenReturn(serviceReference);\n            when(bizManagerService.getBizInOrder()).thenReturn(bizList);\n            OperationTransformer.transformToBizOperation(\n                \"nameA:vA:activated;nameA:vB:deactivated;nameB:vB:activated\", pluginContext);\n        } catch (Exception e) {\n            ex = e;\n        }\n        Assert.assertNotNull(ex);\n        Assert.assertTrue(ex.getMessage().contains(\"Exist illegal biz\"));\n    }\n\n    @Test\n    public void testTransformUnChangedState() {\n        List<BizOperation> bizOperations = OperationTransformer.doTransformToBizOperation(\n            \"nameA:vA:activated;nameA:vB:deactivated;nameB:vA:activated\", mockBizState());\n        Assert.assertEquals(0, bizOperations.size());\n    }\n\n    private Map<String, Map<String, BizState>> mockBizState() {\n        Map<String, Map<String, BizState>> bizStateMap = new LinkedHashMap<String, Map<String, BizState>>();\n\n        Map<String, BizState> bizAVersionStateMap = new HashMap<String, BizState>();\n        bizAVersionStateMap.put(\"vA\", BizState.ACTIVATED);\n        bizAVersionStateMap.put(\"vB\", BizState.DEACTIVATED);\n        bizStateMap.put(\"nameA\", bizAVersionStateMap);\n\n        Map<String, BizState> bizBVersionStateMap = new HashMap<String, BizState>();\n        bizBVersionStateMap.put(\"vA\", BizState.ACTIVATED);\n        bizStateMap.put(\"nameB\", bizBVersionStateMap);\n\n        return bizStateMap;\n    }\n}\n"
  },
  {
    "path": "sofa-ark-plugin/config-ark-plugin/src/test/java/com/alipay/sofa/ark/config/ZookeeperConfiguratorTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.config;\n\nimport com.alipay.sofa.ark.config.zk.ZookeeperConfigurator;\nimport com.alipay.sofa.ark.spi.constant.Constants;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.Map;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class ZookeeperConfiguratorTest {\n\n    @Test\n    public void testInvalidZookeeperAddress() {\n        Throwable throwable = null;\n        try {\n            ZookeeperConfigurator.buildConfig(\"xxx\");\n        } catch (Throwable t) {\n            throwable = t;\n        }\n        Assert.assertNotNull(throwable);\n        Assert.assertTrue(throwable.getMessage().equals(\n            \"Zookeeper config should start with 'zookeeper://'.\"));\n    }\n\n    @Test\n    public void testRegistryConfig() {\n        RegistryConfig registryConfig = ZookeeperConfigurator\n            .buildConfig(\"zookeeper://localhost:2181?k1=v1&k2=v2\");\n        Assert.assertEquals(Constants.CONFIG_PROTOCOL_ZOOKEEPER, registryConfig.getProtocol());\n        Assert.assertEquals(\"localhost:2181\", registryConfig.getAddress());\n        Assert.assertEquals(2, registryConfig.getParameters().size());\n        Assert.assertEquals(\"v1\", registryConfig.getParameter(\"k1\"));\n        Assert.assertEquals(\"v2\", registryConfig.getParameter(\"k2\"));\n    }\n\n    @Test\n    public void testZookeeperAddress() {\n        String address = ZookeeperConfigurator\n            .parseAddress(\"zookeeper://localhost:2181?k1=v1&k2=v2\");\n        Assert.assertEquals(\"localhost:2181\", address);\n    }\n\n    @Test\n    public void testZookeeperParameter() {\n        Map<String, String> parameters = ZookeeperConfigurator\n            .parseParam(\"zookeeper://localhost:2181?k1=v1&k2=v2\");\n        Assert.assertEquals(2, parameters.size());\n        Assert.assertEquals(\"v1\", parameters.get(\"k1\"));\n        Assert.assertEquals(\"v2\", parameters.get(\"k2\"));\n    }\n\n    @Test\n    public void testInvalidZookeeperParameter() {\n        Throwable throwable = null;\n        try {\n            ZookeeperConfigurator.parseParam(\"zookeeper://localhost:2181?k1\");\n        } catch (Throwable t) {\n            throwable = t;\n        }\n        Assert.assertNotNull(throwable);\n        Assert.assertTrue(throwable.getMessage().contains(\"invalid format\"));\n    }\n}"
  },
  {
    "path": "sofa-ark-plugin/netty-ark-plugin/CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Module Overview\n\n**Artifact ID**: `netty-ark-plugin`\n**Package**: `com.alipay.sofa.ark.netty`\n\nThis is a built-in Ark Plugin that provides Netty-based telnet server support for runtime management.\n\n## Purpose\n\n- Enable remote telnet access to Ark container\n- Provide command-line interface for runtime operations\n- Support management commands: biz, plugin, info queries\n\n## Key Features\n\n### Telnet Server\n- Netty-based telnet server implementation\n- Listen on configurable port\n- Command parsing and execution\n\n### Supported Commands\n- `biz` - Manage business modules (install, uninstall, switch, check)\n- `plugin` - Manage plugins (check, install)\n- `info` - Query container and module information\n- `help` - Show available commands\n\n## Usage\n\nAdd plugin dependency:\n```xml\n<dependency>\n    <groupId>com.alipay.sofa</groupId>\n    <artifactId>netty-ark-plugin</artifactId>\n    <classifier>ark-plugin</classifier>\n</dependency>\n```\n\nConnect via telnet:\n```bash\ntelnet localhost 1234\n```\n\n## Dependencies\n\n- `sofa-ark-spi` - Service interfaces\n- `sofa-ark-api` - API for operations\n- `netty-all` - Netty networking framework\n\n## Used By\n\n- Applications requiring remote management capabilities\n- Operations teams for runtime monitoring"
  },
  {
    "path": "sofa-ark-plugin/netty-ark-plugin/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <parent>\n        <artifactId>sofa-ark-bom</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n        <relativePath>../../sofa-ark-bom</relativePath>\n    </parent>\n\n    <artifactId>netty-ark-plugin</artifactId>\n    <name>${project.groupId}:${project.artifactId}</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-webflux</artifactId>\n            <version>${spring.boot.version}</version>\n            <scope>provided</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>io.projectreactor.netty</groupId>\n            <artifactId>reactor-netty</artifactId>\n            <version>${reactor-netty.version}</version>\n            <scope>provided</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-common</artifactId>\n            <version>${project.version}</version>\n            <scope>compile</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>sofa-ark-plugin-maven-plugin</artifactId>\n                <version>${project.version}</version>\n                <executions>\n                    <execution>\n                        <id>default-cli</id>\n                        <goals>\n                            <goal>ark-plugin</goal>\n                        </goals>\n\n                        <configuration>\n                            <activator>com.alipay.sofa.ark.NettyPluginActivator</activator>\n                            <excludes>\n                                <exclude>*:*:*</exclude>\n                                <exclude>com.alipay.sofa:netty-ark-plugin</exclude>\n                            </excludes>\n                        </configuration>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "sofa-ark-plugin/netty-ark-plugin/src/main/java/com/alipay/sofa/ark/NettyPluginActivator.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark;\n\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.netty.EmbeddedServerServiceImpl;\nimport com.alipay.sofa.ark.spi.model.PluginContext;\nimport com.alipay.sofa.ark.spi.service.PluginActivator;\nimport com.alipay.sofa.ark.spi.web.EmbeddedServerService;\nimport reactor.netty.http.server.HttpServer;\n\npublic class NettyPluginActivator implements PluginActivator {\n\n    private EmbeddedServerService embeddedNettyService = new EmbeddedServerServiceImpl();\n\n    @Override\n    public void start(PluginContext context) {\n        context.publishService(EmbeddedServerService.class, embeddedNettyService);\n    }\n\n    @Override\n    public void stop(PluginContext context) {\n        for (Object o : embeddedNettyService) {\n            if (!(o instanceof HttpServer)) {\n                continue;\n            }\n            HttpServer webServer = (HttpServer) o;\n            try {\n                //webServer.stop();\n            } catch (Exception ex) {\n                ArkLoggerFactory.getDefaultLogger().error(\"Unable to stop embedded Netty\", ex);\n            }\n        }\n    }\n}"
  },
  {
    "path": "sofa-ark-plugin/netty-ark-plugin/src/main/java/com/alipay/sofa/ark/netty/ArkNettyIdentification.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.netty;\n\npublic class ArkNettyIdentification {\n\n}\n"
  },
  {
    "path": "sofa-ark-plugin/netty-ark-plugin/src/main/java/com/alipay/sofa/ark/netty/EmbeddedServerServiceImpl.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.netty;\n\nimport com.alipay.sofa.ark.spi.web.AbstractEmbeddedServerService;\nimport reactor.netty.http.server.HttpServer;\n\npublic class EmbeddedServerServiceImpl extends AbstractEmbeddedServerService<HttpServer> {\n\n}"
  },
  {
    "path": "sofa-ark-plugin/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>sofa-ark-bom</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n        <relativePath>../sofa-ark-bom</relativePath>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>sofa-ark-plugin</artifactId>\n    <packaging>pom</packaging>\n    <name>${project.groupId}:${project.artifactId}</name>\n\n    <modules>\n        <module>config-ark-plugin</module>\n        <module>netty-ark-plugin</module>\n        <module>web-ark-plugin</module>\n    </modules>\n</project>\n"
  },
  {
    "path": "sofa-ark-plugin/web-ark-plugin/CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Module Overview\n\n**Artifact ID**: `web-ark-plugin`\n**Package**: `com.alipay.sofa.ark.web`\n\nThis is a built-in Ark Plugin that provides embedded web server support for business modules.\n\n## Purpose\n\n- Enable multiple web applications in different Biz modules\n- Provide embedded Tomcat server support\n- Handle web context isolation between Biz modules\n\n## Key Features\n\n### Embedded Server\n- Starts embedded Tomcat server\n- Each Biz can have its own web context path\n- Supports Spring Boot web applications\n\n### Web Context Isolation\n- Different Biz modules can serve at different context paths\n- Static resource isolation\n- Session isolation support\n\n## Usage\n\nAdd plugin dependency:\n```xml\n<dependency>\n    <groupId>com.alipay.sofa</groupId>\n    <artifactId>web-ark-plugin</artifactId>\n    <classifier>ark-plugin</classifier>\n</dependency>\n```\n\nConfigure web context in Maven plugin:\n```xml\n<configuration>\n    <webContextPath>/my-app</webContextPath>\n</configuration>\n```\n\n## Dependencies\n\n- `sofa-ark-spi` - Service interfaces\n- `sofa-ark-api` - API for operations\n- Tomcat embedded (provided scope)\n- Spring Boot (provided scope)\n\n## Used By\n\n- Web applications running on SOFAArk\n- Multiple web applications merged into single deployment"
  },
  {
    "path": "sofa-ark-plugin/web-ark-plugin/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>sofa-ark-bom</artifactId>\n        <groupId>com.alipay.sofa</groupId>\n        <version>${sofa.ark.version}</version>\n        <relativePath>../../sofa-ark-bom</relativePath>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>web-ark-plugin</artifactId>\n    <name>${project.groupId}:${project.artifactId}</name>\n\n<!--    <properties>-->\n<!--        <spring.boot.version>1.5.22.RELEASE</spring.boot.version>-->\n<!--    </properties>-->\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot</artifactId>\n            <version>${spring.boot.version}</version>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-autoconfigure</artifactId>\n            <version>${spring.boot.version}</version>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-tomcat</artifactId>\n            <version>${spring.boot.version}</version>\n            <scope>provided</scope>\n        </dependency>\n\n\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>sofa-ark-common</artifactId>\n        </dependency>\n\n        <!-- Test Dependencies -->\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-inline</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <version>${spring.boot.version}</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>com.alipay.sofa</groupId>\n                <artifactId>sofa-ark-plugin-maven-plugin</artifactId>\n                <version>${project.version}</version>\n                <executions>\n                    <execution>\n                        <id>default-cli</id>\n                        <goals>\n                            <goal>ark-plugin</goal>\n                        </goals>\n\n                        <configuration>\n                            <activator>com.alipay.sofa.ark.web.embed.WebPluginActivator</activator>\n                            <exported>\n                                <classes>\n                                    <class>com.alipay.sofa.ark.web.embed.tomcat.SwitchClassLoaderAutoConfiguration</class>\n                                </classes>\n                            </exported>\n                            <excludes>\n                                <exclude>*:*:*</exclude>\n                                <exclude>com.alipay.sofa:web-ark-plugin</exclude>\n                            </excludes>\n                        </configuration>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <dependencies>\n                    <dependency>\n                        <groupId>org.apache.maven.surefire</groupId>\n                        <artifactId>surefire-junit47</artifactId>\n                        <version>${surefire.version}</version>\n                    </dependency>\n                </dependencies>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "sofa-ark-plugin/web-ark-plugin/src/main/java/com/alipay/sofa/ark/web/embed/WebPluginActivator.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.web.embed;\n\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport com.alipay.sofa.ark.spi.model.PluginContext;\nimport com.alipay.sofa.ark.spi.service.PluginActivator;\nimport com.alipay.sofa.ark.spi.web.EmbeddedServerService;\nimport com.alipay.sofa.ark.web.embed.tomcat.EmbeddedServerServiceImpl;\nimport org.apache.catalina.startup.Tomcat;\n\n/**\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class WebPluginActivator implements PluginActivator {\n\n    EmbeddedServerService embeddedServerService = new EmbeddedServerServiceImpl();\n\n    @Override\n    public void start(PluginContext context) {\n        context.publishService(EmbeddedServerService.class, embeddedServerService);\n    }\n\n    @Override\n    public void stop(PluginContext context) {\n        for (Object o : embeddedServerService) {\n            if (!(o instanceof Tomcat)) {\n                continue;\n            }\n            Tomcat webServer = (Tomcat) o;\n            try {\n                webServer.destroy();\n            } catch (Exception ex) {\n                ArkLoggerFactory.getDefaultLogger().warn(\"Unable to stop embedded Tomcat\", ex);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-plugin/web-ark-plugin/src/main/java/com/alipay/sofa/ark/web/embed/tomcat/ArkTomcatEmbeddedWebappClassLoader.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.web.embed.tomcat;\n\nimport com.alipay.sofa.ark.common.log.ArkLoggerFactory;\nimport org.apache.catalina.loader.ParallelWebappClassLoader;\nimport org.slf4j.Logger;\n\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.Collections;\nimport java.util.Enumeration;\n\n/**\n * Extension of Tomcat's {@link ParallelWebappClassLoader} that does not consider the\n * {@link ClassLoader#getSystemClassLoader() system classloader}. This is required to\n * ensure that any custom context class loader is always used (as is the case with some\n * executable archives).\n *\n * There are two case to initialize tomcat for multi biz model:\n *\n * 1. When included this Tomcat ClassLoader by\n * {@link com.alipay.sofa.ark.web.embed.tomcat.ArkTomcatEmbeddedWebappClassLoader},\n * this will ensure there should always be one tomcat instance with only one port.\n * In this case, each module use same web port, must define different web context path.\n *\n *   --------------\n *  │ Biz Module A │\\\n *   --------------  \\\n *                    \\----→ Tomcat 1: Port 1\n *   --------------   /\n *  │ Biz Module B │/\n *   --------------\n *\n * 2. If not included, then each biz module with mvc will create a tomcat,\n * In this case, each module can define any web context path, but must define different web server port.\n *\n *   --------------\n *  │ Biz Module A │ ----→ Tomcat 1: Port 1\n *   --------------\n *\n *   --------------\n *  │ Biz Module B │ ----→ Tomcat 2: Port2\n *   --------------\n *\n * Actually, the mostly used in prod env is the first case, so we need to include a web plugin.\n *\n * @author qilong.zql\n * @author Phillip Webb\n * @since 0.6.0\n */\npublic class ArkTomcatEmbeddedWebappClassLoader extends ParallelWebappClassLoader {\n    private static final Logger LOGGER = ArkLoggerFactory\n                                           .getLogger(ArkTomcatEmbeddedWebappClassLoader.class);\n\n    static {\n        ClassLoader.registerAsParallelCapable();\n    }\n\n    public ArkTomcatEmbeddedWebappClassLoader() {\n    }\n\n    /**\n     * NOTE: super web class loader will set parent to systemClassLoader if 'parent' param value is null.\n     * So 'parent' class loader usually would not be null.\n     *\n     * @param parent\n     */\n    public ArkTomcatEmbeddedWebappClassLoader(ClassLoader parent) {\n        super(parent);\n    }\n\n    @Override\n    public URL findResource(String name) {\n        return null;\n    }\n\n    @Override\n    public Enumeration<URL> findResources(String name) throws IOException {\n        return Collections.emptyEnumeration();\n    }\n\n    @Override\n    public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {\n        synchronized (getClassLoadingLock(name)) {\n            Class<?> result = findExistingLoadedClass(name);\n            result = (result != null) ? result : doLoadClass(name);\n            if (result == null) {\n                throw new ClassNotFoundException(name);\n            }\n            return resolveIfNecessary(result, resolve);\n        }\n    }\n\n    /**\n     * We try to load class from cache in current class loader chain.\n     *\n     * @param name\n     * @return\n     */\n    private Class<?> findExistingLoadedClass(String name) {\n        Class<?> resultClass = findLoadedClass0(name);\n        resultClass = (resultClass != null) ? resultClass : findLoadedClass(name);\n        return resultClass;\n    }\n\n    /**\n     * doLoadClass is used to handle class not found from cache, so doLoadClass try to load class from class loader chain and put it into class cache.\n     *\n     * @param name\n     * @return\n     * @throws ClassNotFoundException\n     */\n    private Class<?> doLoadClass(String name) throws ClassNotFoundException {\n        checkPackageAccess(name);\n        // Note: set delegate TRUE will load class from parent class loader first, otherwise from current class loader first.\n        // If class is javax or apache class, filter will return true, otherwise filter return false.\n        if ((this.delegate || filter(name, true))) {\n            Class<?> result = loadFromParent(name);\n            return (result != null) ? result : findClassIgnoringNotFound(name);\n        }\n        Class<?> result = findClassIgnoringNotFound(name);\n        return (result != null) ? result : loadFromParent(name);\n    }\n\n    private Class<?> resolveIfNecessary(Class<?> resultClass, boolean resolve) {\n        if (resolve) {\n            resolveClass(resultClass);\n        }\n        return (resultClass);\n    }\n\n    @Override\n    protected void addURL(URL url) {\n        // Ignore URLs added by the Tomcat 8 implementation (see gh-919)\n        if (LOGGER.isTraceEnabled()) {\n            LOGGER.trace(\"Ignoring request to add \" + url + \" to the tomcat classloader\");\n        }\n    }\n\n    private Class<?> loadFromParent(String name) {\n        if (this.parent == null) {\n            return null;\n        }\n        try {\n            return Class.forName(name, false, this.parent);\n        } catch (ClassNotFoundException ex) {\n            return null;\n        }\n    }\n\n    /**\n     * Invoke findClass in tomcat web class loader.\n     * Note: After Tomcat stopped, Tomcat will construct a IllegalStateException, and then put it into a ClassNotFoundException.\n     * SOFAArk will always treat this inner IllegalStateException as a ClassNotFoundException and return null,\n     * which means this Class was not found and then find this class through parent class loader (e.g. ApplicationClassLoader).\n     *\n     * @param name\n     * @return\n     */\n    private Class<?> findClassIgnoringNotFound(String name) {\n        try {\n            return findClass(name);\n        } catch (ClassNotFoundException ex) {\n            return null;\n        }\n    }\n\n    void checkPackageAccess(String name) throws ClassNotFoundException {\n        if (this.securityManager != null && name.lastIndexOf('.') >= 0) {\n            try {\n                this.securityManager.checkPackageAccess(name.substring(0, name.lastIndexOf('.')));\n            } catch (SecurityException ex) {\n                throw new ClassNotFoundException(\"Security Violation, attempt to use \"\n                                                 + \"Restricted Class: \" + name, ex);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-plugin/web-ark-plugin/src/main/java/com/alipay/sofa/ark/web/embed/tomcat/EmbeddedServerServiceImpl.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.web.embed.tomcat;\n\nimport com.alipay.sofa.ark.spi.web.AbstractEmbeddedServerService;\nimport org.apache.catalina.startup.Tomcat;\n\n/**\n * This implementation would be published as ark service.\n *\n * @author qilong.zql\n * @since 0.6.0\n */\npublic class EmbeddedServerServiceImpl extends AbstractEmbeddedServerService<Tomcat> {\n}"
  },
  {
    "path": "sofa-ark-plugin/web-ark-plugin/src/main/java/com/alipay/sofa/ark/web/embed/tomcat/SwitchClassLoaderAutoConfiguration.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.web.embed.tomcat;\n\nimport org.apache.catalina.startup.Tomcat;\nimport org.apache.coyote.UpgradeProtocol;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.annotation.Order;\n\nimport javax.servlet.Filter;\nimport javax.servlet.Servlet;\n\n/**\n * switch classloader to bizClassLoader in pre of web request handler\n * fix https://github.com/koupleless/koupleless/issues/212\n *\n * please notice: this AutoConfiguration should been loaded by both base and biz,\n * so the class name should be different in this plugin\n *\n * @author lvjing2\n * @since 2.2.10\n */\n@Configuration\npublic class SwitchClassLoaderAutoConfiguration {\n\n    @Bean(name = \"switchClassLoaderFilter\")\n    @Order(10)\n    @ConditionalOnClass(value = { Servlet.class, Tomcat.class, UpgradeProtocol.class }, name = {\n            \"com.alipay.sofa.ark.springboot1.web.SwitchClassLoaderFilter\",\n            \"com.alipay.sofa.ark.web.embed.tomcat.ArkTomcatEmbeddedWebappClassLoader\",\n            \"org.springframework.boot.web.servlet.server.ServletWebServerFactory\",\n            \"org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration\" })\n    @ConditionalOnMissingClass(\"org.springframework.boot.web.embedded.tomcat.TomcatProtocolHandlerCustomizer\")\n    public Filter switchClassLoaderFilter1() {\n        try {\n            Class<?> clazz = Class\n                .forName(\"com.alipay.sofa.ark.springboot1.web.SwitchClassLoaderFilter\");\n            return (Filter) clazz.newInstance();\n        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Bean(name = \"switchClassLoaderFilter\")\n    @Order(10)\n    @ConditionalOnClass(value = { Servlet.class, Tomcat.class, UpgradeProtocol.class }, name = {\n            \"com.alipay.sofa.ark.springboot2.web.SwitchClassLoaderFilter\",\n            \"com.alipay.sofa.ark.web.embed.tomcat.ArkTomcatEmbeddedWebappClassLoader\",\n            \"org.springframework.boot.web.servlet.server.ServletWebServerFactory\",\n            \"org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration\",\n            \"org.springframework.boot.web.embedded.tomcat.TomcatProtocolHandlerCustomizer\" })\n    public Filter switchClassLoaderFilter2() {\n        try {\n            Class<?> clazz = Class\n                .forName(\"com.alipay.sofa.ark.springboot2.web.SwitchClassLoaderFilter\");\n            return (Filter) clazz.newInstance();\n        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {\n            throw new RuntimeException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "sofa-ark-plugin/web-ark-plugin/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
    "content": "com.alipay.sofa.ark.web.embed.tomcat.SwitchClassLoaderAutoConfiguration\n"
  },
  {
    "path": "sofa-ark-plugin/web-ark-plugin/src/main/resources/META-INF/spring.factories",
    "content": "org.springframework.boot.autoconfigure.EnableAutoConfiguration=\\\n  com.alipay.sofa.ark.web.embed.tomcat.SwitchClassLoaderAutoConfiguration\n"
  },
  {
    "path": "sofa-ark-plugin/web-ark-plugin/src/test/java/com/alipay/sofa/ark/web/embed/WebPluginActivatorTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.web.embed;\n\nimport com.alipay.sofa.ark.spi.model.PluginContext;\nimport com.alipay.sofa.ark.spi.web.EmbeddedServerService;\nimport org.apache.catalina.LifecycleException;\nimport org.apache.catalina.startup.Tomcat;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport static org.mockito.Mockito.*;\n\npublic class WebPluginActivatorTest {\n\n    private WebPluginActivator    webPluginActivator            = new WebPluginActivator();\n\n    private EmbeddedServerService originalEmbeddedServerService = webPluginActivator.embeddedServerService;\n\n    @Before\n    public void setUp() {\n    }\n\n    @After\n    public void tearDown() {\n        webPluginActivator.embeddedServerService = originalEmbeddedServerService;\n    }\n\n    @Test\n    public void testStartAndStop() throws LifecycleException {\n\n        EmbeddedServerService embeddedServerService = mock(EmbeddedServerService.class);\n        webPluginActivator.embeddedServerService = embeddedServerService;\n\n        PluginContext pluginContext = mock(PluginContext.class);\n        webPluginActivator.start(pluginContext);\n        verify(pluginContext, times(1)).publishService(EmbeddedServerService.class,\n            embeddedServerService);\n\n        Tomcat tomcat = mock(Tomcat.class);\n        List<Tomcat> servers = Arrays.asList(tomcat);\n        when(embeddedServerService.iterator()).then(var -> servers.iterator());\n\n        webPluginActivator.stop(pluginContext);\n        verify(embeddedServerService, times(1)).iterator();\n        verify(tomcat, times(1)).destroy();\n\n        doThrow(new LifecycleException()).when(tomcat).destroy();\n        webPluginActivator.stop(pluginContext);\n        verify(embeddedServerService, times(2)).iterator();\n        verify(tomcat, times(2)).destroy();\n\n        when(embeddedServerService.iterator()).thenReturn(Arrays.asList(new Object()).iterator());\n        webPluginActivator.stop(pluginContext);\n        verify(embeddedServerService, times(3)).iterator();\n        verify(tomcat, times(2)).destroy();\n    }\n}\n"
  },
  {
    "path": "sofa-ark-plugin/web-ark-plugin/src/test/java/com/alipay/sofa/ark/web/embed/tomcat/ArkTomcatEmbeddedWebappClassLoaderTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.web.embed.tomcat;\n\nimport org.apache.catalina.LifecycleException;\nimport org.apache.catalina.WebResource;\nimport org.apache.catalina.WebResourceRoot;\nimport org.apache.catalina.loader.ResourceEntry;\nimport org.apache.catalina.loader.WebappClassLoaderBase;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.IOException;\nimport java.lang.reflect.Field;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport static org.apache.catalina.LifecycleState.STARTED;\nimport static org.junit.Assert.*;\nimport static org.mockito.Mockito.*;\n\npublic class ArkTomcatEmbeddedWebappClassLoaderTest {\n\n    private ArkTomcatEmbeddedWebappClassLoader arkTomcatEmbeddedWebappClassLoader = new ArkTomcatEmbeddedWebappClassLoader();\n\n    public ArkTomcatEmbeddedWebappClassLoaderTest() {\n    }\n\n    @Before\n    public void setUp() {\n    }\n\n    @After\n    public void tearDown() {\n    }\n\n    @Test\n    public void testLoadClass() throws ClassNotFoundException, LifecycleException {\n\n        // 1) Test load class from current class loader's parent.\n        assertEquals(String.class,\n            arkTomcatEmbeddedWebappClassLoader.loadClass(String.class.getName(), true));\n\n        // 2) Test load class from current class loader's parent, which is null.\n        ArkTomcatEmbeddedWebappClassLoader arkTomcatEmbeddedWebappClassLoader2 = new ArkTomcatEmbeddedWebappClassLoader();\n        try {\n            Field field = WebappClassLoaderBase.class.getDeclaredField(\"parent\");\n            field.setAccessible(true);\n            field.set(arkTomcatEmbeddedWebappClassLoader2, null);\n            arkTomcatEmbeddedWebappClassLoader2.loadClass(String.class.getName(), true);\n            assertFalse(true);\n        } catch (Exception e) {\n            if (e instanceof ClassNotFoundException) {\n                // we expected ClassNotFoundException is thrown here, so we do nothing.\n            } else {\n                throw new RuntimeException(e);\n            }\n        }\n\n        // 3) Test load class without web class loader class cache.\n        try {\n            Field field = WebappClassLoaderBase.class.getDeclaredField(\"state\");\n            field.setAccessible(true);\n            field.set(arkTomcatEmbeddedWebappClassLoader, STARTED);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n\n        WebResourceRoot webResourceRoot = mock(WebResourceRoot.class);\n        WebResource webResource = mock(WebResource.class);\n        String path = \"/\" + this.getClass().getName().replace('.', '/') + \".class\";\n        when(webResourceRoot.getClassLoaderResource(path)).thenReturn(webResource);\n        arkTomcatEmbeddedWebappClassLoader.setResources(webResourceRoot);\n\n        assertEquals(this.getClass(),\n            arkTomcatEmbeddedWebappClassLoader.loadClass(this.getClass().getName(), true));\n        verify(webResourceRoot, times(1)).getClassLoaderResource(path);\n        // note: exists always return false to mock resource not found and fallback to parent class loader.\n        verify(webResource, times(1)).exists();\n\n        // 4) Test load class from web class loader class cache.\n        ConcurrentHashMap<String, ResourceEntry> resourceEntries = new ConcurrentHashMap<>();\n        path = \"/a/b.class\";\n        try {\n            Field field = WebappClassLoaderBase.class.getDeclaredField(\"resourceEntries\");\n            field.setAccessible(true);\n            ResourceEntry resourceEntry = new ResourceEntry();\n            resourceEntry.loadedClass = this.getClass();\n            resourceEntries.put(path, resourceEntry);\n            field.set(arkTomcatEmbeddedWebappClassLoader, resourceEntries);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n\n        assertEquals(this.getClass(), arkTomcatEmbeddedWebappClassLoader.loadClass(\"a.b\", true));\n\n        // 5) Test load class with delegate TRUE.\n        arkTomcatEmbeddedWebappClassLoader.setDelegate(true);\n        assertEquals(String.class,\n            arkTomcatEmbeddedWebappClassLoader.loadClass(String.class.getName(), true));\n        assertEquals(this.getClass(), arkTomcatEmbeddedWebappClassLoader.loadClass(\"a.b\", false)); // cover resolve false\n\n        // 6) Test load apache class.\n        arkTomcatEmbeddedWebappClassLoader.setDelegate(false);\n        assertEquals(WebappClassLoaderBase.class, arkTomcatEmbeddedWebappClassLoader.loadClass(\n            WebappClassLoaderBase.class.getName(), true));\n        ResourceEntry resourceEntry = new ResourceEntry();\n        resourceEntry.loadedClass = String.class;\n        resourceEntries.put(\"/org/apache.class\", resourceEntry);\n        assertEquals(String.class,\n            arkTomcatEmbeddedWebappClassLoader.loadClass(\"org.apache\", false)); // cover resolve false\n    }\n\n    @Test(expected = ClassNotFoundException.class)\n    public void testLoadClassWithNotFound() throws ClassNotFoundException {\n        assertEquals(this.getClass(), arkTomcatEmbeddedWebappClassLoader.loadClass(\"a.b\", true));\n    }\n\n    @Test\n    public void testOtherMethods() throws IOException, ClassNotFoundException {\n\n        new ArkTomcatEmbeddedWebappClassLoader(this.getClass().getClassLoader());\n\n        assertNull(arkTomcatEmbeddedWebappClassLoader.findResource(\"aaa\"));\n\n        assertEquals(false, arkTomcatEmbeddedWebappClassLoader.findResources(\"aaa\")\n            .hasMoreElements());\n\n        arkTomcatEmbeddedWebappClassLoader.addURL(null);\n\n        try {\n            Field field = WebappClassLoaderBase.class.getDeclaredField(\"securityManager\");\n            field.setAccessible(true);\n            field.set(arkTomcatEmbeddedWebappClassLoader, new SecurityManager());\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n        arkTomcatEmbeddedWebappClassLoader.checkPackageAccess(String.class.getName());\n        arkTomcatEmbeddedWebappClassLoader.checkPackageAccess(\"java\"); // cover wrong class name\n    }\n\n    @Test(expected = ClassNotFoundException.class)\n    public void testCheckPackageAccessFailed() throws IOException, ClassNotFoundException {\n\n        SecurityManager securityManager = mock(SecurityManager.class);\n        doThrow(new SecurityException()).when(securityManager).checkPackageAccess(\"a\");\n\n        try {\n            Field field = WebappClassLoaderBase.class.getDeclaredField(\"securityManager\");\n            field.setAccessible(true);\n            field.set(arkTomcatEmbeddedWebappClassLoader, securityManager);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n\n        arkTomcatEmbeddedWebappClassLoader.checkPackageAccess(\"a.b\");\n    }\n}\n"
  },
  {
    "path": "sofa-ark-plugin/web-ark-plugin/src/test/java/com/alipay/sofa/ark/web/embed/tomcat/EmbeddedServerServiceImplTest.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alipay.sofa.ark.web.embed.tomcat;\n\nimport org.apache.catalina.startup.Tomcat;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\n\npublic class EmbeddedServerServiceImplTest {\n\n    private EmbeddedServerServiceImpl embeddedServerServiceImpl = new EmbeddedServerServiceImpl();\n\n    @Before\n    public void setUp() {\n    }\n\n    @After\n    public void tearDown() {\n    }\n\n    @Test\n    public void testPutEmbedServer() {\n        int port = 8080;\n        assertEquals(false, embeddedServerServiceImpl.putEmbedServer(port, null));\n        assertEquals(null, embeddedServerServiceImpl.getEmbedServer(port));\n\n        Tomcat tomcat = new Tomcat();\n        embeddedServerServiceImpl.putEmbedServer(port, tomcat);\n        assertEquals(tomcat, embeddedServerServiceImpl.getEmbedServer(port));\n\n        Tomcat tomcat2 = new Tomcat();\n        embeddedServerServiceImpl.putEmbedServer(port, tomcat2);\n        // should be still old tomcat\n        assertEquals(tomcat, embeddedServerServiceImpl.getEmbedServer(port));\n\n        // New test case for edge case\n        int newPort = 9090;\n        embeddedServerServiceImpl.putEmbedServer(newPort, tomcat2);\n        assertEquals(tomcat2, embeddedServerServiceImpl.getEmbedServer(newPort));\n        assertEquals(tomcat, embeddedServerServiceImpl.getEmbedServer(port));\n\n    }\n}"
  }
]