Hello Spring Team,

We are currently migrating a large legacy application from Java 1.6/Spring 3 to a modern stack. We have encountered a persistent NoSuchMethodException when a Struts 2 Action, which is proxied by Spring AOP, is invoked.

This issue seems to be a deep incompatibility that only occurs with this specific combination of modern frameworks.

Environment Spring Framework: 6.1.10 Struts 2: 7.0.3 (with struts2-spring-plugin) Java Version: 21 (Temurin 21.0.3) Servlet Container: Apache Tomcat 10.1.34 Steps to Reproduce A complete Minimal, Reproducible Example (MRE) is attached as a Maven project (mre-project.zip).

Unzip the attached project. Build the project using mvn clean package. Deploy the resulting mre-project.war to Tomcat 10.1. Access the URL: http://localhost:8080/mre-project/test.action Current Behavior When the URL is accessed, the application fails with a java.lang.NoSuchMethodException. The Struts 2 DefaultActionInvocation is unable to find the execute() method on the Spring-generated CGLIB proxy class.

Here is the stack trace from the MRE:

java.lang.NoSuchMethodException: com.example.mre.TestAction$$SpringCGLIB$$0.execute()
    ognl.OgnlRuntime.callAppropriateMethod(OgnlRuntime.java:1901)
    ognl.ObjectMethodAccessor.callMethod(ObjectMethodAccessor.java:68)
    org.apache.struts2.ognl.accessor.XWorkMethodAccessor.callMethodWithDebugInfo(XWorkMethodAccessor.java:101)
    org.apache.struts2.ognl.accessor.XWorkMethodAccessor.callMethod(XWorkMethodAccessor.java:93)
    ognl.OgnlRuntime.callMethod(OgnlRuntime.java:2034)
    ognl.ASTMethod.getValueBody(ASTMethod.java:97)
    ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
    ognl.SimpleNode.getValue(SimpleNode.java:258)
    ognl.Ognl.getValue(Ognl.java:586)
    org.apache.struts2.ognl.OgnlUtil.ognlGet(OgnlUtil.java:425)
    org.apache.struts2.ognl.OgnlUtil.callMethod(OgnlUtil.java:401)
    org.apache.struts2.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:447)
    org.apache.struts2.DefaultActionInvocation.invokeActionOnly(DefaultActionInvocation.java:320)
    org.apache.struts2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:271)
    ... (stack trace continues)

Expected Behavior The DefaultActionInvocation should successfully find and invoke the execute() method on the Spring-generated proxy, allowing the action to execute without error.

Additional Context The issue occurs when AOP is enabled with proxy-target-class="true" in applicationContext.xml. We believe the CGLIB proxy generated in the Java 21 environment is not correctly exposing the methods of the target Action class to the OGNL runtime.

Thank you for your time and for maintaining this great framework. Please let us know if you need any further information.

ActionError.zip

Comment From: jhoeller

You could try to introspect the actual methods on the proxied Action instance through action.getDeclaredMethods(), seeing whether a public execute() method is actually there. Since the proxy is technically a subclass of TestAction then, I cannot imagine how that method would not be there - since at the very least, it would get inherited from the plain TestAction base class.

As a side note, a Struts2 Action could work with an interface-based proxy by design. That said, it should still work with proxy-target-class as well. From a quick look at your source code, I cannot see anything obviously wrong. I'm not familar enough with OGNL to make a judgment about OGNL's expectations there. You might have to debug OgnlRuntime.callAppropriateMethod: As far as I can see from a quick glance, OGNL might find the method but reject it as "not accessible" for some reason.

Comment From: bclozel

Additional note: Spring Framework 6.1.x goes out of OSS support by the end of this month. @jimmy3454 I guess you're considering 6.1.x as a step towards a supported version, but I wanted to let you know anyway.

Comment From: jimmy3454

Hello,

An update on this issue.

After further investigation with the attached MRE project, I have found a workaround that allows the action to execute successfully.

The NoSuchMethodException can be resolved by adding the following two constants to the struts.xml file:

XML

<constant name="struts.devMode" value="true" />
<constant name="struts.objectFactory" value="spring" />
...

Explanation of the workaround:

struts.disallowProxyObjectAccess="false": This disables a default security feature in modern Struts that prevents OGNL from accessing proxy objects. This appears to be necessary as Spring is proxying the Action class. struts.ognl.allowlist.acceptedPackageNames="com.example.mre": This adds the application's package to the OGNL security allowlist. This is required because the Spring CGLIB proxy class exists within this package, which is not on the default allowlist. While these settings allow the MRE to run, it seems the default security configurations in Struts 7 are not fully compatible out-of-the-box with the proxy objects created by Spring AOP in this environment (Java 21).

Thank you.