This is an automated email from the ASF dual-hosted git repository. lukaszlenart pushed a commit to branch fix/struts7-action-detection in repository https://gitbox.apache.org/repos/asf/struts-intellij-plugin.git
commit 7b26500d15307d25a0749e3111cb0ccc7b527e32 Author: Lukasz Lenart <[email protected]> AuthorDate: Thu Jul 2 16:28:12 2026 +0200 fix(inspection): detect Struts 7 actions via modern Action interface Recognize actions implementing org.apache.struts2.action.Action (Struts 7+) in addition to the legacy com.opensymphony.xwork2.Action, and check the action interface independently of the Convention plugin. Only the *Action naming convention remains gated behind Convention plugin presence. This lets the @StrutsParameter inspection cover convention/interface-based actions on Struts 7, where the legacy XWork Action interface no longer exists. Co-authored-by: Cursor <[email protected]> --- CHANGELOG.md | 1 + .../java/com/intellij/struts2/StrutsConstants.java | 6 ++++ .../struts2/inspection/StrutsActionClassUtil.java | 16 +++++---- .../StrutsParameterAnnotationInspectionTest.java | 38 ++++++++++++++++++++++ 4 files changed, 55 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a1a56b..773449d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ ### Fixed +- Recognize Struts 7 actions implementing `org.apache.struts2.action.Action` (and detect the action interface independently of the Convention plugin) so the `@StrutsParameter` inspection also covers convention/interface-based actions - Update Struts 7.2.1 metadata support: add current constants and completion values for annotation-required parameters, chaining annotation checks, `html5`/`css_xhtml` themes, `jakarta-stream` multipart parsing, and modern web.xml Struts filters - Fix false "Cannot resolve symbol" errors for namespace-relative JSP result paths (e.g. `WEB-INF/upload.jsp` without leading slash) - Fix path completion inside `<result>` tags by restoring correct `FileReferenceSet` construction for IntelliJ 2026.1 diff --git a/src/main/java/com/intellij/struts2/StrutsConstants.java b/src/main/java/com/intellij/struts2/StrutsConstants.java index 26903c9..cb0c81d 100644 --- a/src/main/java/com/intellij/struts2/StrutsConstants.java +++ b/src/main/java/com/intellij/struts2/StrutsConstants.java @@ -40,6 +40,12 @@ public final class StrutsConstants { @NonNls public static final String XWORK_ACTION_CLASS = "com.opensymphony.xwork2.Action"; + /** + * Modern Struts action interface (Struts 7+), replacing the legacy {@link #XWORK_ACTION_CLASS}. + */ + @NonNls + public static final String STRUTS_ACTION_CLASS = "org.apache.struts2.action.Action"; + @NonNls public static final String STRUTS_PARAMETER_ANNOTATION = "org.apache.struts2.interceptor.parameter.StrutsParameter"; diff --git a/src/main/java/com/intellij/struts2/inspection/StrutsActionClassUtil.java b/src/main/java/com/intellij/struts2/inspection/StrutsActionClassUtil.java index 2f2b2f2..3e1d46f 100644 --- a/src/main/java/com/intellij/struts2/inspection/StrutsActionClassUtil.java +++ b/src/main/java/com/intellij/struts2/inspection/StrutsActionClassUtil.java @@ -55,16 +55,20 @@ final class StrutsActionClassUtil { return true; } - if (!isConventionPluginPresent(psiClass)) { - return false; + // Implementing the Struts action interface marks it as an action, regardless of the Convention plugin. + // Struts 7+ uses org.apache.struts2.action.Action; earlier versions use com.opensymphony.xwork2.Action. + if (InheritanceUtil.isInheritor(psiClass, StrutsConstants.STRUTS_ACTION_CLASS) || + InheritanceUtil.isInheritor(psiClass, StrutsConstants.XWORK_ACTION_CLASS)) { + return true; } - final String className = psiClass.getName(); - if (className != null && StringUtil.endsWith(className, "Action")) { - return true; + // The *Action naming convention only applies when the Convention plugin is present. + if (isConventionPluginPresent(psiClass)) { + final String className = psiClass.getName(); + return className != null && StringUtil.endsWith(className, "Action"); } - return InheritanceUtil.isInheritor(psiClass, StrutsConstants.XWORK_ACTION_CLASS); + return false; } private static boolean isConcretePublicClass(@Nullable PsiClass psiClass) { diff --git a/src/test/java/com/intellij/struts2/inspection/StrutsParameterAnnotationInspectionTest.java b/src/test/java/com/intellij/struts2/inspection/StrutsParameterAnnotationInspectionTest.java index ff6f0c5..dd5d9fb 100644 --- a/src/test/java/com/intellij/struts2/inspection/StrutsParameterAnnotationInspectionTest.java +++ b/src/test/java/com/intellij/struts2/inspection/StrutsParameterAnnotationInspectionTest.java @@ -52,6 +52,32 @@ public class StrutsParameterAnnotationInspectionTest extends BasicLightHighlight myFixture.checkHighlighting(); } + public void testWarnsInActionImplementingModernActionInterface() { + configureStrutsParameterAnnotation(); + configureModernActionInterface(); + createStrutsFileSetFromFixture("struts.xml", "struts-require-annotations.xml"); + + configureFileForHighlighting("test/ModernController.java", """ + package test; + + import org.apache.struts2.action.Action; + + public class ModernController implements Action { + public String <warning descr="%s">username</warning>; + + public void <warning descr="%s">setPassword</warning>(String password) { + } + + @Override + public String execute() { + return SUCCESS; + } + } + """.formatted(WARNING, WARNING)); + + myFixture.checkHighlighting(); + } + public void testDoesNotWarnAboutAnnotatedMembers() { configureStrutsParameterAnnotation(); createStrutsFileSetFromFixture("struts.xml", "struts-require-annotations.xml"); @@ -154,6 +180,18 @@ public class StrutsParameterAnnotationInspectionTest extends BasicLightHighlight """); } + private void configureModernActionInterface() { + myFixture.addFileToProject("org/apache/struts2/action/Action.java", """ + package org.apache.struts2.action; + + public interface Action { + String SUCCESS = "success"; + + String execute() throws Exception; + } + """); + } + private void configureFileForHighlighting(@NotNull String path, @NotNull String text) { final PsiFile psiFile = myFixture.addFileToProject(path, text); myFixture.configureFromExistingVirtualFile(psiFile.getVirtualFile());
