Skip to content

Commit 8315689

Browse files
committed
Cleans up all uploaded files
1 parent 84a51cc commit 8315689

File tree

4 files changed

+385
-13
lines changed

4 files changed

+385
-13
lines changed

.gitignore

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# IDEA
2-
.idea
2+
.idea/
33
*.iml
44
*.ipr
55
*.iws
@@ -38,11 +38,14 @@ buildNumber.properties
3838
.mvn/timing.properties
3939
.mvn/wrapper/maven-wrapper.jar
4040

41-
plugins/testng/test-output
42-
test-output
41+
plugins/testng/test-output/
42+
test-output/
4343

4444
# Sonar
45-
/.sonar/
45+
.sonar/
4646

4747
# Tidelift CLI scanner
4848
.tidelift
49+
50+
# Claude Code specific local settings
51+
.claude/

CLAUDE.md

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Apache Struts 2 Framework (Version 6.7.x)
6+
7+
This is the Apache Struts 2 web framework, a free open-source solution for creating Java web applications. The codebase is a multi-module Maven project with comprehensive plugin architecture.
8+
9+
## Build System & Common Commands
10+
11+
### Maven Commands
12+
```bash
13+
# Build entire project
14+
./mvnw clean install
15+
16+
# Build without running tests
17+
./mvnw clean install -DskipTests
18+
19+
# Build without assembly
20+
./mvnw clean install -DskipAssembly
21+
22+
# Run all tests
23+
./mvnw clean test
24+
25+
# Run tests with coverage
26+
./mvnw clean verify -Pcoverage -DskipAssembly
27+
28+
# Run integration tests
29+
./mvnw clean verify -DskipAssembly
30+
31+
# Test specific module
32+
./mvnw -pl core clean test
33+
./mvnw -pl plugins/spring clean test
34+
35+
# Check for security vulnerabilities
36+
./mvnw clean verify -Pdependency-check
37+
38+
# Run Apache RAT license check
39+
./mvnw clean prepare-package
40+
```
41+
42+
### Test Framework
43+
- **Primary**: JUnit 4.13.2 with Maven Surefire Plugin 3.5.1
44+
- **Pattern**: `**/*Test.java` (excludes `**/TestBean.java`)
45+
- **Coverage**: JaCoCo 0.8.12
46+
- **Additional**: Mockito, EasyMock, AssertJ, Spring Test
47+
- **Integration**: Maven Failsafe Plugin with Jetty on port 8090
48+
49+
## Project Architecture
50+
51+
### Core Structure
52+
```
53+
struts6/
54+
├── core/ # Core Struts 2 framework (main dependency)
55+
├── plugins/ # Plugin modules
56+
│ ├── spring/ # Spring integration
57+
│ ├── json/ # JSON support
58+
│ ├── tiles/ # Apache Tiles integration
59+
│ ├── velocity/ # Velocity template engine
60+
│ └── [20+ other plugins]
61+
├── apps/ # Sample applications
62+
│ ├── showcase/ # Feature demonstration app
63+
│ └── rest-showcase/ # REST API examples
64+
├── bundles/ # OSGi bundles
65+
├── bom/ # Bill of Materials
66+
└── assembly/ # Distribution packaging
67+
```
68+
69+
### Key Technologies
70+
- **Java**: Minimum JDK 8, supports up to JDK 21
71+
- **Servlet API**: 3.1+ required
72+
- **Expression Language**: OGNL 3.3.5
73+
- **Dependency Injection**: Custom container (`com.opensymphony.xwork2.inject`)
74+
- **Templating**: FreeMarker 2.3.33 (default), Velocity, JSP
75+
- **Logging**: SLF4J 2.0.16 with Log4j2 2.24.1
76+
- **Build**: Maven with wrapper (3.9.6)
77+
78+
### Core Components Architecture
79+
80+
#### Action Framework (MVC Pattern)
81+
- **Actions**: Located in `core/src/main/java/org/apache/struts2/action/`
82+
- **Action Support**: `ActionSupport` base class with validation and i18n
83+
- **Action Context**: `ActionContext` provides access to servlet objects
84+
- **Action Invocation**: `DefaultActionInvocation` handles action execution
85+
86+
#### Configuration System
87+
- **XML-based**: Primary configuration via `struts.xml` files
88+
- **Annotation-based**: Convention plugin for zero-config approach
89+
- **Java-based**: `StrutsJavaConfiguration` for programmatic setup
90+
- **Property files**: `struts.properties` for framework settings
91+
92+
#### Interceptor Chain
93+
- **Framework Core**: All requests processed through interceptor chain
94+
- **Built-in Interceptors**: 20+ interceptors in `org.apache.struts2.interceptor`
95+
- **Validation**: `ValidationInterceptor` with annotation support
96+
- **File Upload**: `FileUploadInterceptor` with security controls
97+
- **Parameters**: `ParametersInterceptor` with OGNL expression handling
98+
99+
#### Result Framework
100+
- **Result Types**: JSP, FreeMarker, Redirect, Stream, JSON, etc.
101+
- **Chaining**: `ActionChainResult` for action-to-action calls
102+
- **Templates**: Pluggable result renderers
103+
104+
#### Value Stack (OGNL Integration)
105+
- **Expression Language**: OGNL for property access and method calls
106+
- **Security**: `SecurityMemberAccess` prevents dangerous operations
107+
- **Performance**: Caffeine-based expression caching
108+
- **Context**: CompoundRoot provides hierarchical value resolution
109+
110+
### Plugin Architecture
111+
Each plugin is a separate Maven module with:
112+
- **Plugin Descriptor**: `struts-plugin.xml` defines beans and configuration
113+
- **Dependency Isolation**: Separate classloaders for plugin resources
114+
- **Extension Points**: Configurable via dependency injection
115+
- **Popular Plugins**: Spring (DI), JSON (REST), Tiles (Layout), Bean Validation (JSR-303)
116+
117+
### Security Architecture
118+
- **OGNL Security**: Restricted method access and class loading
119+
- **CSRF Protection**: Token-based with `TokenInterceptor`
120+
- **File Upload Security**: Type and size restrictions
121+
- **Content Security Policy**: Built-in CSP support
122+
- **Input Validation**: Server-side validation framework
123+
- **Pattern Matching**: Configurable allowed/excluded patterns
124+
125+
## Development Guidelines
126+
127+
### Code Organization
128+
- **Package Structure**: Follow existing `org.apache.struts2.*` hierarchy
129+
- **Naming Conventions**: Use Struts conventions (Actions end with `Action`)
130+
- **Configuration**: Prefer XML configuration in `struts.xml` for complex setups
131+
- **Testing**: Each module has comprehensive unit and integration tests
132+
133+
### Plugin Development
134+
```java
135+
// Plugin descriptor example (struts-plugin.xml)
136+
<bean type="com.opensymphony.xwork2.ObjectFactory"
137+
name="myObjectFactory"
138+
class="com.example.MyObjectFactory" />
139+
```
140+
141+
### Common Patterns
142+
- **Action Implementation**: Extend `ActionSupport` or implement `Action`
143+
- **Result Mapping**: Use result configuration in `struts.xml`
144+
- **Interceptor Development**: Extend `AbstractInterceptor`
145+
- **Type Conversion**: Implement `TypeConverter` for custom types
146+
- **Validation**: Use validation XML or annotations
147+
148+
### Important Notes
149+
- **Version**: Currently 6.7.5-SNAPSHOT (release branch: `release/struts-6-7-x`)
150+
- **Java Compatibility**: Compiled for Java 8, tested through Java 21
151+
- **Security**: Always validate inputs and follow OWASP guidelines
152+
- **Performance**: Leverage built-in caching (OGNL expressions, templates)
153+
- **Deprecation**: Some legacy XWork components marked for removal
154+
155+
### Build Profiles
156+
- **coverage**: Enables JaCoCo coverage reporting
157+
- **dependency-check**: OWASP dependency vulnerability scanning
158+
- **jdk17**: Special configuration for Java 17+ module system
159+
160+
This is a mature, enterprise-grade framework with extensive documentation at https://struts.apache.org/ and active community support through Apache mailing lists and JIRA (project WW).

core/src/main/java/org/apache/struts2/dispatcher/multipart/JakartaMultiPartRequest.java

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
import java.util.HashMap;
4343
import java.util.List;
4444
import java.util.Map;
45-
import java.util.Set;
4645

4746
import static org.apache.commons.lang3.StringUtils.normalizeSpace;
4847

@@ -59,6 +58,9 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
5958
// maps parameter name -> List of param values
6059
protected Map<String, List<String>> params = new HashMap<>();
6160

61+
// List to track all FileItem instances for comprehensive cleanup
62+
protected List<FileItem> allFileItems = new ArrayList<>();
63+
6264
/**
6365
* Creates a new request wrapper to handle multi-part data using methods adapted from Jason Pell's
6466
* multipart classes (see class description).
@@ -103,6 +105,10 @@ protected void processUpload(HttpServletRequest request, String saveDir) throws
103105
if (ServletFileUpload.isMultipartContent(request)) {
104106
for (FileItem item : parseRequest(request, saveDir)) {
105107
LOG.debug("Found file item: [{}]", normalizeSpace(item.getFieldName()));
108+
109+
// Track all FileItem instances for comprehensive cleanup
110+
allFileItems.add(item);
111+
106112
if (item.isFormField()) {
107113
processNormalFormField(item, request.getCharacterEncoding());
108114
} else {
@@ -240,7 +246,11 @@ public UploadedFile[] getFile(String fieldName) {
240246
// Ensure file exists even if it is empty.
241247
if (diskFileItem.getSize() == 0 && storeLocation != null && !storeLocation.exists()) {
242248
try {
243-
storeLocation.createNewFile();
249+
if (storeLocation.createNewFile()) {
250+
LOG.debug("File {} has been created", storeLocation.getAbsolutePath());
251+
} else {
252+
LOG.warn("File {} already exists", storeLocation.getAbsolutePath());
253+
}
244254
} catch (IOException e) {
245255
LOG.error("Cannot write uploaded empty file to disk: {}", storeLocation.getAbsolutePath(), e);
246256
}
@@ -357,15 +367,31 @@ public InputStream getInputStream() throws IOException {
357367
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#cleanUp()
358368
*/
359369
public void cleanUp() {
360-
Set<String> names = files.keySet();
361-
for (String name : names) {
362-
List<FileItem> items = files.get(name);
363-
for (FileItem item : items) {
364-
LOG.debug("Removing file [{}]", normalizeSpace(name));
365-
if (!item.isInMemory()) {
366-
item.delete();
370+
try {
371+
LOG.debug("Performing comprehensive cleanup for {} file items.", allFileItems.size());
372+
for (FileItem item : allFileItems) {
373+
try {
374+
if (item instanceof DiskFileItem) {
375+
DiskFileItem diskItem = (DiskFileItem) item;
376+
File storeLocation = diskItem.getStoreLocation();
377+
if (storeLocation != null && storeLocation.exists()) {
378+
LOG.debug("Deleting temporary file: [{}]", storeLocation.getName());
379+
if (!storeLocation.delete()) {
380+
LOG.warn("Unable to delete temporary file: [{}]", storeLocation.getName());
381+
}
382+
}
383+
}
384+
// Also call the item's delete method as backup
385+
if (!item.isInMemory()) {
386+
item.delete();
387+
}
388+
} catch (Exception e) {
389+
LOG.warn("Error during cleanup of file item: [{}]", normalizeSpace(item.getFieldName()), e);
367390
}
368391
}
392+
} finally {
393+
// Clear only the tracking collection, preserve parsed data
394+
allFileItems.clear();
369395
}
370396
}
371397

0 commit comments

Comments
 (0)