Skip to content

Commit b743793

Browse files
committed
feat: support packer config
1 parent 0d3b56d commit b743793

85 files changed

Lines changed: 2438 additions & 650 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

boot/src/main/java/com/reajason/javaweb/boot/api/GlobalExceptionHandler.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ public ErrorResponse handleGenerationException(GenerationException exception) {
2929
return new ErrorResponse(exception.getMessage());
3030
}
3131

32+
@ResponseStatus(HttpStatus.BAD_REQUEST)
33+
@ExceptionHandler(IllegalArgumentException.class)
34+
public ErrorResponse handleIllegalArgumentException(IllegalArgumentException exception) {
35+
return new ErrorResponse(exception.getMessage());
36+
}
37+
3238
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
3339
@ExceptionHandler(Throwable.class)
3440
public ErrorResponse handleThrowable(Throwable throwable) {

boot/src/main/java/com/reajason/javaweb/boot/controller/ConfigController.java

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,24 @@ public Map<String, List<String>> getServers() {
3434
}
3535

3636
@RequestMapping("/packers")
37-
public List<String> getPackers() {
38-
return Arrays.stream(Packers.values())
39-
.filter(packers -> packers.getParentPacker() == null)
40-
.map(Packers::name).toList();
37+
public List<PackerCategoryDTO> getPackers() {
38+
List<PackerCategoryDTO> result = new ArrayList<>();
39+
for (Map.Entry<String, List<Packers>> entry : Packers.groupedPackers().entrySet()) {
40+
PackerCategoryDTO category = new PackerCategoryDTO();
41+
category.setName(entry.getKey());
42+
List<PackerOptionDTO> options = new ArrayList<>();
43+
for (Packers packer : entry.getValue()) {
44+
PackerOptionDTO option = new PackerOptionDTO();
45+
option.setName(packer.name());
46+
option.setOutputKind(packer.getOutputKind());
47+
option.setCategoryAnchor(packer.hasChildren());
48+
option.setSchema(packer.getSchema());
49+
options.add(option);
50+
}
51+
category.setPackers(options);
52+
result.add(category);
53+
}
54+
return result;
4155
}
4256

4357
@RequestMapping
@@ -66,4 +80,64 @@ public CommandConfigVO getCommandConfigs() {
6680
commandConfigVO.setImplementationClasses(Arrays.stream(CommandConfig.ImplementationClass.values()).toList());
6781
return commandConfigVO;
6882
}
69-
}
83+
84+
public static class PackerCategoryDTO {
85+
private String name;
86+
private List<PackerOptionDTO> packers;
87+
88+
public String getName() {
89+
return name;
90+
}
91+
92+
public void setName(String name) {
93+
this.name = name;
94+
}
95+
96+
public List<PackerOptionDTO> getPackers() {
97+
return packers;
98+
}
99+
100+
public void setPackers(List<PackerOptionDTO> packers) {
101+
this.packers = packers;
102+
}
103+
}
104+
105+
public static class PackerOptionDTO {
106+
private String name;
107+
private String outputKind;
108+
private boolean categoryAnchor;
109+
private Object schema;
110+
111+
public String getName() {
112+
return name;
113+
}
114+
115+
public void setName(String name) {
116+
this.name = name;
117+
}
118+
119+
public String getOutputKind() {
120+
return outputKind;
121+
}
122+
123+
public void setOutputKind(String outputKind) {
124+
this.outputKind = outputKind;
125+
}
126+
127+
public boolean isCategoryAnchor() {
128+
return categoryAnchor;
129+
}
130+
131+
public void setCategoryAnchor(boolean categoryAnchor) {
132+
this.categoryAnchor = categoryAnchor;
133+
}
134+
135+
public Object getSchema() {
136+
return schema;
137+
}
138+
139+
public void setSchema(Object schema) {
140+
this.schema = schema;
141+
}
142+
}
143+
}

boot/src/main/java/com/reajason/javaweb/boot/controller/MemShellGeneratorController.java

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
import com.reajason.javaweb.memshell.config.InjectorConfig;
88
import com.reajason.javaweb.memshell.config.ShellConfig;
99
import com.reajason.javaweb.memshell.config.ShellToolConfig;
10-
import com.reajason.javaweb.packer.AggregatePacker;
10+
import com.reajason.javaweb.packer.ClassPackerConfig;
1111
import com.reajason.javaweb.packer.JarPacker;
12+
import com.reajason.javaweb.packer.JarPackerConfig;
1213
import com.reajason.javaweb.packer.Packer;
14+
import com.reajason.javaweb.packer.Packers;
1315
import org.springframework.web.bind.annotation.*;
1416

1517
import java.util.Base64;
@@ -22,19 +24,24 @@
2224
@RequestMapping("/api/memshell/generate")
2325
@CrossOrigin("*")
2426
public class MemShellGeneratorController {
27+
2528
@PostMapping
2629
public MemShellGenerateResponse generate(@RequestBody MemShellGenerateRequest request) {
2730
ShellConfig shellConfig = request.getShellConfig();
2831
ShellToolConfig shellToolConfig = request.parseShellToolConfig();
2932
InjectorConfig injectorConfig = request.getInjectorConfig();
3033
MemShellResult generateResult = MemShellGenerator.generate(shellConfig, injectorConfig, shellToolConfig);
31-
Packer packer = request.getPacker().getInstance();
32-
if (packer instanceof AggregatePacker) {
33-
return new MemShellGenerateResponse(generateResult, ((AggregatePacker) packer).packAll(generateResult.toClassPackerConfig()));
34+
if (request.getPackerSpec() == null) {
35+
throw new IllegalArgumentException("packerSpec is required");
3436
}
37+
Packers packers = Packers.fromName(request.getPackerSpec().getName());
38+
Packer<?> packer = packers.getInstance();
3539
if (packer instanceof JarPacker) {
36-
return new MemShellGenerateResponse(generateResult, Base64.getEncoder().encodeToString(((JarPacker) packer).packBytes(generateResult.toJarPackerConfig())));
40+
JarPackerConfig<?> jarPackerConfig = generateResult.toJarPackerConfig();
41+
return new MemShellGenerateResponse(generateResult, Base64.getEncoder().encodeToString(((JarPacker) packer).packBytes(jarPackerConfig)));
3742
}
38-
return new MemShellGenerateResponse(generateResult, packer.pack(generateResult.toClassPackerConfig()));
43+
ClassPackerConfig classPackerConfig = generateResult.toClassPackerConfig();
44+
classPackerConfig.setCustomConfig(packer.resolveCustomConfig(request.getPackerSpec().getConfig()));
45+
return new MemShellGenerateResponse(generateResult, packer.pack(classPackerConfig));
3946
}
40-
}
47+
}

boot/src/main/java/com/reajason/javaweb/boot/controller/ProbeShellGeneratorController.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
import com.reajason.javaweb.boot.dto.ProbeShellGenerateRequest;
44
import com.reajason.javaweb.boot.dto.ProbeShellGenerateResponse;
5-
import com.reajason.javaweb.packer.AggregatePacker;
5+
import com.reajason.javaweb.packer.ClassPackerConfig;
66
import com.reajason.javaweb.packer.Packer;
7+
import com.reajason.javaweb.packer.Packers;
78
import com.reajason.javaweb.probe.ProbeShellGenerator;
89
import com.reajason.javaweb.probe.ProbeShellResult;
910
import com.reajason.javaweb.probe.config.ProbeConfig;
@@ -23,11 +24,13 @@ public ProbeShellGenerateResponse generate(@RequestBody ProbeShellGenerateReques
2324
ProbeConfig probeConfig = request.getProbeConfig();
2425
ProbeContentConfig probeContentConfig = request.parseProbeContentConfig();
2526
ProbeShellResult generateResult = ProbeShellGenerator.generate(probeConfig, probeContentConfig);
26-
Packer packer = request.getPacker().getInstance();
27-
if (packer instanceof AggregatePacker) {
28-
return new ProbeShellGenerateResponse(generateResult, ((AggregatePacker) packer).packAll(generateResult.toClassPackerConfig()));
29-
} else {
30-
return new ProbeShellGenerateResponse(generateResult, packer.pack(generateResult.toClassPackerConfig()));
27+
if (request.getPackerSpec() == null) {
28+
throw new IllegalArgumentException("packerSpec is required");
3129
}
30+
Packers packers = Packers.fromName(request.getPackerSpec().getName());
31+
Packer packer = packers.getInstance();
32+
ClassPackerConfig classPackerConfig = generateResult.toClassPackerConfig();
33+
classPackerConfig.setCustomConfig(packer.resolveCustomConfig(request.getPackerSpec().getConfig()));
34+
return new ProbeShellGenerateResponse(generateResult, packer.pack(classPackerConfig));
3235
}
3336
}

boot/src/main/java/com/reajason/javaweb/boot/dto/MemShellGenerateRequest.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.reajason.javaweb.boot.dto;
22

33
import com.reajason.javaweb.memshell.config.*;
4-
import com.reajason.javaweb.packer.Packers;
54
import lombok.Data;
65

76
import static com.reajason.javaweb.memshell.ShellTool.*;
@@ -15,7 +14,7 @@ public class MemShellGenerateRequest {
1514
private ShellConfig shellConfig;
1615
private ShellToolConfigDTO shellToolConfig;
1716
private InjectorConfig injectorConfig;
18-
private Packers packer;
17+
private PackerRequestSpecDTO packerSpec;
1918

2019
@Data
2120
public static class ShellToolConfigDTO {
@@ -84,4 +83,4 @@ public ShellToolConfig parseShellToolConfig() {
8483
default -> throw new UnsupportedOperationException("unknown shell tool " + shellConfig.getShellTool());
8584
};
8685
}
87-
}
86+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.reajason.javaweb.boot.dto;
2+
3+
import com.reajason.javaweb.packer.spec.PackerRequestSpec;
4+
import lombok.Data;
5+
6+
import java.util.LinkedHashMap;
7+
import java.util.Map;
8+
9+
/**
10+
* Packer selection request payload.
11+
*/
12+
@Data
13+
public class PackerRequestSpecDTO {
14+
private String name;
15+
private Map<String, Object> config = new LinkedHashMap<>();
16+
17+
public PackerRequestSpec toPackerRequestSpec() {
18+
PackerRequestSpec spec = new PackerRequestSpec();
19+
spec.setName(name);
20+
if (config != null) {
21+
spec.setConfig(config);
22+
}
23+
return spec;
24+
}
25+
}

boot/src/main/java/com/reajason/javaweb/boot/dto/ProbeShellGenerateRequest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.reajason.javaweb.boot.dto;
22

3-
import com.reajason.javaweb.packer.Packers;
43
import com.reajason.javaweb.probe.config.*;
54
import lombok.Data;
65

@@ -12,7 +11,7 @@
1211
public class ProbeShellGenerateRequest {
1312
private ProbeConfig probeConfig;
1413
private ProbeContentConfigDTO probeContentConfig;
15-
private Packers packer;
14+
private PackerRequestSpecDTO packerSpec;
1615

1716
@Data
1817
static class ProbeContentConfigDTO {

boot/src/test/java/com/reajason/javaweb/boot/controller/ConfigControllerIntegrationTest.java

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@
99

1010
import java.util.List;
1111
import java.util.Map;
12+
import java.util.Objects;
1213

1314
import static org.junit.jupiter.api.Assertions.assertEquals;
15+
import static org.junit.jupiter.api.Assertions.assertFalse;
1416
import static org.junit.jupiter.api.Assertions.assertNotNull;
17+
import static org.junit.jupiter.api.Assertions.assertTrue;
1518

1619
/**
1720
* @author ReaJason
@@ -43,5 +46,38 @@ public void testConfigPackersEndpoint() {
4346
ResponseEntity<List> response = restTemplate.getForEntity("/api/config/packers", List.class);
4447
assertEquals(HttpStatus.OK, response.getStatusCode());
4548
assertNotNull(response.getBody());
49+
Map<?, ?> firstCategory = (Map<?, ?>) response.getBody().get(0);
50+
assertNotNull(firstCategory.get("name"));
51+
List<?> packers = (List<?>) firstCategory.get("packers");
52+
assertNotNull(packers);
53+
Map<?, ?> firstPacker = (Map<?, ?>) packers.get(0);
54+
assertNotNull(firstPacker.get("name"));
55+
assertNotNull(firstPacker.get("schema"));
56+
57+
Map<?, ?> jspCategory = response.getBody().stream()
58+
.map(item -> (Map<?, ?>) item)
59+
.filter(item -> Objects.equals(item.get("name"), "JSP"))
60+
.findFirst()
61+
.orElseThrow(() -> new AssertionError("JSP category not found"));
62+
List<?> jspPackers = (List<?>) jspCategory.get("packers");
63+
List<String> jspPackerNames = jspPackers.stream()
64+
.map(item -> (Map<?, ?>) item)
65+
.map(item -> (String) item.get("name"))
66+
.toList();
67+
assertFalse(jspPackerNames.contains("ClassLoaderJSPUnicode"));
68+
assertFalse(jspPackerNames.contains("DefineClassJSPUnicode"));
69+
assertFalse(jspPackerNames.contains("JSPXUnicode"));
70+
71+
Map<?, ?> classLoaderJsp = jspPackers.stream()
72+
.map(item -> (Map<?, ?>) item)
73+
.filter(item -> Objects.equals(item.get("name"), "ClassLoaderJSP"))
74+
.findFirst()
75+
.orElseThrow(() -> new AssertionError("ClassLoaderJSP not found"));
76+
Map<?, ?> schema = (Map<?, ?>) classLoaderJsp.get("schema");
77+
List<?> fields = (List<?>) schema.get("fields");
78+
boolean hasUnicodeField = fields.stream()
79+
.map(item -> (Map<?, ?>) item)
80+
.anyMatch(item -> Objects.equals(item.get("key"), "unicode"));
81+
assertTrue(hasUnicodeField);
4682
}
47-
}
83+
}

boot/src/test/java/com/reajason/javaweb/boot/controller/MemShellGeneratorControllerTest.java

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,20 @@
33
import com.reajason.javaweb.Server;
44
import com.reajason.javaweb.boot.dto.MemShellGenerateRequest;
55
import com.reajason.javaweb.boot.dto.MemShellGenerateResponse;
6+
import com.reajason.javaweb.boot.dto.PackerRequestSpecDTO;
67
import com.reajason.javaweb.memshell.ShellTool;
78
import com.reajason.javaweb.memshell.ShellType;
89
import com.reajason.javaweb.memshell.config.InjectorConfig;
910
import com.reajason.javaweb.memshell.config.ShellConfig;
10-
import com.reajason.javaweb.packer.Packers;
1111
import org.junit.jupiter.api.Test;
1212
import org.springframework.beans.factory.annotation.Autowired;
1313
import org.springframework.boot.test.context.SpringBootTest;
1414
import org.springframework.boot.test.web.client.TestRestTemplate;
1515
import org.springframework.http.HttpStatus;
1616
import org.springframework.http.ResponseEntity;
1717

18+
import java.util.Map;
19+
1820
import static org.junit.jupiter.api.Assertions.assertEquals;
1921
import static org.junit.jupiter.api.Assertions.assertNotNull;
2022

@@ -30,6 +32,23 @@ class MemShellGeneratorControllerTest {
3032

3133
@Test
3234
void generateShell() {
35+
MemShellGenerateRequest request = buildRequest("ScriptEngine", null);
36+
ResponseEntity<MemShellGenerateResponse> response = restTemplate.postForEntity(
37+
"/api/memshell/generate", request, MemShellGenerateResponse.class);
38+
assertEquals(HttpStatus.OK, response.getStatusCode());
39+
assertNotNull(response.getBody());
40+
}
41+
42+
@Test
43+
void generateJspxShellWithCustomConfig() {
44+
MemShellGenerateRequest request = buildRequest("JSPX", Map.of("unicode", true));
45+
ResponseEntity<MemShellGenerateResponse> response = restTemplate.postForEntity(
46+
"/api/memshell/generate", request, MemShellGenerateResponse.class);
47+
assertEquals(HttpStatus.OK, response.getStatusCode());
48+
assertNotNull(response.getBody());
49+
}
50+
51+
private static MemShellGenerateRequest buildRequest(String packerName, Map<String, Object> packerConfig) {
3352
MemShellGenerateRequest request = new MemShellGenerateRequest();
3453
request.setShellConfig(ShellConfig.builder()
3554
.server(Server.Tomcat)
@@ -43,16 +62,18 @@ void generateShell() {
4362
request.setInjectorConfig(InjectorConfig.builder()
4463
.urlPattern("/*")
4564
.build());
46-
request.setPacker(Packers.ScriptEngine);
65+
PackerRequestSpecDTO packerRequestSpecDTO = new PackerRequestSpecDTO();
66+
packerRequestSpecDTO.setName(packerName);
67+
if (packerConfig != null) {
68+
packerRequestSpecDTO.setConfig(packerConfig);
69+
}
70+
request.setPackerSpec(packerRequestSpecDTO);
4771
MemShellGenerateRequest.ShellToolConfigDTO shellToolConfigDTO = new MemShellGenerateRequest.ShellToolConfigDTO();
4872
shellToolConfigDTO.setGodzillaKey("key");
4973
shellToolConfigDTO.setGodzillaPass("pass");
5074
shellToolConfigDTO.setHeaderName("User-Agent");
5175
shellToolConfigDTO.setHeaderValue("hello");
5276
request.setShellToolConfig(shellToolConfigDTO);
53-
ResponseEntity<MemShellGenerateResponse> response = restTemplate.postForEntity(
54-
"/api/memshell/generate", request, MemShellGenerateResponse.class);
55-
assertEquals(HttpStatus.OK, response.getStatusCode());
56-
assertNotNull(response.getBody());
77+
return request;
5778
}
58-
}
79+
}

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ idea {
99
}
1010
}
1111

12-
version = "2.6.0"
12+
version = "2.7.0-SNAPSHOT"
1313

1414
tasks.register("publishAllToMavenCentral") {
1515
dependsOn(":memshell-party-common:publishToMavenCentral")

0 commit comments

Comments
 (0)