You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@struts.apache.org by Zoran Avtarovski <zo...@sparecreative.com> on 2025/06/03 05:43:19 UTC
Re: File Upload Failure in Struts 7.0.3 Migration - UploadedFilesAware Implementation Issue
Hi Jesus,
What does your web.xml look like with regards to the struts filters? We
had a similar issue which was resolved by moving to the combined
StrutsPrepareAndExecuteFilter. Sitemesh also played a part in our issue,
but I think by what you are seeing it's worth trying.
Z.
On 1/6/2025 6:29 am, Jesus Moreno wrote:
> Hi Lukasz, thanks for your appreciated comments.
>
> I would like to explain in more detail what we have done to try to find the
> problem and solve it. Here the context:
>
> This is my environment:
>
> - Struts Version: 7.0.3
> - Java Version: 17
> - Servlet Container: Apache Tomcat 10.1.36
> - Key Dependencies (pom):
> - struts2-core:7.0.3
> - struts2-convention-plugin:7.0.3
> - struts2-json-plugin:7.0.3
> - jakarta.servlet-api:5.0.0
> - log4j-core:2.17.2
> - Enforced exclusions: javax.servlet:*, javax.activation:*
>
> We've created a basic Maven project (standard archetype) with a simple file
> upload form containing:
> - 4 regular text fields (working correctly with
> @StrutsParameter(depth=0))
> - 1 file upload field (<input type="file" name="upload">)
>
> This is my basic jsp:
>
> <%@ page contentType="text/html; charset=UTF-8" %>
> <%@ taglib prefix="s" uri="/struts-tags" %>
> <!DOCTYPE html>
> <html>
> <head>
> <title>File upload result</title>
> </head>
> <body>
> <h1>Hola Mundo desde la tierra...</h1>
> <form action="hello" method="post" enctype="multipart/form-data">
> <input type="text" name="name"/>
> <input type="text" name="lastname"/>
> <input type="text" name="gender"/>
> <input type="text" name="age"/>
> <input type="file" name="*upload*"/>
> <input type="submit" value="Subir" />
> </form>
> </body>
> </html>
>
> Expected Behavior:
>
> - When implementing UploadedFilesAware, the framework should:
> - Detect multipart requests
> - Process uploaded files through withUploadedFiles(List<UploadedFile>)
> - Make files available to the action
>
> Where my action looks like:
>
> import org.apache.logging.log4j.LogManager;
> import org.apache.logging.log4j.Logger;
> import org.apache.struts2.ActionSupport;
> import org.apache.struts2.action.UploadedFilesAware;
> import org.apache.struts2.dispatcher.multipart.UploadedFile;
> import org.apache.struts2.interceptor.parameter.StrutsParameter;
>
> public class HelloAction extends ActionSupport implements
> UploadedFilesAware {
> private static final long serialVersionUID = 1L;
> private Logger logger = LogManager.getLogger(HelloAction.class);
>
> private UploadedFile upload;
> private String uploadFileName;
> private String uploadContentType;
>
> @StrutsParameter(depth = 0)
> public String name;
>
> @StrutsParameter(depth = 0)
> public String lastname;
>
> @StrutsParameter(depth = 0)
> public String gender;
>
> @StrutsParameter(depth = 0)
> public String age;
>
> @Override
> public String execute() {
> logger.info("Executing execute method..." + this.uploadFileName);
> return SUCCESS;
> }
> public UploadedFile getUpload() {
> return upload;
> }
> public void setUpload(UploadedFile upload) {
> this.upload = upload;
> }
> public String getUploadFileName() {
> return uploadFileName;
> }
> public void setUploadFileName(String uploadFileName) {
> this.uploadFileName = uploadFileName;
> }
> public String getUploadContentType() {
> return uploadContentType;
> }
> public void setUploadContentType(String uploadContentType) {
> this.uploadContentType = uploadContentType;
> }
> public Logger getLogger() {
> return logger;
> }
> public void setLogger(Logger logger) {
> this.logger = logger;
> }
> public String getName() {
> return name;
> }
> public void setName(String name) {
> this.name = name;
> }
> public String getLastname() {
> return lastname;
> }
> public void setLastname(String lastname) {
> this.lastname = lastname;
> }
> public String getGender() {
> return gender;
> }
> public void setGender(String gender) {
> this.gender = gender;
> }
> public String getAge() {
> return age;
> }
> public void setAge(String age) {
> this.age = age;
> }
>
> @Override
> public void withUploadedFiles(List<UploadedFile> uploadedFiles) {
> if (uploadedFiles != null && !uploadedFiles.isEmpty()) {
> UploadedFile firstFile = uploadedFiles.get(0); // Take the file
> this.upload = firstFile;
> this.uploadFileName = firstFile.getName(); // String
> this.uploadContentType = firstFile.getContentType(); // String
> logger.info("File name: " + uploadFileName);
> }
> }
>
> Actual Behavior:
> Despite proper multipart request detection:
>
> The interceptor finds the request wrapper:
>
> 2025-05-31 DEBUG ActionFileUploadInterceptor:200 - Found multipart request:
> MultiPartRequestWrapper
>
> *Our custom DebugMultiPartRequest confirms file presence:*
>
> 2025-05-31 12:00:13 INFO DebugMultiPartRequest:26 - Todos los parámetros
> del request:
> 2025-05-31 12:00:13 INFO DebugMultiPartRequest:30 - - nombre: Jesus
> 2025-05-31 12:00:13 INFO DebugMultiPartRequest:30 - - apellido: Moreno
> 2025-05-31 12:00:13 INFO DebugMultiPartRequest:30 - - sexo: M
> 2025-05-31 12:00:13 INFO DebugMultiPartRequest:30 - - edad: 50
>
>
>
> *2025-05-31 12:00:13 INFO DebugMultiPartRequest:39 - Part 'upload'
> encontrada:2025-05-31 12:00:13 INFO DebugMultiPartRequest:40 - - Nombre:
> Adaware.jpg2025-05-31 12:00:13 INFO DebugMultiPartRequest:41 - - Tamaño:
> 25546*2025-05-31 12:00:13 DEBUG InstantiatingNullHandler:106 - Entering
> nullPropertyValue
> [target=[org.apache.struts2.text.DefaultTextProvider@2a580722],
> property=struts]
> 2025-05-31 12:00:13 DEBUG ConfigurationManager:180 - Checking
> ConfigurationProviders for reload.
> 2025-05-31 12:00:13 DEBUG InstantiatingNullHandler:106 - Entering
> nullPropertyValue
> [target=[org.apache.struts2.text.DefaultTextProvider@2a580722],
> property=struts]
> 2025-05-31 12:00:13 DEBUG DefaultActionProxy:88 - Creating an
> DefaultActionProxy for namespace [/] and action name [hello]
> 2025-05-31 12:00:14 DEBUG DefaultActionInvocation:310 - Executing
> conditional interceptor: ActionFileUploadInterceptor
> 2025-05-31 12:00:14 DEBUG ActionFileUploadInterceptor:200 - Found multipart
> request: MultiPartRequestWrapper
>
> But the interceptor reports:
>
> *2025-05-31 12:00:14 DEBUG ActionFileUploadInterceptor:179 - No files have
> been uploaded/accepted*
>
> Investigation Steps Taken:
>
> 1. Verified all prerequisites:
> - Form uses enctype="multipart/form-data"
> - Using DTD 6.0 as suggested
> - Regularly cleaning Tomcat's work directory
> - Confirmed Jakarta EE 9+ compatibility
>
> 2. Created extensive debug instrumentation:
> - Custom DebugMultiPartRequest shows files reach the server
> - Interceptor-level logging confirms the disconnect
>
> Technical Findings:
>
> 1. The MultiPartRequestWrapper appears to lose file references between:
> - Initial parsing (where files are detectable via raw Part API)
> - Interceptor processing phase
>
> 2. Key suspicious behavior:
> - getFileParameterNames() returns empty enumeration
> - Despite request.getPart("upload") working correctly
>
> Finally As you suggested, we've:
>
> - Consistently cleaned deployment directories
> - Verified DTD compatibility
> - Followed migration guides carefully
> - Yet the team remains persistent in investigating this apparent
> framework bug rather than working around it.
>
> # Summary
> Files are received by the server (confirmed via custom
> DebugMultiPartRequest) but ActionFileUploadInterceptor fails to process
> them via UploadedFilesAware interface in Struts 7.0.3 (Jakarta EE 9/Tomcat
> 10 environment).
>
> This is my custom DebugMultiPartRequest:
>
> public class DebugMultiPartRequest extends JakartaMultiPartRequest {
> private static final Logger logger =
> LogManager.getLogger(DebugMultiPartRequest.class);
> @Override
> public void parse(HttpServletRequest request, String saveDir) throws
> IOException {
> super.parse(request, saveDir); // Procesa el request normalmente
> logger.info("=== DebugMultiPartRequest (Struts 7) ===");
> // 1. Listar TODOS los parámetros del request (no solo archivos)
> logger.info("Todos los parámetros del request:");
> Enumeration<String> paramNames = request.getParameterNames();
> while (paramNames.hasMoreElements()) {
> String name = paramNames.nextElement();
> logger.info(" - " + name + ": " + request.getParameter(name));
> }
> // 2. Listar partes multipart (alternativa para Jakarta EE 9+)
> if (request instanceof jakarta.servlet.http.HttpServletRequest
> jakartaRequest) {
> jakarta.servlet.http.Part part;
> try {
> part = jakartaRequest.getPart("upload");
> if (part != null) {
>
>
>
> * logger.info<http://logger.info>("Part 'upload' encontrada:");
> logger.info<http://logger.info>(" - Nombre: " +
> part.getSubmittedFileName()); logger.info<http://logger.info>(" - Tamaño:
> " + part.getSize());* } else {
> logger.info("❌ Parte 'upload' NO encontrada en el request.");
> }
> } catch (IOException e) {
> // TODO Auto-generated catch block
> e.printStackTrace();
> } catch (ServletException e) {
> // TODO Auto-generated catch block
> e.printStackTrace();
> } // Nombre de tu campo
>
> }
> }
> }
>
> After this investigation, we appreciated your feedback if there's any
> version where this issue we've identified has been resolved, or how we can
> make file uploads work properly.
>
> Regards
> JMC
>
>
> El mié, 14 may 2025 a las 0:09, Lukasz Lenart (<lu...@apache.org>)
> escribió:
>
>> sob., 10 maj 2025 o 07:09 Jesus Moreno<je...@gmail.com> napisał(a):
>>> 2025-05-09 01:00:25 WARNFile:79 - Struts has detected a file upload UI
>> tag
>>> (s:file) being used without a form set to method 'POST'
>>> (Despite <s:form method="POST" enctype="multipart/form-data"> being
>>> correctly declared)
>> This can be related to cached JSP files, please remove them from the
>> container "temp" folder or "work" folder.
>>
>> https://cwiki.apache.org/confluence/display/WW/Struts+6.x.x+to+7.x.x+migration#Struts6.x.xto7.x.xmigration-Temp/WorkdirectoryofApplicationServer/ServletContainer
>>
>>> struts.xml:
>>> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts
>>> Configuration 6.5//EN"
>>> "https://struts.apache.org/dtds/struts-6.5.dtd">
>> There is no such version as 6.5, use 6.0 instead
>> https://struts.apache.org/dtds/
>>
>>
>> Cheers
>> Łukasz
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail:user-unsubscribe@struts.apache.org
>> For additional commands, e-mail:user-help@struts.apache.org
>>
>>
Re: Issue with Namespace Configuration
Posted by Joseph Huynh <jo...@yahoo.com.INVALID>.
Hi Lukasz,
Sorry, it was a false alarm to report Struts2 failed to dispatch in case of duplicate names in the same namespace. I used System.out to monitor the flow and I didn't see what I expected to see and thought it silently failed. I now switch to log4j2 framework and can verify the flow from both console and log file. It is logical to return viewRegistration after the registration is successfully done.
Thank you for the links! After I get myself comfortable with Struts2 and complete the migration to Struts2 from Struts1 for all our applications, I will definitely work on moving from Ant build to Maven. Thank you again!
Regards,Joseph
On Wednesday, June 11, 2025 at 01:20:51 AM EDT, Lukasz Lenart <lu...@apache.org> wrote:
śr., 11 cze 2025 o 00:59 Joseph Huynh <jo...@yahoo.com.invalid>
napisał(a):
>
> Hi Lukasz,
> I just figured out that I had bundled struts.xml in wrong place in WEB-INF/ instead of in WEB-INF/classes/. It is dispatching correctly now. My struts.xml is mostly fine except for the duplicate result name in the same namespace. I think Struts2 fails (silently) to dispatch in this case because it won't do anything, no errors either.
Duplicated result names don't matter, result names are "action local".
This means you can have an action named "viewRegistration" and at the
same time another action could have a result named "viewRegistration".
Basically the flow is as: http request -> action (by name) -> result
(by name for this action)
> I would love to learn to use Maven. I've tried several times in the past but not successful.
Just install Maven and use archetypes to generate your application,
all modern IDEs support Maven out-of-box. Follow these steps
https://struts.apache.org/maven-archetypes/
https://struts.apache.org/getting-started/
If unclear please let me know and I will update the docs.
Cheers
Lukasz
---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org
Re: Issue with Namespace Configuration
Posted by Lukasz Lenart <lu...@apache.org>.
śr., 11 cze 2025 o 00:59 Joseph Huynh <jo...@yahoo.com.invalid>
napisał(a):
>
> Hi Lukasz,
> I just figured out that I had bundled struts.xml in wrong place in WEB-INF/ instead of in WEB-INF/classes/. It is dispatching correctly now. My struts.xml is mostly fine except for the duplicate result name in the same namespace. I think Struts2 fails (silently) to dispatch in this case because it won't do anything, no errors either.
Duplicated result names don't matter, result names are "action local".
This means you can have an action named "viewRegistration" and at the
same time another action could have a result named "viewRegistration".
Basically the flow is as: http request -> action (by name) -> result
(by name for this action)
> I would love to learn to use Maven. I've tried several times in the past but not successful.
Just install Maven and use archetypes to generate your application,
all modern IDEs support Maven out-of-box. Follow these steps
https://struts.apache.org/maven-archetypes/
https://struts.apache.org/getting-started/
If unclear please let me know and I will update the docs.
Cheers
Lukasz
---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org
Re: Issue with Namespace Configuration
Posted by Joseph Huynh <jo...@yahoo.com.INVALID>.
Hi Lukasz,
I just figured out that I had bundled struts.xml in wrong place in WEB-INF/ instead of in WEB-INF/classes/. It is dispatching correctly now. My struts.xml is mostly fine except for the duplicate result name in the same namespace. I think Struts2 fails (silently) to dispatch in this case because it won't do anything, no errors either.
I would love to learn to use Maven. I've tried several times in the past but not successful.
Thanks,Joseph H.
On Tuesday, June 10, 2025 at 03:25:44 AM EDT, Lukasz Lenart <lu...@apache.org> wrote:
Is there a chance to expose source code of your app on Github? And I
would suggest learning how to use Maven to build the app instead of
Ant.
Cheers
Lukasz
pt., 6 cze 2025 o 18:49 Joseph Huynh <jo...@yahoo.com.invalid>
napisał(a):
>
> Hello,
> I have a simple web application to learn how the controller works in Struts framework. The build is deployed to Tomcat 11.0.5. The application has two simple JSP files PgStudent.jsp (configured as the welcome page) and PgRegistration.jsp. Each page has two buttons to let user go back and forth between the two pages. It loads the welcome page PgStudent fine, but any request will encounter web error 404, like this one: There is no Action mapped for namespace [/] and action name [viewRegistration] associated with context path [/s2ex1], or from localhost_access log: 0:0:0:0:0:0:0:1 - - [06/Jun/2025:12:12:28 -0400] "POST /s2ex1/viewRegistration HTTP/1.1" 404 829.
> Please help see where I do it wrong. Namespace is specified in JSP files, but it always sees namespace as /. I am attaching the project file structure and the list of the files:
> Project Files Structure
> s2ex1 lib caffeine-3.1.8.jar freemarker-2.3.34.jar jakarta.servlet-api-5.0.0.jar (to compile only) ognl-3.3.5.jar struts2-core-7.0.3.jar xwork-core-2.3.35.jar src s2ex1 RegistrationAction.java StudentAction.java WebContent jsp PgRegistration.jsp PgStudent.jsp WEB-INF struts.xml web.xml build.xml
> List of the 7 files
> 1. struts.xml<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "https://struts.apache.org/dtds/struts-2.0.dtd"><struts> <constant name="struts.devMode" value="true"/> <!-- student namespace --> <package name="student" namespace="/student" extends="struts-default"> <action name="viewStudent" class="s2ex1.StudentAction" method="viewStudent"> <result name="viewStudent">/jsp/PgStudent.jsp</result> </action> <action name="updateStudent" class="s2ex1.StudentAction" method="updateStudent"> <result name="viewStudent">/jsp/PgStudent.jsp</result> </action> </package>
> <!-- registration namespace --> <package name="registration" namespace="/registration" extends="struts-default"> <action name="viewRegistration" class="s2ex1.RegistrationAction" method="viewRegistration"> <result name="viewRegistration">/jsp/PgRegistration.jsp</result> </action>
> <action name="register" class="s2ex1.RegistrationAction" method="register"> <result name="viewRegistration">/jsp/PgRegistration.jsp</result> </action> </package></struts>
> 2. web.xml<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd" version="5.0">
> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter>
> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
> <!-- Set PgStudent.jsp as the default page --> <welcome-file-list> <welcome-file>jsp/PgStudent.jsp</welcome-file> </welcome-file-list></web-app>
> 3. PgRegistration.jsp<%@ taglib prefix="s" uri="/struts-tags" %><html><head> <title>Registration Records</title></head><body> <h2>Registration Records</h2>
> <s:form namespace="/registration" action="register" method="post"> <s:submit value="Register for Class"/> </s:form>
> <s:form namespace="/student" action="viewStudent" method="post"> <s:submit value="Return to Student Section"/> </s:form></body></html>
> 4. PgStudent.jsp<%@ taglib prefix="s" uri="/struts-tags" %>
> <html><head> <title>Student Info</title></head><body> <h2>Student - Contact Information</h2>
> <s:form namespace="/student" action="updateStudent" method="post"> <s:submit value="Update Contact Info"/> </s:form>
> <s:form namespace="/registration" action="viewRegistration" method="post"> <s:submit value="View Registrations"/> </s:form></body></html>
> 5. RegistrationAction.javapackage s2ex1;
> import jakarta.servlet.http.HttpServletRequest;//import jakarta.servlet.http.HttpSession;
> import org.apache.struts2.ActionSupport;import org.apache.struts2.action.ServletRequestAware;
> public class RegistrationAction extends ActionSupport implements ServletRequestAware { private static final long serialVersionUID = -4447001525367432296L; private HttpServletRequest request;
> @Override public void withServletRequest(HttpServletRequest request) { this.request = request; } public HttpServletRequest getWithServletRequest() { return this.request; } public String viewRegistration() { return "viewRegistration"; } public String register() { return "viewRegistration"; } public String viewStudent() { return "viewStudent"; }}
> 6. StudentAction.javapackage s2ex1;
> import jakarta.servlet.http.HttpServletRequest;//import jakarta.servlet.http.HttpSession;
> import org.apache.struts2.ActionSupport;import org.apache.struts2.action.ServletRequestAware;
> public class StudentAction extends ActionSupport implements ServletRequestAware { private static final long serialVersionUID = -4056538155914479859L; private HttpServletRequest request;
> @Override public void withServletRequest(HttpServletRequest request) { this.request = request; }
> public HttpServletRequest getWithServletRequest() { return this.request; } public String viewStudent() { return "viewStudent"; } public String updateStudent() { return "viewStudent"; } public String viewRegistration() { return "viewRegistration"; }}
> 7. build.xml<project name="s2ex1" default="war" basedir="."> <!-- Define global properties --> <property name="src.dir" value="src"/> <property name="web.dir" value="WebContent"/> <property name="build.dir" value="build"/> <property name="classes.dir" value="${build.dir}/classes"/> <property name="war.file" value="s2ex1.war"/> <!-- All external libraries --> <property name="lib.dir" value="${basedir}/lib"/>
> <!-- Clean --> <target name="clean"> <delete dir="${build.dir}"/> <delete file="${war.file}"/> </target>
> <!-- Compile Java sources --> <target name="compile"> <mkdir dir="${classes.dir}"/> <javac srcdir="${src.dir}" destdir="${classes.dir}" includeantruntime="false" source="21" target="21" > <classpath> <fileset dir="${lib.dir}"> <include name="*.jar"/> </fileset> </classpath> </javac> </target>
> <!-- Prepare WAR directory --> <target name="prepare"> <mkdir dir="${build.dir}/WebContent"/> <copy todir="${build.dir}/WebContent"> <fileset dir="${web.dir}"/> </copy> <copy file="WebContent/WEB-INF/struts.xml" todir="${build.dir}/WebContent/WEB-INF"/> <copy todir="${build.dir}/WebContent/WEB-INF/classes"> <fileset dir="${classes.dir}"/> </copy> <copy todir="${build.dir}/WebContent/WEB-INF/lib"> <fileset dir="${lib.dir}/"> <include name="struts2-core-7.0.3.jar"/> <include name="freemarker-2.3.34.jar"/> <include name="ognl-3.3.5.jar"/> <include name="caffeine-3.1.8.jar"/> <include name="xwork-core-2.3.35.jar"/> </fileset> </copy> </target>
> <!-- Create WAR file --> <target name="war" depends="clean, compile, prepare"> <mkdir dir="${build.dir}/WebContent"/> <war destfile="${war.file}" webxml="${build.dir}/WebContent/WEB-INF/web.xml"> <fileset dir="${build.dir}/WebContent"/> </war> <echo message="Build file created: ${war.file}"/> </target>
> </project>
> Thank you,Joseph H.
---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org
Re: Issue with Namespace Configuration
Posted by Lukasz Lenart <lu...@apache.org>.
Is there a chance to expose source code of your app on Github? And I
would suggest learning how to use Maven to build the app instead of
Ant.
Cheers
Lukasz
pt., 6 cze 2025 o 18:49 Joseph Huynh <jo...@yahoo.com.invalid>
napisał(a):
>
> Hello,
> I have a simple web application to learn how the controller works in Struts framework. The build is deployed to Tomcat 11.0.5. The application has two simple JSP files PgStudent.jsp (configured as the welcome page) and PgRegistration.jsp. Each page has two buttons to let user go back and forth between the two pages. It loads the welcome page PgStudent fine, but any request will encounter web error 404, like this one: There is no Action mapped for namespace [/] and action name [viewRegistration] associated with context path [/s2ex1], or from localhost_access log: 0:0:0:0:0:0:0:1 - - [06/Jun/2025:12:12:28 -0400] "POST /s2ex1/viewRegistration HTTP/1.1" 404 829.
> Please help see where I do it wrong. Namespace is specified in JSP files, but it always sees namespace as /. I am attaching the project file structure and the list of the files:
> Project Files Structure
> s2ex1 lib caffeine-3.1.8.jar freemarker-2.3.34.jar jakarta.servlet-api-5.0.0.jar (to compile only) ognl-3.3.5.jar struts2-core-7.0.3.jar xwork-core-2.3.35.jar src s2ex1 RegistrationAction.java StudentAction.java WebContent jsp PgRegistration.jsp PgStudent.jsp WEB-INF struts.xml web.xml build.xml
> List of the 7 files
> 1. struts.xml<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "https://struts.apache.org/dtds/struts-2.0.dtd"><struts> <constant name="struts.devMode" value="true"/> <!-- student namespace --> <package name="student" namespace="/student" extends="struts-default"> <action name="viewStudent" class="s2ex1.StudentAction" method="viewStudent"> <result name="viewStudent">/jsp/PgStudent.jsp</result> </action> <action name="updateStudent" class="s2ex1.StudentAction" method="updateStudent"> <result name="viewStudent">/jsp/PgStudent.jsp</result> </action> </package>
> <!-- registration namespace --> <package name="registration" namespace="/registration" extends="struts-default"> <action name="viewRegistration" class="s2ex1.RegistrationAction" method="viewRegistration"> <result name="viewRegistration">/jsp/PgRegistration.jsp</result> </action>
> <action name="register" class="s2ex1.RegistrationAction" method="register"> <result name="viewRegistration">/jsp/PgRegistration.jsp</result> </action> </package></struts>
> 2. web.xml<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd" version="5.0">
> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter>
> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
> <!-- Set PgStudent.jsp as the default page --> <welcome-file-list> <welcome-file>jsp/PgStudent.jsp</welcome-file> </welcome-file-list></web-app>
> 3. PgRegistration.jsp<%@ taglib prefix="s" uri="/struts-tags" %><html><head> <title>Registration Records</title></head><body> <h2>Registration Records</h2>
> <s:form namespace="/registration" action="register" method="post"> <s:submit value="Register for Class"/> </s:form>
> <s:form namespace="/student" action="viewStudent" method="post"> <s:submit value="Return to Student Section"/> </s:form></body></html>
> 4. PgStudent.jsp<%@ taglib prefix="s" uri="/struts-tags" %>
> <html><head> <title>Student Info</title></head><body> <h2>Student - Contact Information</h2>
> <s:form namespace="/student" action="updateStudent" method="post"> <s:submit value="Update Contact Info"/> </s:form>
> <s:form namespace="/registration" action="viewRegistration" method="post"> <s:submit value="View Registrations"/> </s:form></body></html>
> 5. RegistrationAction.javapackage s2ex1;
> import jakarta.servlet.http.HttpServletRequest;//import jakarta.servlet.http.HttpSession;
> import org.apache.struts2.ActionSupport;import org.apache.struts2.action.ServletRequestAware;
> public class RegistrationAction extends ActionSupport implements ServletRequestAware { private static final long serialVersionUID = -4447001525367432296L; private HttpServletRequest request;
> @Override public void withServletRequest(HttpServletRequest request) { this.request = request; } public HttpServletRequest getWithServletRequest() { return this.request; } public String viewRegistration() { return "viewRegistration"; } public String register() { return "viewRegistration"; } public String viewStudent() { return "viewStudent"; }}
> 6. StudentAction.javapackage s2ex1;
> import jakarta.servlet.http.HttpServletRequest;//import jakarta.servlet.http.HttpSession;
> import org.apache.struts2.ActionSupport;import org.apache.struts2.action.ServletRequestAware;
> public class StudentAction extends ActionSupport implements ServletRequestAware { private static final long serialVersionUID = -4056538155914479859L; private HttpServletRequest request;
> @Override public void withServletRequest(HttpServletRequest request) { this.request = request; }
> public HttpServletRequest getWithServletRequest() { return this.request; } public String viewStudent() { return "viewStudent"; } public String updateStudent() { return "viewStudent"; } public String viewRegistration() { return "viewRegistration"; }}
> 7. build.xml<project name="s2ex1" default="war" basedir="."> <!-- Define global properties --> <property name="src.dir" value="src"/> <property name="web.dir" value="WebContent"/> <property name="build.dir" value="build"/> <property name="classes.dir" value="${build.dir}/classes"/> <property name="war.file" value="s2ex1.war"/> <!-- All external libraries --> <property name="lib.dir" value="${basedir}/lib"/>
> <!-- Clean --> <target name="clean"> <delete dir="${build.dir}"/> <delete file="${war.file}"/> </target>
> <!-- Compile Java sources --> <target name="compile"> <mkdir dir="${classes.dir}"/> <javac srcdir="${src.dir}" destdir="${classes.dir}" includeantruntime="false" source="21" target="21" > <classpath> <fileset dir="${lib.dir}"> <include name="*.jar"/> </fileset> </classpath> </javac> </target>
> <!-- Prepare WAR directory --> <target name="prepare"> <mkdir dir="${build.dir}/WebContent"/> <copy todir="${build.dir}/WebContent"> <fileset dir="${web.dir}"/> </copy> <copy file="WebContent/WEB-INF/struts.xml" todir="${build.dir}/WebContent/WEB-INF"/> <copy todir="${build.dir}/WebContent/WEB-INF/classes"> <fileset dir="${classes.dir}"/> </copy> <copy todir="${build.dir}/WebContent/WEB-INF/lib"> <fileset dir="${lib.dir}/"> <include name="struts2-core-7.0.3.jar"/> <include name="freemarker-2.3.34.jar"/> <include name="ognl-3.3.5.jar"/> <include name="caffeine-3.1.8.jar"/> <include name="xwork-core-2.3.35.jar"/> </fileset> </copy> </target>
> <!-- Create WAR file --> <target name="war" depends="clean, compile, prepare"> <mkdir dir="${build.dir}/WebContent"/> <war destfile="${war.file}" webxml="${build.dir}/WebContent/WEB-INF/web.xml"> <fileset dir="${build.dir}/WebContent"/> </war> <echo message="Build file created: ${war.file}"/> </target>
> </project>
> Thank you,Joseph H.
---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org
Aw: Issue with Namespace Configuration
Posted by Ute Kaiser <ut...@web.de.INVALID>.
Hi Joseph,
sorry I can not help with your namespace issues but I would update
struts.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"http://struts.apache.org/dtds/struts-6.0.dtd">
and get rid of xwork-core-2.3.35.jar which is not part of struts 7.0.3 any more.
Best regards
Ute
---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org
Issue with Namespace Configuration
Posted by Joseph Huynh <jo...@yahoo.com.INVALID>.
Hello,
I have a simple web application to learn how the controller works in Struts framework. The build is deployed to Tomcat 11.0.5. The application has two simple JSP files PgStudent.jsp (configured as the welcome page) and PgRegistration.jsp. Each page has two buttons to let user go back and forth between the two pages. It loads the welcome page PgStudent fine, but any request will encounter web error 404, like this one: There is no Action mapped for namespace [/] and action name [viewRegistration] associated with context path [/s2ex1], or from localhost_access log: 0:0:0:0:0:0:0:1 - - [06/Jun/2025:12:12:28 -0400] "POST /s2ex1/viewRegistration HTTP/1.1" 404 829.
Please help see where I do it wrong. Namespace is specified in JSP files, but it always sees namespace as /. I am attaching the project file structure and the list of the files:
Project Files Structure
s2ex1 lib caffeine-3.1.8.jar freemarker-2.3.34.jar jakarta.servlet-api-5.0.0.jar (to compile only) ognl-3.3.5.jar struts2-core-7.0.3.jar xwork-core-2.3.35.jar src s2ex1 RegistrationAction.java StudentAction.java WebContent jsp PgRegistration.jsp PgStudent.jsp WEB-INF struts.xml web.xml build.xml
List of the 7 files
1. struts.xml<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "https://struts.apache.org/dtds/struts-2.0.dtd"><struts> <constant name="struts.devMode" value="true"/> <!-- student namespace --> <package name="student" namespace="/student" extends="struts-default"> <action name="viewStudent" class="s2ex1.StudentAction" method="viewStudent"> <result name="viewStudent">/jsp/PgStudent.jsp</result> </action> <action name="updateStudent" class="s2ex1.StudentAction" method="updateStudent"> <result name="viewStudent">/jsp/PgStudent.jsp</result> </action> </package>
<!-- registration namespace --> <package name="registration" namespace="/registration" extends="struts-default"> <action name="viewRegistration" class="s2ex1.RegistrationAction" method="viewRegistration"> <result name="viewRegistration">/jsp/PgRegistration.jsp</result> </action>
<action name="register" class="s2ex1.RegistrationAction" method="register"> <result name="viewRegistration">/jsp/PgRegistration.jsp</result> </action> </package></struts>
2. web.xml<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd" version="5.0">
<filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter>
<filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
<!-- Set PgStudent.jsp as the default page --> <welcome-file-list> <welcome-file>jsp/PgStudent.jsp</welcome-file> </welcome-file-list></web-app>
3. PgRegistration.jsp<%@ taglib prefix="s" uri="/struts-tags" %><html><head> <title>Registration Records</title></head><body> <h2>Registration Records</h2>
<s:form namespace="/registration" action="register" method="post"> <s:submit value="Register for Class"/> </s:form>
<s:form namespace="/student" action="viewStudent" method="post"> <s:submit value="Return to Student Section"/> </s:form></body></html>
4. PgStudent.jsp<%@ taglib prefix="s" uri="/struts-tags" %>
<html><head> <title>Student Info</title></head><body> <h2>Student - Contact Information</h2>
<s:form namespace="/student" action="updateStudent" method="post"> <s:submit value="Update Contact Info"/> </s:form>
<s:form namespace="/registration" action="viewRegistration" method="post"> <s:submit value="View Registrations"/> </s:form></body></html>
5. RegistrationAction.javapackage s2ex1;
import jakarta.servlet.http.HttpServletRequest;//import jakarta.servlet.http.HttpSession;
import org.apache.struts2.ActionSupport;import org.apache.struts2.action.ServletRequestAware;
public class RegistrationAction extends ActionSupport implements ServletRequestAware { private static final long serialVersionUID = -4447001525367432296L; private HttpServletRequest request;
@Override public void withServletRequest(HttpServletRequest request) { this.request = request; } public HttpServletRequest getWithServletRequest() { return this.request; } public String viewRegistration() { return "viewRegistration"; } public String register() { return "viewRegistration"; } public String viewStudent() { return "viewStudent"; }}
6. StudentAction.javapackage s2ex1;
import jakarta.servlet.http.HttpServletRequest;//import jakarta.servlet.http.HttpSession;
import org.apache.struts2.ActionSupport;import org.apache.struts2.action.ServletRequestAware;
public class StudentAction extends ActionSupport implements ServletRequestAware { private static final long serialVersionUID = -4056538155914479859L; private HttpServletRequest request;
@Override public void withServletRequest(HttpServletRequest request) { this.request = request; }
public HttpServletRequest getWithServletRequest() { return this.request; } public String viewStudent() { return "viewStudent"; } public String updateStudent() { return "viewStudent"; } public String viewRegistration() { return "viewRegistration"; }}
7. build.xml<project name="s2ex1" default="war" basedir="."> <!-- Define global properties --> <property name="src.dir" value="src"/> <property name="web.dir" value="WebContent"/> <property name="build.dir" value="build"/> <property name="classes.dir" value="${build.dir}/classes"/> <property name="war.file" value="s2ex1.war"/> <!-- All external libraries --> <property name="lib.dir" value="${basedir}/lib"/>
<!-- Clean --> <target name="clean"> <delete dir="${build.dir}"/> <delete file="${war.file}"/> </target>
<!-- Compile Java sources --> <target name="compile"> <mkdir dir="${classes.dir}"/> <javac srcdir="${src.dir}" destdir="${classes.dir}" includeantruntime="false" source="21" target="21" > <classpath> <fileset dir="${lib.dir}"> <include name="*.jar"/> </fileset> </classpath> </javac> </target>
<!-- Prepare WAR directory --> <target name="prepare"> <mkdir dir="${build.dir}/WebContent"/> <copy todir="${build.dir}/WebContent"> <fileset dir="${web.dir}"/> </copy> <copy file="WebContent/WEB-INF/struts.xml" todir="${build.dir}/WebContent/WEB-INF"/> <copy todir="${build.dir}/WebContent/WEB-INF/classes"> <fileset dir="${classes.dir}"/> </copy> <copy todir="${build.dir}/WebContent/WEB-INF/lib"> <fileset dir="${lib.dir}/"> <include name="struts2-core-7.0.3.jar"/> <include name="freemarker-2.3.34.jar"/> <include name="ognl-3.3.5.jar"/> <include name="caffeine-3.1.8.jar"/> <include name="xwork-core-2.3.35.jar"/> </fileset> </copy> </target>
<!-- Create WAR file --> <target name="war" depends="clean, compile, prepare"> <mkdir dir="${build.dir}/WebContent"/> <war destfile="${war.file}" webxml="${build.dir}/WebContent/WEB-INF/web.xml"> <fileset dir="${build.dir}/WebContent"/> </war> <echo message="Build file created: ${war.file}"/> </target>
</project>
Thank you,Joseph H.
Re: struts-tag property in javascript
Posted by Dave Newton <da...@gmail.com>.
On Tue, Jun 10, 2025 at 13:37 Lukasz Lenart <lu...@apache.org> wrote:
> Nope, basically ${} means "use JSP EL" which is prohibited "inside" S2
> tags, using %{} means "use OGNL expression" which is fine.
Oops, forgot about that smh
d
Re: struts-tag property in javascript
Posted by Lukasz Lenart <lu...@apache.org>.
wt., 10 cze 2025 o 18:23 Ute Kaiser <ut...@web.de.invalid> napisał(a):
>
> Update after more investigating Nate's answer:
>
> <s:submit type="input" value="Delete"
> onclick="document.getElementById('deleteid').value='%{id}';">
>
> Using ${id} threw an error (I suppose my config not fit for JSP EL)
Nope, basically ${} means "use JSP EL" which is prohibited "inside" S2
tags, using %{} means "use OGNL expression" which is fine.
Cheers
Łukasz
---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org
RE: struts-tag property in javascript
Posted by Ute Kaiser <ut...@web.de.INVALID>.
Update after more investigating Nate's answer:
<s:submit type="input" value="Delete"
onclick="document.getElementById('deleteid').value='%{id}';">
Using ${id} threw an error (I suppose my config not fit for JSP EL)
Best regards
Ute
---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org
Aw: RE: struts-tag property in javascript
Posted by Ute Kaiser <ut...@web.de.INVALID>.
Hi and thx for your fast answers.
I am aware that migration could go much deeper in cleaning up but rewriting the action und jsp files is fissel work enough at the moment.
Dave's answer "But you should be able use standard JSP EL iirc." inspired me.
First I did not understand (never used EL), then I applied part of it "But you should be able use standard JSP".
And so, I found a working solution:
The error was caused by:
s:submit type="input" value="Delete"
Using plain html
input type="button" value="Delete" (or type=submit")
works fine with
onclick="javascript:if(confirmDelete()){document.getElementById('deleteid').value='<s:property value="id"/>';}"/>
Thx for the inspiration
Ute
---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org
RE: struts-tag property in javascript
Posted by Nate Kerkhofs <Na...@ikan.be>.
Hi Ute,
I would recommend you split up your JavaScript into separate files as well, but if you ABSOLUTELY have to write a property value inside a Struts tag, the best way to do that is probably through an OGNL or JSTL expression. In your case, you can replace the s:property tag inside the onclick by %{id} or ${id} and it probably should work. I am unable to test your exact case, but I have such expressions inside my own code and they work fine.
But again, I would strongly recommend you split your JavaScript into a separate file. If there are any properties you need to populate from server values, you can put those in a separate script tag inside the head of your page using s:property tags:
<script>
var jsProperty = <s:property value="id" />;
</script>
Nate
-----Original Message-----
From: Dave Newton <da...@gmail.com>
Sent: Friday, 6 June 2025 15:58
To: Struts Users Mailing List <us...@struts.apache.org>
Subject: Re: struts-tag property in javascript
On Fri, Jun 6, 2025 at 09:14 Ute Kaiser <ut...@web.de.invalid> wrote:
> Hi,
> migrating from Struts1 I encounter in a jsp the error "Equal symbol
> expected" at the "onclick" row.
> I think my old quotes do not work any more with Struts2.
>
> Struts1:
> <logic:iterate id="b" name="blabla" scope="request">
> <input type="button" value="Delete"
>
> onclick="javascript:if(confirmDelete()){document.getElementById('delet
> eid').value='<bean:write
> name="b" property="id" />';}"/>
>
> Struts2:
> <s:iterator value="blabla">
> <s:submit type="input" value="Delete"
>
> onclick="javascript:if(confirmDelete()){document.getElementById('delet
> eid').value='<s:property
> value="id"/>';}"/>
>
> I tried 'id', \"id\", \'id\' "id" but nothing worked out
This would be double-evaluation, JSP doesn’t work like this—tags can’t used as property values. But you should be able use standard JSP EL iirc.
That said, I’d take the opportunity to clean up JSPs and JS since you’re rewriting everything anyway and split up the JS and markup if you’re not also migrating the front end away from JSP.
Dave
Re: struts-tag property in javascript
Posted by Dave Newton <da...@gmail.com>.
On Fri, Jun 6, 2025 at 09:14 Ute Kaiser <ut...@web.de.invalid> wrote:
> Hi,
> migrating from Struts1 I encounter in a jsp the error "Equal symbol
> expected" at the "onclick" row.
> I think my old quotes do not work any more with Struts2.
>
> Struts1:
> <logic:iterate id="b" name="blabla" scope="request">
> <input type="button" value="Delete"
>
> onclick="javascript:if(confirmDelete()){document.getElementById('deleteid').value='<bean:write
> name="b" property="id" />';}"/>
>
> Struts2:
> <s:iterator value="blabla">
> <s:submit type="input" value="Delete"
>
> onclick="javascript:if(confirmDelete()){document.getElementById('deleteid').value='<s:property
> value="id"/>';}"/>
>
> I tried 'id', \"id\", \'id\' "id" but nothing worked out
This would be double-evaluation, JSP doesn’t work like this—tags can’t used
as property values. But you should be able use standard JSP EL iirc.
That said, I’d take the opportunity to clean up JSPs and JS since you’re
rewriting everything anyway and split up the JS and markup if you’re not
also migrating the front end away from JSP.
Dave
struts-tag property in javascript
Posted by Ute Kaiser <ut...@web.de.INVALID>.
Hi,
migrating from Struts1 I encounter in a jsp the error "Equal symbol expected" at the "onclick" row.
I think my old quotes do not work any more with Struts2.
Struts1:
<logic:iterate id="b" name="blabla" scope="request">
<input type="button" value="Delete"
onclick="javascript:if(confirmDelete()){document.getElementById('deleteid').value='<bean:write name="b" property="id" />';}"/>
Struts2:
<s:iterator value="blabla">
<s:submit type="input" value="Delete"
onclick="javascript:if(confirmDelete()){document.getElementById('deleteid').value='<s:property value="id"/>';}"/>
I tried 'id', \"id\", \'id\' "id" but nothing worked out
Any advice appreciated (hopefully not too complicated since I have this row in many jsp files)
Ute
---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org
Re: File Upload Failure in Struts 7.0.3 Migration - UploadedFilesAware Implementation Issue
Posted by Jesus Moreno <je...@gmail.com>.
Thank you Zoran and I appreciate your assistance, but I believe we are
tackling the problem with a very small application, whereas what we
actually need is to implement or replace the file upload functionality from
version 2.5 to version 7.0.3 in a larger application, where we already have
almost 100% of the issues resolved. We just need to fix the file upload
problem. We will keep investigating—perhaps we should try the same approach
but with an older version of Struts.
JMC
El mar, 3 jun 2025 a las 19:14, Zoran Avtarovski (<zo...@sparecreative.com>)
escribió:
> Thanks for that.
>
> It doesn't look like you have the same issue we did. I wish I could be
> of more help.
>
> I can only suggest start with example app working and then implement
> your requirements on a step by step basis.
>
> In the end that's what helped us identify the issue and get it working.
>
> Z.
>
>
>
>
> On 4/6/2025 4:56 am, Jesus Moreno wrote:
> > Sure, my web.xml looks like this:
> >
> > <?xml version="1.0" encoding="UTF-8"?>
> > <web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
> > xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
> > xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
> > https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
> > version="6.0">
> > <display-name>Struts Blank</display-name>
> > <!-- 1. Context Parameter para Log4j2 -->
> > <context-param>
> > <param-name>log4jConfiguration</param-name>
> > <param-value>/WEB-INF/classes/log4j2.xml</param-value>
> > </context-param>
> > <filter>
> > <filter-name>struts2</filter-name>
> >
> >
> <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
> > </filter>
> > <filter-mapping>
> > <filter-name>struts2</filter-name>
> > <url-pattern>/*</url-pattern>
> > </filter-mapping>
> > </web-app>
> >
> > I forgot also show my struts.xml:
> >
> > <?xml version="1.0" encoding="UTF-8" ?>
> > <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts
> > Configuration 6.0//EN"
> > "https://struts.apache.org/dtds/struts-6.0.dtd">
> > <struts>
> > <constant name="struts.ui.theme" value="simple"/> <!-- En
> struts.xml
> > -->
> > <constant name="struts.ui.templateDir" value="template"/>
> > <constant name="struts.ui.templateSuffix" value="ftl"/>
> > <constant name="struts.devMode" value="true"/>
> > <constant name="struts.enable.DynamicMethodInvocation"
> value="false"/>
> > <constant name="struts.logging.fileUploadInterceptor"
> value="TRACE"/>
> > <constant name="struts.logging.level" value="TRACE"/>
> > <constant name="struts.multipart.parser"
> > value="mx.com.struts.action.DebugMultiPartRequest"/> <!-- Using my
> custom
> > DebugMultiPartRequest -->
> >
> > <package name="default" extends="struts-default">
> >
> > <action name="">
> > <result>/index.jsp</result>
> > </action>
> >
> > <action name="hello" class="mx.com.struts.action.HelloAction">
> > <result name="success">/success.jsp</result>
> > <result name="error">/error.jsp</result>
> > </action>
> >
> > </package>
> >
> > </struts>
> >
> > On the log I can see that I don't have file on my action:
> >
> > 2025-06-03 12:48:20 DEBUG ActionFileUploadInterceptor:200 - Found
> multipart
> > request: MultiPartRequestWrapper
> > 2025-06-03 12:48:20 DEBUG ActionFileUploadInterceptor:179 - No files have
> > been uploaded/accepted
> >
> > Now I can see is blocked!. I was reading that this message is normal on
> the
> > log:
> >
> > 2025-06-03 12:48:21 DEBUG DefaultAcceptedPatternsChecker:115 - Sets
> > accepted patterns to
> >
> [[\w+((\.\w+)|(\[\d+])|(\(\d+\))|(\['(\w-?|[\u4e00-\u9fa5]-?)+'])|(\('(\w-?|[\u4e00-\u9fa5]-?)+'\)))*]],
> > note this impacts the safety of your application!
> > 2025-06-03 12:48:21 DEBUG StrutsUrlEncoder:38 - Using default encoding:
> > UTF-8
> > 2025-06-03 12:48:21 DEBUG SecurityMemberAccess:142 - Checking access for
> > [target: mx.com.struts.action.HelloAction@4489b4fa, member: private
> > org.apache.struts2.dispatcher.multipart.UploadedFile
> > mx.com.struts.action.HelloAction.miLogo, property: miLogo]
> > 2025-06-03 12:48:21 WARN SecurityMemberAccess:174 - Access to non-public
> > [private org.apache.struts2.dispatcher.multipart.UploadedFile
> > mx.com.struts.action.HelloAction.miLogo] is blocked!
> > 2025-06-03 12:48:21 DEBUG InstantiatingNullHandler:106 - Entering
> > nullPropertyValue [target=[mx.com.struts.action.HelloAction@4489b4fa,
> > org.apache.struts2.text.DefaultTextProvider@287a1ff7], property=miLogo]
> >
> > As comment I changed file name as miLogo because I was trying use a
> > different name like upload.
> >
> > Thanks for the support.
> >
> > JMC
> >
> >
> > El lun, 2 jun 2025 a las 23:44, Zoran Avtarovski (<
> zoran@sparecreative.com>)
> > escribió:
> >
> >> Hi Jesus,
> >>
> >> What does your web.xml look like with regards to the struts filters? We
> >> had a similar issue which was resolved by moving to the combined
> >> StrutsPrepareAndExecuteFilter. Sitemesh also played a part in our issue,
> >> but I think by what you are seeing it's worth trying.
> >>
> >> Z.
> >>
> >> On 1/6/2025 6:29 am, Jesus Moreno wrote:
> >>> Hi Lukasz, thanks for your appreciated comments.
> >>>
> >>> I would like to explain in more detail what we have done to try to find
> >> the
> >>> problem and solve it. Here the context:
> >>>
> >>> This is my environment:
> >>>
> >>> - Struts Version: 7.0.3
> >>> - Java Version: 17
> >>> - Servlet Container: Apache Tomcat 10.1.36
> >>> - Key Dependencies (pom):
> >>> - struts2-core:7.0.3
> >>> - struts2-convention-plugin:7.0.3
> >>> - struts2-json-plugin:7.0.3
> >>> - jakarta.servlet-api:5.0.0
> >>> - log4j-core:2.17.2
> >>> - Enforced exclusions: javax.servlet:*, javax.activation:*
> >>>
> >>> We've created a basic Maven project (standard archetype) with a simple
> >> file
> >>> upload form containing:
> >>> - 4 regular text fields (working correctly with
> >>> @StrutsParameter(depth=0))
> >>> - 1 file upload field (<input type="file" name="upload">)
> >>>
> >>> This is my basic jsp:
> >>>
> >>> <%@ page contentType="text/html; charset=UTF-8" %>
> >>> <%@ taglib prefix="s" uri="/struts-tags" %>
> >>> <!DOCTYPE html>
> >>> <html>
> >>> <head>
> >>> <title>File upload result</title>
> >>> </head>
> >>> <body>
> >>> <h1>Hola Mundo desde la tierra...</h1>
> >>> <form action="hello" method="post" enctype="multipart/form-data">
> >>> <input type="text" name="name"/>
> >>> <input type="text" name="lastname"/>
> >>> <input type="text" name="gender"/>
> >>> <input type="text" name="age"/>
> >>> <input type="file" name="*upload*"/>
> >>> <input type="submit" value="Subir" />
> >>> </form>
> >>> </body>
> >>> </html>
> >>>
> >>> Expected Behavior:
> >>>
> >>> - When implementing UploadedFilesAware, the framework should:
> >>> - Detect multipart requests
> >>> - Process uploaded files through withUploadedFiles(List<UploadedFile>)
> >>> - Make files available to the action
> >>>
> >>> Where my action looks like:
> >>>
> >>> import org.apache.logging.log4j.LogManager;
> >>> import org.apache.logging.log4j.Logger;
> >>> import org.apache.struts2.ActionSupport;
> >>> import org.apache.struts2.action.UploadedFilesAware;
> >>> import org.apache.struts2.dispatcher.multipart.UploadedFile;
> >>> import org.apache.struts2.interceptor.parameter.StrutsParameter;
> >>>
> >>> public class HelloAction extends ActionSupport implements
> >>> UploadedFilesAware {
> >>> private static final long serialVersionUID = 1L;
> >>> private Logger logger = LogManager.getLogger(HelloAction.class);
> >>>
> >>> private UploadedFile upload;
> >>> private String uploadFileName;
> >>> private String uploadContentType;
> >>>
> >>> @StrutsParameter(depth = 0)
> >>> public String name;
> >>>
> >>> @StrutsParameter(depth = 0)
> >>> public String lastname;
> >>>
> >>> @StrutsParameter(depth = 0)
> >>> public String gender;
> >>>
> >>> @StrutsParameter(depth = 0)
> >>> public String age;
> >>>
> >>> @Override
> >>> public String execute() {
> >>> logger.info("Executing execute method..." +
> this.uploadFileName);
> >>> return SUCCESS;
> >>> }
> >>> public UploadedFile getUpload() {
> >>> return upload;
> >>> }
> >>> public void setUpload(UploadedFile upload) {
> >>> this.upload = upload;
> >>> }
> >>> public String getUploadFileName() {
> >>> return uploadFileName;
> >>> }
> >>> public void setUploadFileName(String uploadFileName) {
> >>> this.uploadFileName = uploadFileName;
> >>> }
> >>> public String getUploadContentType() {
> >>> return uploadContentType;
> >>> }
> >>> public void setUploadContentType(String uploadContentType) {
> >>> this.uploadContentType = uploadContentType;
> >>> }
> >>> public Logger getLogger() {
> >>> return logger;
> >>> }
> >>> public void setLogger(Logger logger) {
> >>> this.logger = logger;
> >>> }
> >>> public String getName() {
> >>> return name;
> >>> }
> >>> public void setName(String name) {
> >>> this.name = name;
> >>> }
> >>> public String getLastname() {
> >>> return lastname;
> >>> }
> >>> public void setLastname(String lastname) {
> >>> this.lastname = lastname;
> >>> }
> >>> public String getGender() {
> >>> return gender;
> >>> }
> >>> public void setGender(String gender) {
> >>> this.gender = gender;
> >>> }
> >>> public String getAge() {
> >>> return age;
> >>> }
> >>> public void setAge(String age) {
> >>> this.age = age;
> >>> }
> >>>
> >>> @Override
> >>> public void withUploadedFiles(List<UploadedFile> uploadedFiles) {
> >>> if (uploadedFiles != null && !uploadedFiles.isEmpty()) {
> >>> UploadedFile firstFile = uploadedFiles.get(0); // Take
> the
> >> file
> >>> this.upload = firstFile;
> >>> this.uploadFileName = firstFile.getName(); // String
> >>> this.uploadContentType = firstFile.getContentType(); //
> >> String
> >>> logger.info("File name: " + uploadFileName);
> >>> }
> >>> }
> >>>
> >>> Actual Behavior:
> >>> Despite proper multipart request detection:
> >>>
> >>> The interceptor finds the request wrapper:
> >>>
> >>> 2025-05-31 DEBUG ActionFileUploadInterceptor:200 - Found multipart
> >> request:
> >>> MultiPartRequestWrapper
> >>>
> >>> *Our custom DebugMultiPartRequest confirms file presence:*
> >>>
> >>> 2025-05-31 12:00:13 INFO DebugMultiPartRequest:26 - Todos los
> parámetros
> >>> del request:
> >>> 2025-05-31 12:00:13 INFO DebugMultiPartRequest:30 - - nombre: Jesus
> >>> 2025-05-31 12:00:13 INFO DebugMultiPartRequest:30 - - apellido:
> Moreno
> >>> 2025-05-31 12:00:13 INFO DebugMultiPartRequest:30 - - sexo: M
> >>> 2025-05-31 12:00:13 INFO DebugMultiPartRequest:30 - - edad: 50
> >>>
> >>>
> >>>
> >>> *2025-05-31 12:00:13 INFO DebugMultiPartRequest:39 - Part 'upload'
> >>> encontrada:2025-05-31 12:00:13 INFO DebugMultiPartRequest:40 - -
> >> Nombre:
> >>> Adaware.jpg2025-05-31 12:00:13 INFO DebugMultiPartRequest:41 - -
> >> Tamaño:
> >>> 25546*2025-05-31 12:00:13 DEBUG InstantiatingNullHandler:106 - Entering
> >>> nullPropertyValue
> >>> [target=[org.apache.struts2.text.DefaultTextProvider@2a580722],
> >>> property=struts]
> >>> 2025-05-31 12:00:13 DEBUG ConfigurationManager:180 - Checking
> >>> ConfigurationProviders for reload.
> >>> 2025-05-31 12:00:13 DEBUG InstantiatingNullHandler:106 - Entering
> >>> nullPropertyValue
> >>> [target=[org.apache.struts2.text.DefaultTextProvider@2a580722],
> >>> property=struts]
> >>> 2025-05-31 12:00:13 DEBUG DefaultActionProxy:88 - Creating an
> >>> DefaultActionProxy for namespace [/] and action name [hello]
> >>> 2025-05-31 12:00:14 DEBUG DefaultActionInvocation:310 - Executing
> >>> conditional interceptor: ActionFileUploadInterceptor
> >>> 2025-05-31 12:00:14 DEBUG ActionFileUploadInterceptor:200 - Found
> >> multipart
> >>> request: MultiPartRequestWrapper
> >>>
> >>> But the interceptor reports:
> >>>
> >>> *2025-05-31 12:00:14 DEBUG ActionFileUploadInterceptor:179 - No files
> >> have
> >>> been uploaded/accepted*
> >>>
> >>> Investigation Steps Taken:
> >>>
> >>> 1. Verified all prerequisites:
> >>> - Form uses enctype="multipart/form-data"
> >>> - Using DTD 6.0 as suggested
> >>> - Regularly cleaning Tomcat's work directory
> >>> - Confirmed Jakarta EE 9+ compatibility
> >>>
> >>> 2. Created extensive debug instrumentation:
> >>> - Custom DebugMultiPartRequest shows files reach the server
> >>> - Interceptor-level logging confirms the disconnect
> >>>
> >>> Technical Findings:
> >>>
> >>> 1. The MultiPartRequestWrapper appears to lose file references between:
> >>> - Initial parsing (where files are detectable via raw Part API)
> >>> - Interceptor processing phase
> >>>
> >>> 2. Key suspicious behavior:
> >>> - getFileParameterNames() returns empty enumeration
> >>> - Despite request.getPart("upload") working correctly
> >>>
> >>> Finally As you suggested, we've:
> >>>
> >>> - Consistently cleaned deployment directories
> >>> - Verified DTD compatibility
> >>> - Followed migration guides carefully
> >>> - Yet the team remains persistent in investigating this apparent
> >>> framework bug rather than working around it.
> >>>
> >>> # Summary
> >>> Files are received by the server (confirmed via custom
> >>> DebugMultiPartRequest) but ActionFileUploadInterceptor fails to process
> >>> them via UploadedFilesAware interface in Struts 7.0.3 (Jakarta EE
> >> 9/Tomcat
> >>> 10 environment).
> >>>
> >>> This is my custom DebugMultiPartRequest:
> >>>
> >>> public class DebugMultiPartRequest extends JakartaMultiPartRequest {
> >>> private static final Logger logger =
> >>> LogManager.getLogger(DebugMultiPartRequest.class);
> >>> @Override
> >>> public void parse(HttpServletRequest request, String saveDir)
> throws
> >>> IOException {
> >>> super.parse(request, saveDir); // Procesa el request
> normalmente
> >>> logger.info("=== DebugMultiPartRequest (Struts 7) ===");
> >>> // 1. Listar TODOS los parámetros del request (no solo
> archivos)
> >>> logger.info("Todos los parámetros del request:");
> >>> Enumeration<String> paramNames = request.getParameterNames();
> >>> while (paramNames.hasMoreElements()) {
> >>> String name = paramNames.nextElement();
> >>> logger.info(" - " + name + ": " +
> >> request.getParameter(name));
> >>> }
> >>> // 2. Listar partes multipart (alternativa para Jakarta EE 9+)
> >>> if (request instanceof jakarta.servlet.http.HttpServletRequest
> >>> jakartaRequest) {
> >>> jakarta.servlet.http.Part part;
> >>> try {
> >>> part = jakartaRequest.getPart("upload");
> >>> if (part != null) {
> >>>
> >>>
> >>>
> >>> * logger.info<http://logger.info>("Part 'upload' encontrada:");
> >>> logger.info<http://logger.info>(" - Nombre: " +
> >>> part.getSubmittedFileName()); logger.info<http://logger.info>(" -
> >> Tamaño:
> >>> " + part.getSize());* } else {
> >>> logger.info("❌ Parte 'upload' NO encontrada en el
> >> request.");
> >>> }
> >>> } catch (IOException e) {
> >>> // TODO Auto-generated catch block
> >>> e.printStackTrace();
> >>> } catch (ServletException e) {
> >>> // TODO Auto-generated catch block
> >>> e.printStackTrace();
> >>> } // Nombre de tu campo
> >>>
> >>> }
> >>> }
> >>> }
> >>>
> >>> After this investigation, we appreciated your feedback if there's any
> >>> version where this issue we've identified has been resolved, or how we
> >> can
> >>> make file uploads work properly.
> >>>
> >>> Regards
> >>> JMC
> >>>
> >>>
> >>> El mié, 14 may 2025 a las 0:09, Lukasz Lenart (<
> lukaszlenart@apache.org
> >>> )
> >>> escribió:
> >>>
> >>>> sob., 10 maj 2025 o 07:09 Jesus Moreno<je...@gmail.com>
> napisał(a):
> >>>>> 2025-05-09 01:00:25 WARNFile:79 - Struts has detected a file upload
> UI
> >>>> tag
> >>>>> (s:file) being used without a form set to method 'POST'
> >>>>> (Despite <s:form method="POST" enctype="multipart/form-data"> being
> >>>>> correctly declared)
> >>>> This can be related to cached JSP files, please remove them from the
> >>>> container "temp" folder or "work" folder.
> >>>>
> >>>>
> >>
> https://cwiki.apache.org/confluence/display/WW/Struts+6.x.x+to+7.x.x+migration#Struts6.x.xto7.x.xmigration-Temp/WorkdirectoryofApplicationServer/ServletContainer
> >>>>> struts.xml:
> >>>>> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts
> >>>>> Configuration 6.5//EN"
> >>>>> "https://struts.apache.org/dtds/struts-6.5.dtd">
> >>>> There is no such version as 6.5, use 6.0 instead
> >>>> https://struts.apache.org/dtds/
> >>>>
> >>>>
> >>>> Cheers
> >>>> Łukasz
> >>>>
> >>>> ---------------------------------------------------------------------
> >>>> To unsubscribe,e-mail:user-unsubscribe@struts.apache.org
> >>>> For additional commands,e-mail:user-help@struts.apache.org
> >>>>
> >>>>
> >
> >
--
Ing. Jesús Moreno Canseco
Tel.: (55)6377-0600
Cel.: 55-1409-5156
jesusmca@gmail.com <jm...@ismarts.com.mx>
Re: File Upload Failure in Struts 7.0.3 Migration - UploadedFilesAware Implementation Issue
Posted by Zoran Avtarovski <zo...@sparecreative.com>.
Thanks for that.
It doesn't look like you have the same issue we did. I wish I could be
of more help.
I can only suggest start with example app working and then implement
your requirements on a step by step basis.
In the end that's what helped us identify the issue and get it working.
Z.
On 4/6/2025 4:56 am, Jesus Moreno wrote:
> Sure, my web.xml looks like this:
>
> <?xml version="1.0" encoding="UTF-8"?>
> <web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
> xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
> https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
> version="6.0">
> <display-name>Struts Blank</display-name>
> <!-- 1. Context Parameter para Log4j2 -->
> <context-param>
> <param-name>log4jConfiguration</param-name>
> <param-value>/WEB-INF/classes/log4j2.xml</param-value>
> </context-param>
> <filter>
> <filter-name>struts2</filter-name>
>
> <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
> </filter>
> <filter-mapping>
> <filter-name>struts2</filter-name>
> <url-pattern>/*</url-pattern>
> </filter-mapping>
> </web-app>
>
> I forgot also show my struts.xml:
>
> <?xml version="1.0" encoding="UTF-8" ?>
> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts
> Configuration 6.0//EN"
> "https://struts.apache.org/dtds/struts-6.0.dtd">
> <struts>
> <constant name="struts.ui.theme" value="simple"/> <!-- En struts.xml
> -->
> <constant name="struts.ui.templateDir" value="template"/>
> <constant name="struts.ui.templateSuffix" value="ftl"/>
> <constant name="struts.devMode" value="true"/>
> <constant name="struts.enable.DynamicMethodInvocation" value="false"/>
> <constant name="struts.logging.fileUploadInterceptor" value="TRACE"/>
> <constant name="struts.logging.level" value="TRACE"/>
> <constant name="struts.multipart.parser"
> value="mx.com.struts.action.DebugMultiPartRequest"/> <!-- Using my custom
> DebugMultiPartRequest -->
>
> <package name="default" extends="struts-default">
>
> <action name="">
> <result>/index.jsp</result>
> </action>
>
> <action name="hello" class="mx.com.struts.action.HelloAction">
> <result name="success">/success.jsp</result>
> <result name="error">/error.jsp</result>
> </action>
>
> </package>
>
> </struts>
>
> On the log I can see that I don't have file on my action:
>
> 2025-06-03 12:48:20 DEBUG ActionFileUploadInterceptor:200 - Found multipart
> request: MultiPartRequestWrapper
> 2025-06-03 12:48:20 DEBUG ActionFileUploadInterceptor:179 - No files have
> been uploaded/accepted
>
> Now I can see is blocked!. I was reading that this message is normal on the
> log:
>
> 2025-06-03 12:48:21 DEBUG DefaultAcceptedPatternsChecker:115 - Sets
> accepted patterns to
> [[\w+((\.\w+)|(\[\d+])|(\(\d+\))|(\['(\w-?|[\u4e00-\u9fa5]-?)+'])|(\('(\w-?|[\u4e00-\u9fa5]-?)+'\)))*]],
> note this impacts the safety of your application!
> 2025-06-03 12:48:21 DEBUG StrutsUrlEncoder:38 - Using default encoding:
> UTF-8
> 2025-06-03 12:48:21 DEBUG SecurityMemberAccess:142 - Checking access for
> [target: mx.com.struts.action.HelloAction@4489b4fa, member: private
> org.apache.struts2.dispatcher.multipart.UploadedFile
> mx.com.struts.action.HelloAction.miLogo, property: miLogo]
> 2025-06-03 12:48:21 WARN SecurityMemberAccess:174 - Access to non-public
> [private org.apache.struts2.dispatcher.multipart.UploadedFile
> mx.com.struts.action.HelloAction.miLogo] is blocked!
> 2025-06-03 12:48:21 DEBUG InstantiatingNullHandler:106 - Entering
> nullPropertyValue [target=[mx.com.struts.action.HelloAction@4489b4fa,
> org.apache.struts2.text.DefaultTextProvider@287a1ff7], property=miLogo]
>
> As comment I changed file name as miLogo because I was trying use a
> different name like upload.
>
> Thanks for the support.
>
> JMC
>
>
> El lun, 2 jun 2025 a las 23:44, Zoran Avtarovski (<zo...@sparecreative.com>)
> escribió:
>
>> Hi Jesus,
>>
>> What does your web.xml look like with regards to the struts filters? We
>> had a similar issue which was resolved by moving to the combined
>> StrutsPrepareAndExecuteFilter. Sitemesh also played a part in our issue,
>> but I think by what you are seeing it's worth trying.
>>
>> Z.
>>
>> On 1/6/2025 6:29 am, Jesus Moreno wrote:
>>> Hi Lukasz, thanks for your appreciated comments.
>>>
>>> I would like to explain in more detail what we have done to try to find
>> the
>>> problem and solve it. Here the context:
>>>
>>> This is my environment:
>>>
>>> - Struts Version: 7.0.3
>>> - Java Version: 17
>>> - Servlet Container: Apache Tomcat 10.1.36
>>> - Key Dependencies (pom):
>>> - struts2-core:7.0.3
>>> - struts2-convention-plugin:7.0.3
>>> - struts2-json-plugin:7.0.3
>>> - jakarta.servlet-api:5.0.0
>>> - log4j-core:2.17.2
>>> - Enforced exclusions: javax.servlet:*, javax.activation:*
>>>
>>> We've created a basic Maven project (standard archetype) with a simple
>> file
>>> upload form containing:
>>> - 4 regular text fields (working correctly with
>>> @StrutsParameter(depth=0))
>>> - 1 file upload field (<input type="file" name="upload">)
>>>
>>> This is my basic jsp:
>>>
>>> <%@ page contentType="text/html; charset=UTF-8" %>
>>> <%@ taglib prefix="s" uri="/struts-tags" %>
>>> <!DOCTYPE html>
>>> <html>
>>> <head>
>>> <title>File upload result</title>
>>> </head>
>>> <body>
>>> <h1>Hola Mundo desde la tierra...</h1>
>>> <form action="hello" method="post" enctype="multipart/form-data">
>>> <input type="text" name="name"/>
>>> <input type="text" name="lastname"/>
>>> <input type="text" name="gender"/>
>>> <input type="text" name="age"/>
>>> <input type="file" name="*upload*"/>
>>> <input type="submit" value="Subir" />
>>> </form>
>>> </body>
>>> </html>
>>>
>>> Expected Behavior:
>>>
>>> - When implementing UploadedFilesAware, the framework should:
>>> - Detect multipart requests
>>> - Process uploaded files through withUploadedFiles(List<UploadedFile>)
>>> - Make files available to the action
>>>
>>> Where my action looks like:
>>>
>>> import org.apache.logging.log4j.LogManager;
>>> import org.apache.logging.log4j.Logger;
>>> import org.apache.struts2.ActionSupport;
>>> import org.apache.struts2.action.UploadedFilesAware;
>>> import org.apache.struts2.dispatcher.multipart.UploadedFile;
>>> import org.apache.struts2.interceptor.parameter.StrutsParameter;
>>>
>>> public class HelloAction extends ActionSupport implements
>>> UploadedFilesAware {
>>> private static final long serialVersionUID = 1L;
>>> private Logger logger = LogManager.getLogger(HelloAction.class);
>>>
>>> private UploadedFile upload;
>>> private String uploadFileName;
>>> private String uploadContentType;
>>>
>>> @StrutsParameter(depth = 0)
>>> public String name;
>>>
>>> @StrutsParameter(depth = 0)
>>> public String lastname;
>>>
>>> @StrutsParameter(depth = 0)
>>> public String gender;
>>>
>>> @StrutsParameter(depth = 0)
>>> public String age;
>>>
>>> @Override
>>> public String execute() {
>>> logger.info("Executing execute method..." + this.uploadFileName);
>>> return SUCCESS;
>>> }
>>> public UploadedFile getUpload() {
>>> return upload;
>>> }
>>> public void setUpload(UploadedFile upload) {
>>> this.upload = upload;
>>> }
>>> public String getUploadFileName() {
>>> return uploadFileName;
>>> }
>>> public void setUploadFileName(String uploadFileName) {
>>> this.uploadFileName = uploadFileName;
>>> }
>>> public String getUploadContentType() {
>>> return uploadContentType;
>>> }
>>> public void setUploadContentType(String uploadContentType) {
>>> this.uploadContentType = uploadContentType;
>>> }
>>> public Logger getLogger() {
>>> return logger;
>>> }
>>> public void setLogger(Logger logger) {
>>> this.logger = logger;
>>> }
>>> public String getName() {
>>> return name;
>>> }
>>> public void setName(String name) {
>>> this.name = name;
>>> }
>>> public String getLastname() {
>>> return lastname;
>>> }
>>> public void setLastname(String lastname) {
>>> this.lastname = lastname;
>>> }
>>> public String getGender() {
>>> return gender;
>>> }
>>> public void setGender(String gender) {
>>> this.gender = gender;
>>> }
>>> public String getAge() {
>>> return age;
>>> }
>>> public void setAge(String age) {
>>> this.age = age;
>>> }
>>>
>>> @Override
>>> public void withUploadedFiles(List<UploadedFile> uploadedFiles) {
>>> if (uploadedFiles != null && !uploadedFiles.isEmpty()) {
>>> UploadedFile firstFile = uploadedFiles.get(0); // Take the
>> file
>>> this.upload = firstFile;
>>> this.uploadFileName = firstFile.getName(); // String
>>> this.uploadContentType = firstFile.getContentType(); //
>> String
>>> logger.info("File name: " + uploadFileName);
>>> }
>>> }
>>>
>>> Actual Behavior:
>>> Despite proper multipart request detection:
>>>
>>> The interceptor finds the request wrapper:
>>>
>>> 2025-05-31 DEBUG ActionFileUploadInterceptor:200 - Found multipart
>> request:
>>> MultiPartRequestWrapper
>>>
>>> *Our custom DebugMultiPartRequest confirms file presence:*
>>>
>>> 2025-05-31 12:00:13 INFO DebugMultiPartRequest:26 - Todos los parámetros
>>> del request:
>>> 2025-05-31 12:00:13 INFO DebugMultiPartRequest:30 - - nombre: Jesus
>>> 2025-05-31 12:00:13 INFO DebugMultiPartRequest:30 - - apellido: Moreno
>>> 2025-05-31 12:00:13 INFO DebugMultiPartRequest:30 - - sexo: M
>>> 2025-05-31 12:00:13 INFO DebugMultiPartRequest:30 - - edad: 50
>>>
>>>
>>>
>>> *2025-05-31 12:00:13 INFO DebugMultiPartRequest:39 - Part 'upload'
>>> encontrada:2025-05-31 12:00:13 INFO DebugMultiPartRequest:40 - -
>> Nombre:
>>> Adaware.jpg2025-05-31 12:00:13 INFO DebugMultiPartRequest:41 - -
>> Tamaño:
>>> 25546*2025-05-31 12:00:13 DEBUG InstantiatingNullHandler:106 - Entering
>>> nullPropertyValue
>>> [target=[org.apache.struts2.text.DefaultTextProvider@2a580722],
>>> property=struts]
>>> 2025-05-31 12:00:13 DEBUG ConfigurationManager:180 - Checking
>>> ConfigurationProviders for reload.
>>> 2025-05-31 12:00:13 DEBUG InstantiatingNullHandler:106 - Entering
>>> nullPropertyValue
>>> [target=[org.apache.struts2.text.DefaultTextProvider@2a580722],
>>> property=struts]
>>> 2025-05-31 12:00:13 DEBUG DefaultActionProxy:88 - Creating an
>>> DefaultActionProxy for namespace [/] and action name [hello]
>>> 2025-05-31 12:00:14 DEBUG DefaultActionInvocation:310 - Executing
>>> conditional interceptor: ActionFileUploadInterceptor
>>> 2025-05-31 12:00:14 DEBUG ActionFileUploadInterceptor:200 - Found
>> multipart
>>> request: MultiPartRequestWrapper
>>>
>>> But the interceptor reports:
>>>
>>> *2025-05-31 12:00:14 DEBUG ActionFileUploadInterceptor:179 - No files
>> have
>>> been uploaded/accepted*
>>>
>>> Investigation Steps Taken:
>>>
>>> 1. Verified all prerequisites:
>>> - Form uses enctype="multipart/form-data"
>>> - Using DTD 6.0 as suggested
>>> - Regularly cleaning Tomcat's work directory
>>> - Confirmed Jakarta EE 9+ compatibility
>>>
>>> 2. Created extensive debug instrumentation:
>>> - Custom DebugMultiPartRequest shows files reach the server
>>> - Interceptor-level logging confirms the disconnect
>>>
>>> Technical Findings:
>>>
>>> 1. The MultiPartRequestWrapper appears to lose file references between:
>>> - Initial parsing (where files are detectable via raw Part API)
>>> - Interceptor processing phase
>>>
>>> 2. Key suspicious behavior:
>>> - getFileParameterNames() returns empty enumeration
>>> - Despite request.getPart("upload") working correctly
>>>
>>> Finally As you suggested, we've:
>>>
>>> - Consistently cleaned deployment directories
>>> - Verified DTD compatibility
>>> - Followed migration guides carefully
>>> - Yet the team remains persistent in investigating this apparent
>>> framework bug rather than working around it.
>>>
>>> # Summary
>>> Files are received by the server (confirmed via custom
>>> DebugMultiPartRequest) but ActionFileUploadInterceptor fails to process
>>> them via UploadedFilesAware interface in Struts 7.0.3 (Jakarta EE
>> 9/Tomcat
>>> 10 environment).
>>>
>>> This is my custom DebugMultiPartRequest:
>>>
>>> public class DebugMultiPartRequest extends JakartaMultiPartRequest {
>>> private static final Logger logger =
>>> LogManager.getLogger(DebugMultiPartRequest.class);
>>> @Override
>>> public void parse(HttpServletRequest request, String saveDir) throws
>>> IOException {
>>> super.parse(request, saveDir); // Procesa el request normalmente
>>> logger.info("=== DebugMultiPartRequest (Struts 7) ===");
>>> // 1. Listar TODOS los parámetros del request (no solo archivos)
>>> logger.info("Todos los parámetros del request:");
>>> Enumeration<String> paramNames = request.getParameterNames();
>>> while (paramNames.hasMoreElements()) {
>>> String name = paramNames.nextElement();
>>> logger.info(" - " + name + ": " +
>> request.getParameter(name));
>>> }
>>> // 2. Listar partes multipart (alternativa para Jakarta EE 9+)
>>> if (request instanceof jakarta.servlet.http.HttpServletRequest
>>> jakartaRequest) {
>>> jakarta.servlet.http.Part part;
>>> try {
>>> part = jakartaRequest.getPart("upload");
>>> if (part != null) {
>>>
>>>
>>>
>>> * logger.info<http://logger.info>("Part 'upload' encontrada:");
>>> logger.info<http://logger.info>(" - Nombre: " +
>>> part.getSubmittedFileName()); logger.info<http://logger.info>(" -
>> Tamaño:
>>> " + part.getSize());* } else {
>>> logger.info("❌ Parte 'upload' NO encontrada en el
>> request.");
>>> }
>>> } catch (IOException e) {
>>> // TODO Auto-generated catch block
>>> e.printStackTrace();
>>> } catch (ServletException e) {
>>> // TODO Auto-generated catch block
>>> e.printStackTrace();
>>> } // Nombre de tu campo
>>>
>>> }
>>> }
>>> }
>>>
>>> After this investigation, we appreciated your feedback if there's any
>>> version where this issue we've identified has been resolved, or how we
>> can
>>> make file uploads work properly.
>>>
>>> Regards
>>> JMC
>>>
>>>
>>> El mié, 14 may 2025 a las 0:09, Lukasz Lenart (<lukaszlenart@apache.org
>>> )
>>> escribió:
>>>
>>>> sob., 10 maj 2025 o 07:09 Jesus Moreno<je...@gmail.com> napisał(a):
>>>>> 2025-05-09 01:00:25 WARNFile:79 - Struts has detected a file upload UI
>>>> tag
>>>>> (s:file) being used without a form set to method 'POST'
>>>>> (Despite <s:form method="POST" enctype="multipart/form-data"> being
>>>>> correctly declared)
>>>> This can be related to cached JSP files, please remove them from the
>>>> container "temp" folder or "work" folder.
>>>>
>>>>
>> https://cwiki.apache.org/confluence/display/WW/Struts+6.x.x+to+7.x.x+migration#Struts6.x.xto7.x.xmigration-Temp/WorkdirectoryofApplicationServer/ServletContainer
>>>>> struts.xml:
>>>>> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts
>>>>> Configuration 6.5//EN"
>>>>> "https://struts.apache.org/dtds/struts-6.5.dtd">
>>>> There is no such version as 6.5, use 6.0 instead
>>>> https://struts.apache.org/dtds/
>>>>
>>>>
>>>> Cheers
>>>> Łukasz
>>>>
>>>> ---------------------------------------------------------------------
>>>> To unsubscribe,e-mail:user-unsubscribe@struts.apache.org
>>>> For additional commands,e-mail:user-help@struts.apache.org
>>>>
>>>>
>
>
Re: File Upload Failure in Struts 7.0.3 Migration - UploadedFilesAware Implementation Issue
Posted by Jesus Moreno <je...@gmail.com>.
Sure, my web.xml looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<display-name>Struts Blank</display-name>
<!-- 1. Context Parameter para Log4j2 -->
<context-param>
<param-name>log4jConfiguration</param-name>
<param-value>/WEB-INF/classes/log4j2.xml</param-value>
</context-param>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
I forgot also show my struts.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts
Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
<struts>
<constant name="struts.ui.theme" value="simple"/> <!-- En struts.xml
-->
<constant name="struts.ui.templateDir" value="template"/>
<constant name="struts.ui.templateSuffix" value="ftl"/>
<constant name="struts.devMode" value="true"/>
<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
<constant name="struts.logging.fileUploadInterceptor" value="TRACE"/>
<constant name="struts.logging.level" value="TRACE"/>
<constant name="struts.multipart.parser"
value="mx.com.struts.action.DebugMultiPartRequest"/> <!-- Using my custom
DebugMultiPartRequest -->
<package name="default" extends="struts-default">
<action name="">
<result>/index.jsp</result>
</action>
<action name="hello" class="mx.com.struts.action.HelloAction">
<result name="success">/success.jsp</result>
<result name="error">/error.jsp</result>
</action>
</package>
</struts>
On the log I can see that I don't have file on my action:
2025-06-03 12:48:20 DEBUG ActionFileUploadInterceptor:200 - Found multipart
request: MultiPartRequestWrapper
2025-06-03 12:48:20 DEBUG ActionFileUploadInterceptor:179 - No files have
been uploaded/accepted
Now I can see is blocked!. I was reading that this message is normal on the
log:
2025-06-03 12:48:21 DEBUG DefaultAcceptedPatternsChecker:115 - Sets
accepted patterns to
[[\w+((\.\w+)|(\[\d+])|(\(\d+\))|(\['(\w-?|[\u4e00-\u9fa5]-?)+'])|(\('(\w-?|[\u4e00-\u9fa5]-?)+'\)))*]],
note this impacts the safety of your application!
2025-06-03 12:48:21 DEBUG StrutsUrlEncoder:38 - Using default encoding:
UTF-8
2025-06-03 12:48:21 DEBUG SecurityMemberAccess:142 - Checking access for
[target: mx.com.struts.action.HelloAction@4489b4fa, member: private
org.apache.struts2.dispatcher.multipart.UploadedFile
mx.com.struts.action.HelloAction.miLogo, property: miLogo]
2025-06-03 12:48:21 WARN SecurityMemberAccess:174 - Access to non-public
[private org.apache.struts2.dispatcher.multipart.UploadedFile
mx.com.struts.action.HelloAction.miLogo] is blocked!
2025-06-03 12:48:21 DEBUG InstantiatingNullHandler:106 - Entering
nullPropertyValue [target=[mx.com.struts.action.HelloAction@4489b4fa,
org.apache.struts2.text.DefaultTextProvider@287a1ff7], property=miLogo]
As comment I changed file name as miLogo because I was trying use a
different name like upload.
Thanks for the support.
JMC
El lun, 2 jun 2025 a las 23:44, Zoran Avtarovski (<zo...@sparecreative.com>)
escribió:
> Hi Jesus,
>
> What does your web.xml look like with regards to the struts filters? We
> had a similar issue which was resolved by moving to the combined
> StrutsPrepareAndExecuteFilter. Sitemesh also played a part in our issue,
> but I think by what you are seeing it's worth trying.
>
> Z.
>
> On 1/6/2025 6:29 am, Jesus Moreno wrote:
> > Hi Lukasz, thanks for your appreciated comments.
> >
> > I would like to explain in more detail what we have done to try to find
> the
> > problem and solve it. Here the context:
> >
> > This is my environment:
> >
> > - Struts Version: 7.0.3
> > - Java Version: 17
> > - Servlet Container: Apache Tomcat 10.1.36
> > - Key Dependencies (pom):
> > - struts2-core:7.0.3
> > - struts2-convention-plugin:7.0.3
> > - struts2-json-plugin:7.0.3
> > - jakarta.servlet-api:5.0.0
> > - log4j-core:2.17.2
> > - Enforced exclusions: javax.servlet:*, javax.activation:*
> >
> > We've created a basic Maven project (standard archetype) with a simple
> file
> > upload form containing:
> > - 4 regular text fields (working correctly with
> > @StrutsParameter(depth=0))
> > - 1 file upload field (<input type="file" name="upload">)
> >
> > This is my basic jsp:
> >
> > <%@ page contentType="text/html; charset=UTF-8" %>
> > <%@ taglib prefix="s" uri="/struts-tags" %>
> > <!DOCTYPE html>
> > <html>
> > <head>
> > <title>File upload result</title>
> > </head>
> > <body>
> > <h1>Hola Mundo desde la tierra...</h1>
> > <form action="hello" method="post" enctype="multipart/form-data">
> > <input type="text" name="name"/>
> > <input type="text" name="lastname"/>
> > <input type="text" name="gender"/>
> > <input type="text" name="age"/>
> > <input type="file" name="*upload*"/>
> > <input type="submit" value="Subir" />
> > </form>
> > </body>
> > </html>
> >
> > Expected Behavior:
> >
> > - When implementing UploadedFilesAware, the framework should:
> > - Detect multipart requests
> > - Process uploaded files through withUploadedFiles(List<UploadedFile>)
> > - Make files available to the action
> >
> > Where my action looks like:
> >
> > import org.apache.logging.log4j.LogManager;
> > import org.apache.logging.log4j.Logger;
> > import org.apache.struts2.ActionSupport;
> > import org.apache.struts2.action.UploadedFilesAware;
> > import org.apache.struts2.dispatcher.multipart.UploadedFile;
> > import org.apache.struts2.interceptor.parameter.StrutsParameter;
> >
> > public class HelloAction extends ActionSupport implements
> > UploadedFilesAware {
> > private static final long serialVersionUID = 1L;
> > private Logger logger = LogManager.getLogger(HelloAction.class);
> >
> > private UploadedFile upload;
> > private String uploadFileName;
> > private String uploadContentType;
> >
> > @StrutsParameter(depth = 0)
> > public String name;
> >
> > @StrutsParameter(depth = 0)
> > public String lastname;
> >
> > @StrutsParameter(depth = 0)
> > public String gender;
> >
> > @StrutsParameter(depth = 0)
> > public String age;
> >
> > @Override
> > public String execute() {
> > logger.info("Executing execute method..." + this.uploadFileName);
> > return SUCCESS;
> > }
> > public UploadedFile getUpload() {
> > return upload;
> > }
> > public void setUpload(UploadedFile upload) {
> > this.upload = upload;
> > }
> > public String getUploadFileName() {
> > return uploadFileName;
> > }
> > public void setUploadFileName(String uploadFileName) {
> > this.uploadFileName = uploadFileName;
> > }
> > public String getUploadContentType() {
> > return uploadContentType;
> > }
> > public void setUploadContentType(String uploadContentType) {
> > this.uploadContentType = uploadContentType;
> > }
> > public Logger getLogger() {
> > return logger;
> > }
> > public void setLogger(Logger logger) {
> > this.logger = logger;
> > }
> > public String getName() {
> > return name;
> > }
> > public void setName(String name) {
> > this.name = name;
> > }
> > public String getLastname() {
> > return lastname;
> > }
> > public void setLastname(String lastname) {
> > this.lastname = lastname;
> > }
> > public String getGender() {
> > return gender;
> > }
> > public void setGender(String gender) {
> > this.gender = gender;
> > }
> > public String getAge() {
> > return age;
> > }
> > public void setAge(String age) {
> > this.age = age;
> > }
> >
> > @Override
> > public void withUploadedFiles(List<UploadedFile> uploadedFiles) {
> > if (uploadedFiles != null && !uploadedFiles.isEmpty()) {
> > UploadedFile firstFile = uploadedFiles.get(0); // Take the
> file
> > this.upload = firstFile;
> > this.uploadFileName = firstFile.getName(); // String
> > this.uploadContentType = firstFile.getContentType(); //
> String
> > logger.info("File name: " + uploadFileName);
> > }
> > }
> >
> > Actual Behavior:
> > Despite proper multipart request detection:
> >
> > The interceptor finds the request wrapper:
> >
> > 2025-05-31 DEBUG ActionFileUploadInterceptor:200 - Found multipart
> request:
> > MultiPartRequestWrapper
> >
> > *Our custom DebugMultiPartRequest confirms file presence:*
> >
> > 2025-05-31 12:00:13 INFO DebugMultiPartRequest:26 - Todos los parámetros
> > del request:
> > 2025-05-31 12:00:13 INFO DebugMultiPartRequest:30 - - nombre: Jesus
> > 2025-05-31 12:00:13 INFO DebugMultiPartRequest:30 - - apellido: Moreno
> > 2025-05-31 12:00:13 INFO DebugMultiPartRequest:30 - - sexo: M
> > 2025-05-31 12:00:13 INFO DebugMultiPartRequest:30 - - edad: 50
> >
> >
> >
> > *2025-05-31 12:00:13 INFO DebugMultiPartRequest:39 - Part 'upload'
> > encontrada:2025-05-31 12:00:13 INFO DebugMultiPartRequest:40 - -
> Nombre:
> > Adaware.jpg2025-05-31 12:00:13 INFO DebugMultiPartRequest:41 - -
> Tamaño:
> > 25546*2025-05-31 12:00:13 DEBUG InstantiatingNullHandler:106 - Entering
> > nullPropertyValue
> > [target=[org.apache.struts2.text.DefaultTextProvider@2a580722],
> > property=struts]
> > 2025-05-31 12:00:13 DEBUG ConfigurationManager:180 - Checking
> > ConfigurationProviders for reload.
> > 2025-05-31 12:00:13 DEBUG InstantiatingNullHandler:106 - Entering
> > nullPropertyValue
> > [target=[org.apache.struts2.text.DefaultTextProvider@2a580722],
> > property=struts]
> > 2025-05-31 12:00:13 DEBUG DefaultActionProxy:88 - Creating an
> > DefaultActionProxy for namespace [/] and action name [hello]
> > 2025-05-31 12:00:14 DEBUG DefaultActionInvocation:310 - Executing
> > conditional interceptor: ActionFileUploadInterceptor
> > 2025-05-31 12:00:14 DEBUG ActionFileUploadInterceptor:200 - Found
> multipart
> > request: MultiPartRequestWrapper
> >
> > But the interceptor reports:
> >
> > *2025-05-31 12:00:14 DEBUG ActionFileUploadInterceptor:179 - No files
> have
> > been uploaded/accepted*
> >
> > Investigation Steps Taken:
> >
> > 1. Verified all prerequisites:
> > - Form uses enctype="multipart/form-data"
> > - Using DTD 6.0 as suggested
> > - Regularly cleaning Tomcat's work directory
> > - Confirmed Jakarta EE 9+ compatibility
> >
> > 2. Created extensive debug instrumentation:
> > - Custom DebugMultiPartRequest shows files reach the server
> > - Interceptor-level logging confirms the disconnect
> >
> > Technical Findings:
> >
> > 1. The MultiPartRequestWrapper appears to lose file references between:
> > - Initial parsing (where files are detectable via raw Part API)
> > - Interceptor processing phase
> >
> > 2. Key suspicious behavior:
> > - getFileParameterNames() returns empty enumeration
> > - Despite request.getPart("upload") working correctly
> >
> > Finally As you suggested, we've:
> >
> > - Consistently cleaned deployment directories
> > - Verified DTD compatibility
> > - Followed migration guides carefully
> > - Yet the team remains persistent in investigating this apparent
> > framework bug rather than working around it.
> >
> > # Summary
> > Files are received by the server (confirmed via custom
> > DebugMultiPartRequest) but ActionFileUploadInterceptor fails to process
> > them via UploadedFilesAware interface in Struts 7.0.3 (Jakarta EE
> 9/Tomcat
> > 10 environment).
> >
> > This is my custom DebugMultiPartRequest:
> >
> > public class DebugMultiPartRequest extends JakartaMultiPartRequest {
> > private static final Logger logger =
> > LogManager.getLogger(DebugMultiPartRequest.class);
> > @Override
> > public void parse(HttpServletRequest request, String saveDir) throws
> > IOException {
> > super.parse(request, saveDir); // Procesa el request normalmente
> > logger.info("=== DebugMultiPartRequest (Struts 7) ===");
> > // 1. Listar TODOS los parámetros del request (no solo archivos)
> > logger.info("Todos los parámetros del request:");
> > Enumeration<String> paramNames = request.getParameterNames();
> > while (paramNames.hasMoreElements()) {
> > String name = paramNames.nextElement();
> > logger.info(" - " + name + ": " +
> request.getParameter(name));
> > }
> > // 2. Listar partes multipart (alternativa para Jakarta EE 9+)
> > if (request instanceof jakarta.servlet.http.HttpServletRequest
> > jakartaRequest) {
> > jakarta.servlet.http.Part part;
> > try {
> > part = jakartaRequest.getPart("upload");
> > if (part != null) {
> >
> >
> >
> > * logger.info<http://logger.info>("Part 'upload' encontrada:");
> > logger.info<http://logger.info>(" - Nombre: " +
> > part.getSubmittedFileName()); logger.info<http://logger.info>(" -
> Tamaño:
> > " + part.getSize());* } else {
> > logger.info("❌ Parte 'upload' NO encontrada en el
> request.");
> > }
> > } catch (IOException e) {
> > // TODO Auto-generated catch block
> > e.printStackTrace();
> > } catch (ServletException e) {
> > // TODO Auto-generated catch block
> > e.printStackTrace();
> > } // Nombre de tu campo
> >
> > }
> > }
> > }
> >
> > After this investigation, we appreciated your feedback if there's any
> > version where this issue we've identified has been resolved, or how we
> can
> > make file uploads work properly.
> >
> > Regards
> > JMC
> >
> >
> > El mié, 14 may 2025 a las 0:09, Lukasz Lenart (<lukaszlenart@apache.org
> >)
> > escribió:
> >
> >> sob., 10 maj 2025 o 07:09 Jesus Moreno<je...@gmail.com> napisał(a):
> >>> 2025-05-09 01:00:25 WARNFile:79 - Struts has detected a file upload UI
> >> tag
> >>> (s:file) being used without a form set to method 'POST'
> >>> (Despite <s:form method="POST" enctype="multipart/form-data"> being
> >>> correctly declared)
> >> This can be related to cached JSP files, please remove them from the
> >> container "temp" folder or "work" folder.
> >>
> >>
> https://cwiki.apache.org/confluence/display/WW/Struts+6.x.x+to+7.x.x+migration#Struts6.x.xto7.x.xmigration-Temp/WorkdirectoryofApplicationServer/ServletContainer
> >>
> >>> struts.xml:
> >>> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts
> >>> Configuration 6.5//EN"
> >>> "https://struts.apache.org/dtds/struts-6.5.dtd">
> >> There is no such version as 6.5, use 6.0 instead
> >> https://struts.apache.org/dtds/
> >>
> >>
> >> Cheers
> >> Łukasz
> >>
> >> ---------------------------------------------------------------------
> >> To unsubscribe, e-mail:user-unsubscribe@struts.apache.org
> >> For additional commands, e-mail:user-help@struts.apache.org
> >>
> >>
--
Ing. Jesús Moreno Canseco
Tel.: (55)6377-0600
Cel.: 55-1409-5156
jesusmca@gmail.com <jm...@ismarts.com.mx>
Re: File Upload Failure in Struts 7.0.3 Migration - UploadedFilesAware Implementation Issue
Posted by Jesus Moreno <je...@gmail.com>.
Sure, my web.xml looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<display-name>Struts Blank</display-name>
<!-- 1. Context Parameter para Log4j2 -->
<context-param>
<param-name>log4jConfiguration</param-name>
<param-value>/WEB-INF/classes/log4j2.xml</param-value>
</context-param>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
I forgot also show my struts.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts
Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
<struts>
<constant name="struts.ui.theme" value="simple"/> <!-- En struts.xml
-->
<constant name="struts.ui.templateDir" value="template"/>
<constant name="struts.ui.templateSuffix" value="ftl"/>
<constant name="struts.devMode" value="true"/>
<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
<constant name="struts.logging.fileUploadInterceptor" value="TRACE"/>
<constant name="struts.logging.level" value="TRACE"/>
<constant name="struts.multipart.parser"
value="mx.com.struts.action.DebugMultiPartRequest"/> <!-- Using my custom
DebugMultiPartRequest -->
<package name="default" extends="struts-default">
<action name="">
<result>/index.jsp</result>
</action>
<action name="hello" class="mx.com.struts.action.HelloAction">
<result name="success">/success.jsp</result>
<result name="error">/error.jsp</result>
</action>
</package>
</struts>
On the log I can see that I don't have file on my action:
El lun, 2 jun 2025 a las 23:44, Zoran Avtarovski (<zo...@sparecreative.com>)
escribió:
> Hi Jesus,
>
> What does your web.xml look like with regards to the struts filters? We
> had a similar issue which was resolved by moving to the combined
> StrutsPrepareAndExecuteFilter. Sitemesh also played a part in our issue,
> but I think by what you are seeing it's worth trying.
>
> Z.
>
> On 1/6/2025 6:29 am, Jesus Moreno wrote:
> > Hi Lukasz, thanks for your appreciated comments.
> >
> > I would like to explain in more detail what we have done to try to find
> the
> > problem and solve it. Here the context:
> >
> > This is my environment:
> >
> > - Struts Version: 7.0.3
> > - Java Version: 17
> > - Servlet Container: Apache Tomcat 10.1.36
> > - Key Dependencies (pom):
> > - struts2-core:7.0.3
> > - struts2-convention-plugin:7.0.3
> > - struts2-json-plugin:7.0.3
> > - jakarta.servlet-api:5.0.0
> > - log4j-core:2.17.2
> > - Enforced exclusions: javax.servlet:*, javax.activation:*
> >
> > We've created a basic Maven project (standard archetype) with a simple
> file
> > upload form containing:
> > - 4 regular text fields (working correctly with
> > @StrutsParameter(depth=0))
> > - 1 file upload field (<input type="file" name="upload">)
> >
> > This is my basic jsp:
> >
> > <%@ page contentType="text/html; charset=UTF-8" %>
> > <%@ taglib prefix="s" uri="/struts-tags" %>
> > <!DOCTYPE html>
> > <html>
> > <head>
> > <title>File upload result</title>
> > </head>
> > <body>
> > <h1>Hola Mundo desde la tierra...</h1>
> > <form action="hello" method="post" enctype="multipart/form-data">
> > <input type="text" name="name"/>
> > <input type="text" name="lastname"/>
> > <input type="text" name="gender"/>
> > <input type="text" name="age"/>
> > <input type="file" name="*upload*"/>
> > <input type="submit" value="Subir" />
> > </form>
> > </body>
> > </html>
> >
> > Expected Behavior:
> >
> > - When implementing UploadedFilesAware, the framework should:
> > - Detect multipart requests
> > - Process uploaded files through withUploadedFiles(List<UploadedFile>)
> > - Make files available to the action
> >
> > Where my action looks like:
> >
> > import org.apache.logging.log4j.LogManager;
> > import org.apache.logging.log4j.Logger;
> > import org.apache.struts2.ActionSupport;
> > import org.apache.struts2.action.UploadedFilesAware;
> > import org.apache.struts2.dispatcher.multipart.UploadedFile;
> > import org.apache.struts2.interceptor.parameter.StrutsParameter;
> >
> > public class HelloAction extends ActionSupport implements
> > UploadedFilesAware {
> > private static final long serialVersionUID = 1L;
> > private Logger logger = LogManager.getLogger(HelloAction.class);
> >
> > private UploadedFile upload;
> > private String uploadFileName;
> > private String uploadContentType;
> >
> > @StrutsParameter(depth = 0)
> > public String name;
> >
> > @StrutsParameter(depth = 0)
> > public String lastname;
> >
> > @StrutsParameter(depth = 0)
> > public String gender;
> >
> > @StrutsParameter(depth = 0)
> > public String age;
> >
> > @Override
> > public String execute() {
> > logger.info("Executing execute method..." + this.uploadFileName);
> > return SUCCESS;
> > }
> > public UploadedFile getUpload() {
> > return upload;
> > }
> > public void setUpload(UploadedFile upload) {
> > this.upload = upload;
> > }
> > public String getUploadFileName() {
> > return uploadFileName;
> > }
> > public void setUploadFileName(String uploadFileName) {
> > this.uploadFileName = uploadFileName;
> > }
> > public String getUploadContentType() {
> > return uploadContentType;
> > }
> > public void setUploadContentType(String uploadContentType) {
> > this.uploadContentType = uploadContentType;
> > }
> > public Logger getLogger() {
> > return logger;
> > }
> > public void setLogger(Logger logger) {
> > this.logger = logger;
> > }
> > public String getName() {
> > return name;
> > }
> > public void setName(String name) {
> > this.name = name;
> > }
> > public String getLastname() {
> > return lastname;
> > }
> > public void setLastname(String lastname) {
> > this.lastname = lastname;
> > }
> > public String getGender() {
> > return gender;
> > }
> > public void setGender(String gender) {
> > this.gender = gender;
> > }
> > public String getAge() {
> > return age;
> > }
> > public void setAge(String age) {
> > this.age = age;
> > }
> >
> > @Override
> > public void withUploadedFiles(List<UploadedFile> uploadedFiles) {
> > if (uploadedFiles != null && !uploadedFiles.isEmpty()) {
> > UploadedFile firstFile = uploadedFiles.get(0); // Take the
> file
> > this.upload = firstFile;
> > this.uploadFileName = firstFile.getName(); // String
> > this.uploadContentType = firstFile.getContentType(); //
> String
> > logger.info("File name: " + uploadFileName);
> > }
> > }
> >
> > Actual Behavior:
> > Despite proper multipart request detection:
> >
> > The interceptor finds the request wrapper:
> >
> > 2025-05-31 DEBUG ActionFileUploadInterceptor:200 - Found multipart
> request:
> > MultiPartRequestWrapper
> >
> > *Our custom DebugMultiPartRequest confirms file presence:*
> >
> > 2025-05-31 12:00:13 INFO DebugMultiPartRequest:26 - Todos los parámetros
> > del request:
> > 2025-05-31 12:00:13 INFO DebugMultiPartRequest:30 - - nombre: Jesus
> > 2025-05-31 12:00:13 INFO DebugMultiPartRequest:30 - - apellido: Moreno
> > 2025-05-31 12:00:13 INFO DebugMultiPartRequest:30 - - sexo: M
> > 2025-05-31 12:00:13 INFO DebugMultiPartRequest:30 - - edad: 50
> >
> >
> >
> > *2025-05-31 12:00:13 INFO DebugMultiPartRequest:39 - Part 'upload'
> > encontrada:2025-05-31 12:00:13 INFO DebugMultiPartRequest:40 - -
> Nombre:
> > Adaware.jpg2025-05-31 12:00:13 INFO DebugMultiPartRequest:41 - -
> Tamaño:
> > 25546*2025-05-31 12:00:13 DEBUG InstantiatingNullHandler:106 - Entering
> > nullPropertyValue
> > [target=[org.apache.struts2.text.DefaultTextProvider@2a580722],
> > property=struts]
> > 2025-05-31 12:00:13 DEBUG ConfigurationManager:180 - Checking
> > ConfigurationProviders for reload.
> > 2025-05-31 12:00:13 DEBUG InstantiatingNullHandler:106 - Entering
> > nullPropertyValue
> > [target=[org.apache.struts2.text.DefaultTextProvider@2a580722],
> > property=struts]
> > 2025-05-31 12:00:13 DEBUG DefaultActionProxy:88 - Creating an
> > DefaultActionProxy for namespace [/] and action name [hello]
> > 2025-05-31 12:00:14 DEBUG DefaultActionInvocation:310 - Executing
> > conditional interceptor: ActionFileUploadInterceptor
> > 2025-05-31 12:00:14 DEBUG ActionFileUploadInterceptor:200 - Found
> multipart
> > request: MultiPartRequestWrapper
> >
> > But the interceptor reports:
> >
> > *2025-05-31 12:00:14 DEBUG ActionFileUploadInterceptor:179 - No files
> have
> > been uploaded/accepted*
> >
> > Investigation Steps Taken:
> >
> > 1. Verified all prerequisites:
> > - Form uses enctype="multipart/form-data"
> > - Using DTD 6.0 as suggested
> > - Regularly cleaning Tomcat's work directory
> > - Confirmed Jakarta EE 9+ compatibility
> >
> > 2. Created extensive debug instrumentation:
> > - Custom DebugMultiPartRequest shows files reach the server
> > - Interceptor-level logging confirms the disconnect
> >
> > Technical Findings:
> >
> > 1. The MultiPartRequestWrapper appears to lose file references between:
> > - Initial parsing (where files are detectable via raw Part API)
> > - Interceptor processing phase
> >
> > 2. Key suspicious behavior:
> > - getFileParameterNames() returns empty enumeration
> > - Despite request.getPart("upload") working correctly
> >
> > Finally As you suggested, we've:
> >
> > - Consistently cleaned deployment directories
> > - Verified DTD compatibility
> > - Followed migration guides carefully
> > - Yet the team remains persistent in investigating this apparent
> > framework bug rather than working around it.
> >
> > # Summary
> > Files are received by the server (confirmed via custom
> > DebugMultiPartRequest) but ActionFileUploadInterceptor fails to process
> > them via UploadedFilesAware interface in Struts 7.0.3 (Jakarta EE
> 9/Tomcat
> > 10 environment).
> >
> > This is my custom DebugMultiPartRequest:
> >
> > public class DebugMultiPartRequest extends JakartaMultiPartRequest {
> > private static final Logger logger =
> > LogManager.getLogger(DebugMultiPartRequest.class);
> > @Override
> > public void parse(HttpServletRequest request, String saveDir) throws
> > IOException {
> > super.parse(request, saveDir); // Procesa el request normalmente
> > logger.info("=== DebugMultiPartRequest (Struts 7) ===");
> > // 1. Listar TODOS los parámetros del request (no solo archivos)
> > logger.info("Todos los parámetros del request:");
> > Enumeration<String> paramNames = request.getParameterNames();
> > while (paramNames.hasMoreElements()) {
> > String name = paramNames.nextElement();
> > logger.info(" - " + name + ": " +
> request.getParameter(name));
> > }
> > // 2. Listar partes multipart (alternativa para Jakarta EE 9+)
> > if (request instanceof jakarta.servlet.http.HttpServletRequest
> > jakartaRequest) {
> > jakarta.servlet.http.Part part;
> > try {
> > part = jakartaRequest.getPart("upload");
> > if (part != null) {
> >
> >
> >
> > * logger.info<http://logger.info>("Part 'upload' encontrada:");
> > logger.info<http://logger.info>(" - Nombre: " +
> > part.getSubmittedFileName()); logger.info<http://logger.info>(" -
> Tamaño:
> > " + part.getSize());* } else {
> > logger.info("❌ Parte 'upload' NO encontrada en el
> request.");
> > }
> > } catch (IOException e) {
> > // TODO Auto-generated catch block
> > e.printStackTrace();
> > } catch (ServletException e) {
> > // TODO Auto-generated catch block
> > e.printStackTrace();
> > } // Nombre de tu campo
> >
> > }
> > }
> > }
> >
> > After this investigation, we appreciated your feedback if there's any
> > version where this issue we've identified has been resolved, or how we
> can
> > make file uploads work properly.
> >
> > Regards
> > JMC
> >
> >
> > El mié, 14 may 2025 a las 0:09, Lukasz Lenart (<lukaszlenart@apache.org
> >)
> > escribió:
> >
> >> sob., 10 maj 2025 o 07:09 Jesus Moreno<je...@gmail.com> napisał(a):
> >>> 2025-05-09 01:00:25 WARNFile:79 - Struts has detected a file upload UI
> >> tag
> >>> (s:file) being used without a form set to method 'POST'
> >>> (Despite <s:form method="POST" enctype="multipart/form-data"> being
> >>> correctly declared)
> >> This can be related to cached JSP files, please remove them from the
> >> container "temp" folder or "work" folder.
> >>
> >>
> https://cwiki.apache.org/confluence/display/WW/Struts+6.x.x+to+7.x.x+migration#Struts6.x.xto7.x.xmigration-Temp/WorkdirectoryofApplicationServer/ServletContainer
> >>
> >>> struts.xml:
> >>> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts
> >>> Configuration 6.5//EN"
> >>> "https://struts.apache.org/dtds/struts-6.5.dtd">
> >> There is no such version as 6.5, use 6.0 instead
> >> https://struts.apache.org/dtds/
> >>
> >>
> >> Cheers
> >> Łukasz
> >>
> >> ---------------------------------------------------------------------
> >> To unsubscribe, e-mail:user-unsubscribe@struts.apache.org
> >> For additional commands, e-mail:user-help@struts.apache.org
> >>
> >>
--
Ing. Jesús Moreno Canseco
Tel.: (55)6377-0600
Cel.: 55-1409-5156
jesusmca@gmail.com <jm...@ismarts.com.mx>