Skip to content

针对 PluginManager 的一些小修复#346

Merged
MisakaTAT merged 1 commit into
MisakaTAT:mainfrom
qingshu-ui:dev
Sep 16, 2025
Merged

针对 PluginManager 的一些小修复#346
MisakaTAT merged 1 commit into
MisakaTAT:mainfrom
qingshu-ui:dev

Conversation

@qingshu-ui

@qingshu-ui qingshu-ui commented Sep 15, 2025

Copy link
Copy Markdown
Contributor
  1. parseDependencies方法中过滤空字符串:
java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 1
at com.mikuac.shiro.core.PluginManager.isDependencyMissing(PluginManager.java:120) ~[shiro-2.4.8.jar:na]
  1. 使用URLClassLoader的默认实现:双亲委派,避免复杂的依赖加载错误。
  2. 修复isDependencyMissing逻辑

Sourcery 总结

修复 PluginManager 中的依赖解析和检测,并简化插件类加载以使用默认的父级委托模型。

错误修复:

  • 在 parseDependencies 中过滤掉空的依赖项,以防止 ArrayIndexOutOfBoundsException。
  • 修正 isDependencyMissing 逻辑,将依赖项拆分为 group、artifact 和 version,并检查 jar 文件是否存在。

改进:

  • 将自定义插件 ClassLoader 替换为默认的 URLClassLoader,以强制执行标准的父级委托。
Original summary in English

Summary by Sourcery

Fix dependency parsing and detection in PluginManager and simplify plugin class loading to use the default parent-delegation model

Bug Fixes:

  • Filter out empty dependency entries in parseDependencies to prevent ArrayIndexOutOfBoundsException
  • Correct isDependencyMissing logic to split dependencies into group, artifact, and version and check jar existence

Enhancements:

  • Replace custom plugin ClassLoader with default URLClassLoader to enforce standard parent delegation

@sourcery-ai

sourcery-ai Bot commented Sep 15, 2025

Copy link
Copy Markdown
<details>
<summary>审阅者指南(在小型 PR 上折叠)</summary>

## 审阅者指南

该 PR 通过以下方式增强了 PluginManager:防止空的依赖字符串,通过文件系统解析简化依赖存在性检查,并恢复到默认的 URLClassLoader 委托以避免复杂的类加载逻辑。

#### 更新后的 PluginManager 方法的类图

```mermaid
classDiagram
class PluginManager {
    +Set<String> parseDependencies(File jarFile)
    +boolean isDependencyMissing(String groupArtifact)
    +URLClassLoader createPluginClassLoader(List<URL> urls)
}

PluginManager : parseDependencies() filters out empty dependency strings
PluginManager : isDependencyMissing() checks dependency existence via filesystem
PluginManager : createPluginClassLoader() uses default URLClassLoader (parent delegation)

文件级更改

更改 详细信息 文件
在 parseDependencies 中过滤掉空的依赖项条目
  • 在拆分 Dependencies 属性之前添加非空检查
  • 防止空 manifest 导致的 ArrayIndexOutOfBoundsException
src/main/java/com/mikuac/shiro/core/PluginManager.java
改进 isDependencyMissing 逻辑以使用文件存在性检查
  • 使用带限制的 split 来解析 group、artifact、version
  • 在 dependencies 目录下构建 jar 路径
  • 根据文件是否存在而非类加载来返回缺失状态
src/main/java/com/mikuac/shiro/core/PluginManager.java
简化插件类加载器以使用默认的父级委托
  • 移除自定义的 loadClass 和 getResource 覆盖
  • 使用带有父级上下文类加载器的默认 URLClassLoader 构造函数
src/main/java/com/mikuac/shiro/core/PluginManager.java

提示和命令

与 Sourcery 交互

  • 触发新审查: 在拉取请求上评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 从审查评论生成 GitHub issue: 通过回复 Sourcery 的审查评论,让 Sourcery 创建一个 issue。您也可以回复审查评论并加上 @sourcery-ai issue 来创建 issue。
  • 生成拉取请求标题: 随时在拉取请求标题的任何位置写入 @sourcery-ai 来生成标题。您也可以在拉取请求上评论 @sourcery-ai title 来随时(重新)生成标题。
  • 生成拉取请求摘要: 随时在拉取请求正文的任何位置写入 @sourcery-ai summary,以便在您希望的位置生成 PR 摘要。您也可以在拉取请求上评论 @sourcery-ai summary 来随时(重新)生成摘要。
  • 生成审阅者指南: 在拉取请求上评论 @sourcery-ai guide 来随时(重新)生成审阅者指南。
  • 解决所有 Sourcery 评论: 在拉取请求上评论 @sourcery-ai resolve 来解决所有 Sourcery 评论。如果您已经处理了所有评论并且不想再看到它们,这会很有用。
  • 驳回所有 Sourcery 审查: 在拉取请求上评论 @sourcery-ai dismiss 来驳回所有现有的 Sourcery 审查。如果您想重新开始一次新的审查,这会特别有用——别忘了评论 @sourcery-ai review 来触发新的审查!

自定义您的体验

访问您的 仪表板 以:

  • 启用或禁用审查功能,例如 Sourcery 生成的拉取请求摘要、审阅者指南等。
  • 更改审查语言。
  • 添加、移除或编辑自定义审查说明。
  • 调整其他审查设置。

获取帮助

```
Original review guide in English
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

The PR enhances PluginManager by guarding against empty dependency strings, streamlining dependency presence checks via filesystem resolution, and reverting to the default URLClassLoader delegation to avoid complex class-loading logic.

Class diagram for updated PluginManager methods

classDiagram
class PluginManager {
    +Set<String> parseDependencies(File jarFile)
    +boolean isDependencyMissing(String groupArtifact)
    +URLClassLoader createPluginClassLoader(List<URL> urls)
}

PluginManager : parseDependencies() filters out empty dependency strings
PluginManager : isDependencyMissing() checks dependency existence via filesystem
PluginManager : createPluginClassLoader() uses default URLClassLoader (parent delegation)
Loading

File-Level Changes

Change Details Files
Filter out empty dependency entries in parseDependencies
  • Add non-empty check before splitting Dependencies attribute
  • Prevent ArrayIndexOutOfBoundsException for empty manifests
src/main/java/com/mikuac/shiro/core/PluginManager.java
Revamp isDependencyMissing logic to use file existence check
  • Use split with limit to parse group, artifact, version
  • Construct jar path under dependencies directory
  • Return missing status based on file existence instead of class loading
src/main/java/com/mikuac/shiro/core/PluginManager.java
Simplify plugin class loader to default parent delegation
  • Remove custom loadClass and getResource overrides
  • Use default URLClassLoader constructor with parent context class loader
src/main/java/com/mikuac/shiro/core/PluginManager.java

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

你好 - 我已经审查了你的更改 - 以下是一些反馈意见:

  • 在 parseDependencies 中,分割 deps 字符串后,你应该修剪每个条目并过滤掉任何空白令牌,以避免在有尾随逗号或额外空格时出现散乱的空依赖名称。
  • 在 isDependencyMissing 中,在索引之前验证 ':' 分割是否恰好产生三个段,并捕获特定的异常而不是通用的 Exception,以避免掩盖意外错误。
  • 放弃自定义 URLClassLoader 覆盖会改变插件隔离和日志包加载顺序——请验证默认的父优先委托是否仍满足你的插件隔离和冲突避免要求。
AI 代理提示
请解决此代码审查中的评论:

## 总体评论
- 在 parseDependencies 中,分割 deps 字符串后,你应该修剪每个条目并过滤掉任何空白令牌,以避免在有尾随逗号或额外空格时出现散乱的空依赖名称。
- 在 isDependencyMissing 中,在索引之前验证 ':' 分割是否恰好产生三个段,并捕获特定的异常而不是通用的 Exception,以避免掩盖意外错误。
- 放弃自定义 URLClassLoader 覆盖会改变插件隔离和日志包加载顺序——请验证默认的父优先委托是否仍满足你的插件隔离和冲突避免要求。

## 单独评论

### 评论 1
<location> `src/main/java/com/mikuac/shiro/core/PluginManager.java:92-98` </location>
<code_context>
             if (manifest != null) {
                 String deps = manifest.getMainAttributes().getValue("Dependencies");
-                if (deps != null) {
+                if (deps != null && !deps.isEmpty()) {
                     dependencies.addAll(Arrays.asList(deps.split(",\\s*")));
                 }
</code_context>

<issue_to_address>
**suggestion:** 考虑在检查空依赖项之前修剪空白字符。

目前,仅包含空白字符的“Dependencies”值将被视为非空。在检查之前修剪字符串以避免此问题。

```suggestion
            Manifest manifest = jar.getManifest();
            if (manifest != null) {
                String deps = manifest.getMainAttributes().getValue("Dependencies");
                if (deps != null) {
                    String trimmedDeps = deps.trim();
                    if (!trimmedDeps.isEmpty()) {
                        dependencies.addAll(Arrays.asList(trimmedDeps.split(",\\s*")));
                    }
                }
            }
```
</issue_to_address>

### 评论 2
<location> `src/main/java/com/mikuac/shiro/core/PluginManager.java:122` </location>
<code_context>
+            String[] parts = groupArtifact.split(" :", 3);
</code_context>

<issue_to_address>
**issue (bug_risk):** 添加 groupArtifact 格式验证以避免 ArrayIndexOutOfBoundsException。

在分割并访问 parts[2] 之前,验证 groupArtifact 至少包含两个冒号,以防止运行时异常。
</issue_to_address>

### 评论 3
<location> `src/main/java/com/mikuac/shiro/core/PluginManager.java:129` </location>
<code_context>
+                    .resolve(version)
+                    .resolve("%s-%s.jar".formatted(artifact, version));
+            return !jarPath.toFile().exists();
+        } catch (Exception e) {
             return true;
         }
</code_context>

<issue_to_address>
**suggestion (bug_risk):** 捕获通用 Exception 可能会掩盖不相关的错误。

在 catch 块中使用更具体的异常类型,以避免隐藏不相关的错误。

建议实现:

```java
        try {
            String[] parts = groupArtifact.split(":", 3);
            val group = parts[0];
            val artifact = parts[1];
            val version = parts[2];
            val jarPath = DependencyResolver.DEPENDENCIES_DIR.toPath()
                    .resolve(group.replace('.', '/'))
                    .resolve(artifact)
                    .resolve(version)
                    .resolve("%s-%s.jar".formatted(artifact, version));
            return !jarPath.toFile().exists();

        } catch (java.nio.file.InvalidPathException | java.io.IOException e) {
            return true;
        }

```

如果你的上下文中还有其他常见的异常(例如 SecurityException),请考虑将它们添加到 catch 块中。如果你有自定义的异常处理约定,请相应地调整异常类型。
</issue_to_address>

Sourcery 对开源项目免费 - 如果你喜欢我们的评论,请考虑分享它们 ✨
帮助我更有用!请点击每个评论上的 👍 或 👎,我将使用这些反馈来改进你的评论。
Original comment in English

Hey there - I've reviewed your changes - here's some feedback:

  • In parseDependencies, after splitting the deps string you should trim each entry and filter out any blank tokens to avoid stray empty dependency names if there are trailing commas or extra whitespace.
  • In isDependencyMissing, validate that the split on ':' yields exactly three segments before indexing and catch specific exceptions rather than Exception to avoid masking unintended errors.
  • Dropping the custom URLClassLoader overrides changes the plugin isolation and logging package loading order—please verify that the default parent-first delegation still meets your plugin isolation and conflict-avoidance requirements.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In parseDependencies, after splitting the deps string you should trim each entry and filter out any blank tokens to avoid stray empty dependency names if there are trailing commas or extra whitespace.
- In isDependencyMissing, validate that the split on ':' yields exactly three segments before indexing and catch specific exceptions rather than Exception to avoid masking unintended errors.
- Dropping the custom URLClassLoader overrides changes the plugin isolation and logging package loading order—please verify that the default parent-first delegation still meets your plugin isolation and conflict-avoidance requirements.

## Individual Comments

### Comment 1
<location> `src/main/java/com/mikuac/shiro/core/PluginManager.java:92-98` </location>
<code_context>
             if (manifest != null) {
                 String deps = manifest.getMainAttributes().getValue("Dependencies");
-                if (deps != null) {
+                if (deps != null && !deps.isEmpty()) {
                     dependencies.addAll(Arrays.asList(deps.split(",\\s*")));
                 }
</code_context>

<issue_to_address>
**suggestion:** Consider trimming whitespace before checking for empty dependencies.

Currently, a 'Dependencies' value with only whitespace will be considered non-empty. Trim the string before checking to avoid this issue.

```suggestion
            Manifest manifest = jar.getManifest();
            if (manifest != null) {
                String deps = manifest.getMainAttributes().getValue("Dependencies");
                if (deps != null) {
                    String trimmedDeps = deps.trim();
                    if (!trimmedDeps.isEmpty()) {
                        dependencies.addAll(Arrays.asList(trimmedDeps.split(",\\s*")));
                    }
                }
            }
```
</issue_to_address>

### Comment 2
<location> `src/main/java/com/mikuac/shiro/core/PluginManager.java:122` </location>
<code_context>
+            String[] parts = groupArtifact.split(" :", 3);
</code_context>

<issue_to_address>
**issue (bug_risk):** Add validation for groupArtifact format to avoid ArrayIndexOutOfBoundsException.

Validate that groupArtifact contains at least two colons before splitting and accessing parts[2] to prevent runtime exceptions.
</issue_to_address>

### Comment 3
<location> `src/main/java/com/mikuac/shiro/core/PluginManager.java:129` </location>
<code_context>
+                    .resolve(version)
+                    .resolve("%s-%s.jar".formatted(artifact, version));
+            return !jarPath.toFile().exists();
+        } catch (Exception e) {
             return true;
         }
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Catching generic Exception may mask unrelated errors.

Use a more specific exception type in the catch block to avoid hiding unrelated errors.

Suggested implementation:

```java
        try {
            String[] parts = groupArtifact.split(":", 3);
            val group = parts[0];
            val artifact = parts[1];
            val version = parts[2];
            val jarPath = DependencyResolver.DEPENDENCIES_DIR.toPath()
                    .resolve(group.replace('.', '/'))
                    .resolve(artifact)
                    .resolve(version)
                    .resolve("%s-%s.jar".formatted(artifact, version));
            return !jarPath.toFile().exists();

        } catch (java.nio.file.InvalidPathException | java.io.IOException e) {
            return true;
        }

```

If there are other exceptions that are commonly thrown in your context (e.g., SecurityException), consider adding them to the catch block. If you have custom exception handling conventions, adjust the exception types accordingly.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/main/java/com/mikuac/shiro/core/PluginManager.java
Comment thread src/main/java/com/mikuac/shiro/core/PluginManager.java
Comment thread src/main/java/com/mikuac/shiro/core/PluginManager.java
@MisakaTAT
MisakaTAT merged commit a0f948a into MisakaTAT:main Sep 16, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants