Currently I am using Struts 2.3.12 in my project and all the things are working fine. Now I have requirement to upgrade Struts version to 2.3.20 to address some security issue.
I changed the Struts and required Struts plugins version to 2.3.20 in my project pom.xml and build the project war. Now, I am trying to access my application home URL then getting following exception:
There is no Action mapped for namespace
[/web/public]and action name[reset-password!reset]associated with context path[/ims]. - [unknown location]
com.opensymphony.xwork2.DefaultActionProxy.prepare(DefaultActionProxy.java:185)
org.apache.struts2.impl.StrutsActionProxy.prepare(StrutsActionProxy.java:63)
org.apache.struts2.impl.StrutsActionProxyFactory.createActionProxy(StrutsActionProxyFactory.java:37)
com.opensymphony.xwork2.DefaultActionProxyFactory.createActionProxy(DefaultActionProxyFactory.java:58)
org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:554)
org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:81)
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:99)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:215)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
org.displaytag.filter.ResponseOverrideFilter.doFilter(ResponseOverrideFilter.java:125)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:215)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:215)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:172)
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:117)
org.josso.tc55.agent.SSOAgentValve.invoke(SSOAgentValve.java:472)
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:108)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:174)
org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:875)
org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:665)
org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:528)
org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:81)
org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:689)
java.lang.Thread.run(Thread.java:662)
I couldn't get a single clue on what is happening.
I looked into the release note of struts 2.3.20 but I am not getting any hint.
I'm using strust2-convention-plugin.
I am sharing my web.xml and struts.xml files. It looks like as following:
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="jail" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>ims</display-name>
<!-- JCaptcha servlet mapping -->
<servlet>
<servlet-name>jcaptcha</servlet-name>
<servlet-class>com.sapienza.jail.controller.jcaptcha.JailImageCaptchaServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>jcaptcha</servlet-name>
<url-pattern>/web/public/jcaptcha.jpg</url-pattern>
</servlet-mapping>
<!-- Filters -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/web/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>ResponseOverrideFilter</filter-name>
<filter-class>org.displaytag.filter.ResponseOverrideFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ResponseOverrideFilter</filter-name>
<url-pattern>/web/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/web/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/web/public/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.action</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/struts/*</url-pattern>
</filter-mapping>
<!-- JSP configuration -->
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<el-ignored>false</el-ignored>
<page-encoding>UTF-8</page-encoding>
<include-prelude>/jsp/common/taglibs.jspf</include-prelude>
</jsp-property-group>
</jsp-config>
<welcome-file-list>
<welcome-file>/jsp/common/home.jsp</welcome-file>
</welcome-file-list>
<!-- Spring Listener -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- Skin listener -->
<listener>
<listener-class>com.sapienza.jail.listener.ResourceListener</listener-class>
</listener>
<!-- LDAP Synchronisation Listeneer -->
<listener>
<listener-class>com.sapienza.jail.listener.LdapSyncListener</listener-class>
</listener>
<!-- Tiles listener -->
<listener>
<listener-class>org.apache.struts2.tiles.StrutsTilesListener</listener-class>
</listener>
<context-param>
<param-name>org.apache.tiles.impl.BasicTilesContainer.DEFINITIONS_CONFIG</param-name>
<param-value>/WEB-INF/tiles/skins-definitions.xml,/WEB-INF/tiles/pages-definitions.xml</param-value>
</context-param>
<context-param>
<param-name>org.apache.tiles.evaluator.AttributeEvaluator</param-name>
<param-value>org.apache.tiles.evaluator.el.ELAttributeEvaluator</param-value>
</context-param>
</web-app>
struts.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<include file="struts-default.xml"/>
<constant name="struts.objectFactory" value="spring"/>
<constant name="struts.devMode" value="true"/>
<constant name="struts.convention.default.parent.package" value="base-configuration"/>
<constant name="struts.convention.classes.reload" value="true"/>
<constant name="struts.enableJSONValidation" value="true"/>
<!-- struts configuration common for the whole application -->
<include file="struts-base.xml"/>
</struts>
struts-base.xml:
<struts>
<!-- This package is abstract. It is not mean to declare any actions, only
common components such as interceptors, global results ... -->
<package name="base-configuration" abstract="true" extends="tiles-default">
<result-types>
<result-type name="jasper" class="org.apache.struts2.views.jasperreports.JasperReportsResult"/>
<result-type name="json" class="org.apache.struts2.json.JSONResult"/>
<result-type name="tiles" class="org.apache.struts2.views.tiles.TilesResult"/>
</result-types>
<!-- create a custom paramsPrepareParamsStack using our log4j interceptor -->
<interceptors>
<!-- declaration of the custom interceptor using log4j to log exceptions. -->
<interceptor name="log4jExceptionMappingInterceptor" class="log4jExceptionMappingInterceptor" />
<!-- declaration of the custom security interceptor -->
<interceptor name="securityInterceptor" class="securityInterceptor" />
<!-- declaration of the custom SearchBean interceptor -->
<interceptor name="searchBeanInterceptor" class="searchBeanInterceptor" />
<!-- declaration of the custom WhiteSpaceTrim interceptor -->
<interceptor name="whiteSpaceTrimmerInterceptor" class="whiteSpaceTrimmerInterceptor" />
<!-- Struts2 JSON Validation -->
<interceptor name="jsonValidation" class="org.apache.struts2.json.JSONValidationInterceptor" />
<interceptor-stack name="imsDefaultStack">
<!-- insert log4j interceptor inserted in the custom stack -->
<interceptor-ref name="log4jExceptionMappingInterceptor" />
<!-- insert the custom security interceptor -->
<interceptor-ref name="securityInterceptor" />
<interceptor-ref name="alias" />
<interceptor-ref name="params" />
<interceptor-ref name="servletConfig" />
<interceptor-ref name="prepare" />
<interceptor-ref name="i18n" />
<interceptor-ref name="chain" />
<interceptor-ref name="checkbox" />
<interceptor-ref name="staticParams" />
<interceptor-ref name="params" />
<!-- excludes base CRUD methods from the validation process -->
<!-- validation interceptor triggers the xml validation -->
<interceptor-ref name="validation">
<param name="validateAnnotatedMethodOnly">true</param>
<param name="excludeMethods">input,back,cancel,list,view,initCreate,initUpdate,delete</param>
</interceptor-ref>
<!-- ajax validation interceptor -->
<interceptor-ref name="jsonValidation">
<param name="excludeMethods">input</param>
</interceptor-ref>
<!-- workflow interceptor triggers programmatic validation (calls validate()) -->
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,list,view,initCreate,initUpdate,delete</param>
</interceptor-ref>
</interceptor-stack>
<!-- this new custom stack will be used for public URL -->
<interceptor-stack name="imsNoSecurityStack">
<!-- insert log4j interceptor inserted in the custom stack -->
<interceptor-ref name="log4jExceptionMappingInterceptor" />
<interceptor-ref name="alias" />
<interceptor-ref name="params" />
<interceptor-ref name="servletConfig" />
<interceptor-ref name="prepare" />
<interceptor-ref name="i18n" />
<interceptor-ref name="chain" />
<interceptor-ref name="checkbox" />
<interceptor-ref name="staticParams" />
<interceptor-ref name="params" />
<!-- excludes base CRUD methods from the validation process -->
<!-- validation interceptor triggers the xml validation -->
<interceptor-ref name="validation">
<param name="validateAnnotatedMethodOnly">true</param>
<param name="excludeMethods">input,back,cancel,list,view,initCreate,initUpdate,delete</param>
</interceptor-ref>
<!-- ajax validation interceptor -->
<interceptor-ref name="jsonValidation">
<param name="excludeMethods">input</param>
</interceptor-ref>
<!-- workflow interceptor triggers programmatic validation (calls validate()) -->
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,list,view,initCreate,initUpdate,delete</param>
</interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- the new custom stack will be the default one used in the sub packages. -->
<default-interceptor-ref name="imsDefaultStack" />
<!-- exception handling -->
<global-results>
<result name="error">/jsp/common/error.jsp</result>
<result name="securityError">/jsp/common/access-denied.jsp</result>
<result name="ldapError">/jsp/common/ldap-connection-error.jsp</result>
</global-results>
<!-- any unhandled exceptions will return the error page displaying the
message of the exception. -->
<global-exception-mappings>
<exception-mapping result="error" exception="java.lang.Exception" />
<exception-mapping result="securityError" exception="com.sapienza.jail.exception.NoApplicationAccess" />
<exception-mapping result="ldapError" exception="com.sapienza.jail.exception.LDAPConnectionException" />
</global-exception-mappings>
</package>
</struts>
I am using annotation based action mapping in my class as following
@Results({
@Result(name="index", type="tiles", location="testPage"),
@Result(name = "redirect", location = "user/search-user!view", type = "redirectAction")
})
@Namespace("/web/public")
@Action
public class HomeAction extends BaseAction {
private static final Logger logger = Logger.getLogger(HomeAction.class);
private static final String SESSIONBASKET = "userSessionBasket";
//-------------------------------------------------------------------------
// Dependencies injected by spring via setters
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
// Constructor and methods
//-------------------------------------------------------------------------
public HomeAction() {
}
@Override
public String execute() {
if (isInHttpSession(SESSIONBASKET)){
getSession().removeAttribute(SESSIONBASKET);
}
return result(REDIRECT_RESULT);
}
//-------------------------------------------------------------------------
// Getters and Setters
//-------------------------------------------------------------------------
}
Since the version is changed the Struts default
ActionMapperimplementation changed significantly. Many security fixes were applied during that.You could check the security bulletins.
The problem is that you use DMI with URL mapping. The DMI is turned off by default. The DMI feature may not work in the current version of Struts, and you should change your action's mapping.
But prior to upgrading to 2.3.20 you could try upgrading to 2.3.16.3.
To turn on DMI try to enable it using a constant: