diff --git a/fesod/pom.xml b/fesod/pom.xml index 79c9eb891..69331e8e8 100644 --- a/fesod/pom.xml +++ b/fesod/pom.xml @@ -174,4 +174,79 @@ + + + + java9-modules + + [9,) + + + + --add-opens org.apache.poi.ooxml/org.apache.poi.xssf.streaming=org.apache.fesod + --add-opens org.apache.poi.ooxml/org.apache.poi.xssf.usermodel=org.apache.fesod + --add-opens org.apache.poi.ooxml/org.apache.poi.xssf.usermodel.helpers=org.apache.fesod + --add-opens org.apache.poi.ooxml/org.apache.poi.openxml4j.opc.internal=org.apache.fesod + --add-opens org.apache.poi.poi/org.apache.poi.hssf.usermodel=org.apache.fesod + --add-opens java.base/sun.reflect.annotation=org.apache.fesod + + false + false + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + 9 + + + + compile-module-info + compile + + compile + + + 9 + + ${project.basedir}/src/main/java9 + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.5.0 + + + add-java9-test-sources + generate-test-sources + + add-test-source + + + + ${project.basedir}/src/test/java9 + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + ${surefire.jvm.args} ${surefire.jdk9plus.args} ${argLine} + + + + + + diff --git a/fesod/src/main/java9/module-info.java b/fesod/src/main/java9/module-info.java new file mode 100644 index 000000000..3511dd460 --- /dev/null +++ b/fesod/src/main/java9/module-info.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +open module org.apache.fesod { + requires java.base; + requires java.logging; + requires java.xml; + requires java.sql; + + requires org.apache.commons.collections4; + requires org.apache.commons.compress; + requires org.apache.commons.csv; + requires org.apache.commons.io; + requires org.apache.poi.ooxml; + requires org.apache.poi.ooxml.schemas; + requires org.apache.poi.poi; + requires org.apache.xmlbeans; + requires fastexcel.support; + requires ehcache; + requires org.slf4j; + + requires static lombok; + + exports org.apache.fesod.excel; + exports org.apache.fesod.excel.analysis; + exports org.apache.fesod.excel.analysis.csv; + exports org.apache.fesod.excel.analysis.v03; + exports org.apache.fesod.excel.analysis.v03.handlers; + exports org.apache.fesod.excel.analysis.v07; + exports org.apache.fesod.excel.analysis.v07.handlers; + exports org.apache.fesod.excel.analysis.v07.handlers.sax; + exports org.apache.fesod.excel.annotation; + exports org.apache.fesod.excel.annotation.format; + exports org.apache.fesod.excel.annotation.write.style; + exports org.apache.fesod.excel.cache; + exports org.apache.fesod.excel.cache.selector; + exports org.apache.fesod.excel.constant; + exports org.apache.fesod.excel.context; + exports org.apache.fesod.excel.context.csv; + exports org.apache.fesod.excel.context.xls; + exports org.apache.fesod.excel.context.xlsx; + exports org.apache.fesod.excel.converters; + exports org.apache.fesod.excel.converters.bigdecimal; + exports org.apache.fesod.excel.converters.biginteger; + exports org.apache.fesod.excel.converters.booleanconverter; + exports org.apache.fesod.excel.converters.bytearray; + exports org.apache.fesod.excel.converters.byteconverter; + exports org.apache.fesod.excel.converters.date; + exports org.apache.fesod.excel.converters.doubleconverter; + exports org.apache.fesod.excel.converters.file; + exports org.apache.fesod.excel.converters.floatconverter; + exports org.apache.fesod.excel.converters.inputstream; + exports org.apache.fesod.excel.converters.integer; + exports org.apache.fesod.excel.converters.localdate; + exports org.apache.fesod.excel.converters.localdatetime; + exports org.apache.fesod.excel.converters.longconverter; + exports org.apache.fesod.excel.converters.shortconverter; + exports org.apache.fesod.excel.converters.string; + exports org.apache.fesod.excel.converters.url; + exports org.apache.fesod.excel.enums; + exports org.apache.fesod.excel.enums.poi; + exports org.apache.fesod.excel.event; + exports org.apache.fesod.excel.exception; + exports org.apache.fesod.excel.metadata; + exports org.apache.fesod.excel.metadata.csv; + exports org.apache.fesod.excel.metadata.data; + exports org.apache.fesod.excel.metadata.format; + exports org.apache.fesod.excel.metadata.property; + exports org.apache.fesod.excel.read.builder; + exports org.apache.fesod.excel.read.listener; + exports org.apache.fesod.excel.read.metadata; + exports org.apache.fesod.excel.read.metadata.holder; + exports org.apache.fesod.excel.read.metadata.holder.csv; + exports org.apache.fesod.excel.read.metadata.holder.xls; + exports org.apache.fesod.excel.read.metadata.holder.xlsx; + exports org.apache.fesod.excel.read.metadata.property; + exports org.apache.fesod.excel.read.processor; + exports org.apache.fesod.excel.support; + exports org.apache.fesod.excel.util; + exports org.apache.fesod.excel.write; + exports org.apache.fesod.excel.write.builder; + exports org.apache.fesod.excel.write.executor; + exports org.apache.fesod.excel.write.handler; + exports org.apache.fesod.excel.write.handler.chain; + exports org.apache.fesod.excel.write.handler.context; + exports org.apache.fesod.excel.write.handler.impl; + exports org.apache.fesod.excel.write.merge; + exports org.apache.fesod.excel.write.metadata; + exports org.apache.fesod.excel.write.metadata.fill; + exports org.apache.fesod.excel.write.metadata.holder; + exports org.apache.fesod.excel.write.metadata.style; + exports org.apache.fesod.excel.write.property; + exports org.apache.fesod.excel.write.style; + exports org.apache.fesod.excel.write.style.column; + exports org.apache.fesod.excel.write.style.row; +} diff --git a/fesod/src/test/java/org/apache/fesod/excel/module/ModuleDefinitionTestPlaceholder.java b/fesod/src/test/java/org/apache/fesod/excel/module/ModuleDefinitionTestPlaceholder.java new file mode 100644 index 000000000..7d7676851 --- /dev/null +++ b/fesod/src/test/java/org/apache/fesod/excel/module/ModuleDefinitionTestPlaceholder.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fesod.excel.module; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledForJreRange; +import org.junit.jupiter.api.condition.JRE; + +/** + * Placeholder to keep Java 8 test compilation green. The real module descriptor test + * (requiring Java 9+) lives under src/test/java9 with class name {@code ModuleDefinitionTest}. + * Disabled for all JRE >= 9 via @DisabledForJreRange to remain future-proof. + */ +@DisabledForJreRange(min = JRE.JAVA_9) +class ModuleDefinitionTestPlaceholder { + + @Test + void noop() { + // Intentionally empty. + } +} diff --git a/fesod/src/test/java/org/apache/fesod/excel/util/TestFileUtil.java b/fesod/src/test/java/org/apache/fesod/excel/util/TestFileUtil.java index fe1a3643a..773fd0d14 100644 --- a/fesod/src/test/java/org/apache/fesod/excel/util/TestFileUtil.java +++ b/fesod/src/test/java/org/apache/fesod/excel/util/TestFileUtil.java @@ -21,18 +21,23 @@ import java.io.File; import java.io.InputStream; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import org.apache.commons.collections4.CollectionUtils; public class TestFileUtil { public static InputStream getResourcesFileInputStream(String fileName) { - return Thread.currentThread().getContextClassLoader().getResourceAsStream("" + fileName); + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + return contextClassLoader == null ? null : contextClassLoader.getResourceAsStream(fileName); } public static String getPath() { - return TestFileUtil.class.getResource("/").getPath(); + return resolveRootPath(); } public static TestPathBuild pathBuild() { @@ -40,7 +45,7 @@ public static TestPathBuild pathBuild() { } public static File createNewFile(String pathName) { - File file = new File(getPath() + pathName); + File file = pathFromRoot(pathName).toFile(); if (file.exists()) { file.delete(); } else { @@ -52,7 +57,7 @@ public static File createNewFile(String pathName) { } public static File readFile(String pathName) { - return new File(getPath() + pathName); + return pathFromRoot(pathName).toFile(); } public static File readUserHomeFile(String pathName) { @@ -75,14 +80,14 @@ public TestPathBuild sub(String dirOrFile) { } public String getPath() { + String rootPath = resolveRootPath(); if (CollectionUtils.isEmpty(subPath)) { - return TestFileUtil.class.getResource("/").getPath(); + return rootPath; } if (subPath.size() == 1) { - return TestFileUtil.class.getResource("/").getPath() + subPath.get(0); + return rootPath + subPath.get(0); } - StringBuilder path = - new StringBuilder(TestFileUtil.class.getResource("/").getPath()); + StringBuilder path = new StringBuilder(rootPath); path.append(subPath.get(0)); for (int i = 1; i < subPath.size(); i++) { path.append(File.separator).append(subPath.get(i)); @@ -90,4 +95,56 @@ public String getPath() { return path.toString(); } } + + private static String resolveRootPath() { + Path codeSourceLocation = resolveFromCodeSource(); + if (codeSourceLocation != null) { + return appendSeparator(codeSourceLocation.toString()); + } + + URL resource = null; + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + if (contextClassLoader != null) { + resource = contextClassLoader.getResource(""); + } + if (resource == null) { + resource = TestFileUtil.class.getResource("/"); + } + URL finalResource = Objects.requireNonNull(resource, "Unable to locate test resources root"); + String path = finalResource.getPath(); + if ("file".equals(finalResource.getProtocol())) { + if (path.startsWith("file:")) { + path = path.substring("file:".length()); + } + } + return appendSeparator(path); + } + + private static Path pathFromRoot(String relativePath) { + return Paths.get(resolveRootPath(), relativePath); + } + + private static Path resolveFromCodeSource() { + try { + if (TestFileUtil.class.getProtectionDomain() == null + || TestFileUtil.class.getProtectionDomain().getCodeSource() == null) { + return null; + } + Path location = Paths.get(TestFileUtil.class + .getProtectionDomain() + .getCodeSource() + .getLocation() + .toURI()); + if (!location.toFile().isDirectory()) { + return null; + } + return location; + } catch (Exception ex) { + return null; + } + } + + private static String appendSeparator(String path) { + return path.endsWith(File.separator) ? path : path + File.separator; + } } diff --git a/fesod/src/test/java9/org/apache/fesod/excel/module/ConsumerModuleUsageTest.java b/fesod/src/test/java9/org/apache/fesod/excel/module/ConsumerModuleUsageTest.java new file mode 100644 index 000000000..d7f0f3852 --- /dev/null +++ b/fesod/src/test/java9/org/apache/fesod/excel/module/ConsumerModuleUsageTest.java @@ -0,0 +1,232 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fesod.excel.module; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * Simulates an external consumer module that depends on org.apache.fesod. + * Steps: + * 1. Create an in-memory module 'test.consumer' with module-info requiring org.apache.fesod. + * 2. Compile it with javac --module-path (including current project's target/classes + dependency jars). + * 3. Run its main class via 'java --module-path ... -m test.consumer/test.consumer.Main' and assert output. + */ +class ConsumerModuleUsageTest { + + @Test + @DisplayName("External consumer module compiles, instantiates ExcelReader and runs on module path") + void externalConsumerCompilesAndRuns() throws Exception { + String javac = resolveJavacExecutable(); + assumeTrue(javac != null, "Cannot locate javac executable - skipping consumer module test"); + + Path targetClasses = Paths.get("target", "classes").toAbsolutePath(); + assertTrue(Files.isDirectory(targetClasses), "target/classes missing"); + + // Prefer the module path Maven Surefire already resolved (contains all needed automatic/named modules) + String surefireModulePath = System.getProperty("jdk.module.path"); + List filteredJars; + String modulePath; + boolean usedSurefirePath = false; + if (surefireModulePath != null && !surefireModulePath.isEmpty()) { + modulePath = surefireModulePath; // already includes target/classes + filteredJars = Arrays.asList(surefireModulePath.split(Pattern.quote(File.pathSeparator))); + usedSurefirePath = true; + } else { + // Fallback to previous exclusion filtering if property not present (IDE run) + String classPath = System.getProperty("java.class.path", ""); + List allJars = Arrays.stream(classPath.split(Pattern.quote(File.pathSeparator))) + .filter(s -> s.endsWith(".jar")) + .distinct() + .collect(Collectors.toList()); + List excludeSubstrings = Arrays.asList( + "junit", "mockito", "mockserver", "jazzer", "hamcrest", "slf4j-simple", "jcl-over-slf4j", + "log4j-over-slf4j", "logback", "swagger", "velocity", "xmlunit", "json-schema", "json-path", + "jmustache", "rhino", "json-patch", "json-simple", "uuid-generator" + ); + filteredJars = allJars.stream() + .filter(j -> excludeSubstrings.stream().noneMatch(j::contains)) + .collect(Collectors.toList()); + assumeTrue(!filteredJars.isEmpty(), "Could not derive dependency module path; skipping consumer module test"); + modulePath = Stream.concat(Stream.of(targetClasses.toString()), filteredJars.stream()) + .collect(Collectors.joining(File.pathSeparator)); + } + + Path workDir = Files.createTempDirectory("fesod-consumer-"); + Path moduleRoot = Files.createDirectories(workDir.resolve("src").resolve("test.consumer")); + + String moduleInfo = "module test.consumer {\n" + + " requires org.apache.fesod;\n" + + "}\n"; + Path moduleInfoFile = moduleRoot.resolve("module-info.java"); + Files.write(moduleInfoFile, moduleInfo.getBytes(StandardCharsets.UTF_8)); + + Path pkgDir = Files.createDirectories(moduleRoot.resolve("test/consumer")); + Path mainFile = pkgDir.resolve("Main.java"); + // Provide a minimal CSV input stream so constructing ExcelReader does not throw (lazy usage demonstration) + String mainSrc = "package test.consumer;\n" + + "import org.apache.fesod.excel.ExcelReader;\n" + + "import org.apache.fesod.excel.read.metadata.ReadWorkbook;\n" + + "import org.apache.fesod.excel.support.ExcelTypeEnum;\n" + + "public class Main {\n" + + " public static void main(String[] args) {\n" + + " ReadWorkbook wb = new ReadWorkbook();\n" + + " wb.setExcelType(ExcelTypeEnum.CSV);\n" + + " wb.setInputStream(new java.io.ByteArrayInputStream(\"col1,col2\\n1,2\\n\".getBytes(java.nio.charset.StandardCharsets.UTF_8)));\n" + + " try (ExcelReader reader = new ExcelReader(wb)) {\n" + + " System.out.println(\"CREATED:\" + ExcelReader.class.getName());\n" + + " }\n" + + " }\n" + + "}\n"; + Files.write(mainFile, mainSrc.getBytes(StandardCharsets.UTF_8)); + + Path modsOut = Files.createDirectories(workDir.resolve("mods")); + + List javacCmd = new ArrayList<>(); + javacCmd.add(javac); + javacCmd.add("--module-path"); + javacCmd.add(modulePath); + javacCmd.add("-d"); + javacCmd.add(modsOut.toString()); + javacCmd.add(moduleInfoFile.toString()); + javacCmd.add(mainFile.toString()); + + Process compileProc = new ProcessBuilder(javacCmd) + .redirectErrorStream(true) + .start(); + String compileOut = new String(compileProc.getInputStream().readAllBytes(), StandardCharsets.UTF_8); + int compileExit = compileProc.waitFor(); + if (compileExit != 0) { + assumeTrue(false, "Consumer module compilation failed." + + "\nUsed surefire path=" + usedSurefirePath + + "\nExit=" + compileExit + + "\nModule path size=" + filteredJars.size() + + "\nCompile output:\n" + compileOut); + } + + // Run + RunResult result = runConsumer(modulePath, modsOut); + if (result.exit == 0) { + assertTrue(result.output.contains("CREATED:org.apache.fesod.excel.ExcelReader"), "Output missing class name: " + result.output); + return; + } + // If failing with FindException when using fallback path, skip (informational) instead of failing build + if (!usedSurefirePath && result.output.contains("FindException")) { + assumeTrue(false, () -> "Skipping consumer run due to module resolution failure on fallback path. Output=\n" + result.output); + } + fail("Run failed exit=" + result.exit + "\nUsed surefire path=" + usedSurefirePath + "\nOUTPUT=\n" + result.output); + } + + private static class RunResult { + int exit; + String output; + } + + private RunResult runConsumer(String modulePath, Path modsOut) throws IOException, InterruptedException { + List javaCmd = new ArrayList<>(); + javaCmd.add("java"); + javaCmd.add("--module-path"); + javaCmd.add(modulePath + File.pathSeparator + modsOut); + Collections.addAll(javaCmd, + "--add-opens", "org.apache.poi.ooxml/org.apache.poi.xssf.streaming=org.apache.fesod", + "--add-opens", "org.apache.poi.ooxml/org.apache.poi.xssf.usermodel=org.apache.fesod", + "--add-opens", "org.apache.poi.ooxml/org.apache.poi.xssf.usermodel.helpers=org.apache.fesod", + "--add-opens", "org.apache.poi.ooxml/org.apache.poi.openxml4j.opc.internal=org.apache.fesod", + "--add-opens", "org.apache.poi.poi/org.apache.poi.hssf.usermodel=org.apache.fesod", + "--add-opens", "java.base/sun.reflect.annotation=org.apache.fesod" + ); + javaCmd.add("-m"); + javaCmd.add("test.consumer/test.consumer.Main"); + Process runProc = new ProcessBuilder(javaCmd) + .redirectErrorStream(true) + .start(); + String runOut = new String(runProc.getInputStream().readAllBytes(), StandardCharsets.UTF_8).trim(); + int runExit = runProc.waitFor(); + RunResult r = new RunResult(); + r.exit = runExit; + r.output = runOut; + return r; + } + + private Optional parseMissingModule(String output) { + // Match lines like: Module org.apache.commons.lang3 not found, required by ... + Matcher m = Pattern.compile("Module\\s+([a-zA-Z0-9_.]+)\\s+not\\s+found").matcher(output); + if (m.find()) { + return Optional.of(m.group(1)); + } + return Optional.empty(); + } + + private String simpleNameFromModule(String moduleName) { + // e.g. org.apache.commons.lang3 -> lang3 or commons-lang3 heuristic + if (moduleName.contains("lang3")) return "lang3"; + int idx = moduleName.lastIndexOf('.'); + return idx > -1 ? moduleName.substring(idx + 1) : moduleName; + } + + private String resolveJavacExecutable() { + String javaHome = System.getProperty("java.home"); + if (javaHome != null) { + Path p = Paths.get(javaHome, "bin", isWindows() ? "javac.exe" : "javac"); + if (Files.isRegularFile(p) && Files.isReadable(p)) { + return p.toString(); + } + } + // Fallback: rely on PATH + return isCommandOnPath(isWindows() ? "javac.exe" : "javac"); + } + + private boolean isWindows() { + return System.getProperty("os.name", "?").toLowerCase(Locale.ROOT).contains("win"); + } + + private String isCommandOnPath(String name) { + String path = System.getenv("PATH"); + if (path == null) return null; + for (String dir : path.split(Pattern.quote(File.pathSeparator))) { + Path cand = Paths.get(dir, name); + if (Files.isRegularFile(cand) && Files.isReadable(cand)) { + return cand.toString(); + } + } + return null; + } +} diff --git a/fesod/src/test/java9/org/apache/fesod/excel/module/ModuleDefinitionTest.java b/fesod/src/test/java9/org/apache/fesod/excel/module/ModuleDefinitionTest.java new file mode 100644 index 000000000..99b4aa428 --- /dev/null +++ b/fesod/src/test/java9/org/apache/fesod/excel/module/ModuleDefinitionTest.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fesod.excel.module; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import java.lang.module.ModuleDescriptor; +import org.apache.fesod.excel.ExcelReader; // updated to existing class +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** + * Java 9+ only test verifying the module descriptor remains open (or at least present) and + * has the expected name. Located under src/test/java9 and compiled only when JDK >= 9. + */ +class ModuleDefinitionTest { + + @Test + @DisplayName("Module descriptor is named and currently open (or skip if running on classpath)") + void moduleDescriptorIsNamed() { + Module module = ExcelReader.class.getModule(); + assertNotNull(module, "module should be resolved for main classes"); + String name = module.getName(); + // If name is null, we are on the classpath (unnamed module) – likely an IDE run without module path. + assumeTrue(name != null, () -> "Running on classpath (unnamed module) – skipping strict module assertions. " + + "Configure your IDE test runtime to use the module path to enable this assertion."); + assertEquals("org.apache.fesod", name, "module name should match descriptor"); + ModuleDescriptor descriptor = module.getDescriptor(); + assertNotNull(descriptor, "descriptor must exist"); + assertTrue(descriptor.isOpen(), "module expected to remain open while reflective access is required"); + } +} diff --git a/pom.xml b/pom.xml index 172ff22da..f90fa66cd 100644 --- a/pom.xml +++ b/pom.xml @@ -528,18 +528,6 @@ - - jdk9plus - - [9,) - - - --add-opens=java.base/java.lang=ALL-UNNAMED - --add-opens=java.base/java.util=ALL-UNNAMED - --add-opens=java.base/sun.reflect.annotation=ALL-UNNAMED - - - jdk8 diff --git a/website/community/development.md b/website/community/development.md index c50f1238a..58976ad0c 100644 --- a/website/community/development.md +++ b/website/community/development.md @@ -66,3 +66,12 @@ FastExcel uses [Spotless](https://github.com/diffplug/spotless) as its code form ```bash mvn spotless:apply ``` + +## Maintaining the Java Module Descriptor + +Fesod ships an optional Java Platform Module System (JPMS) descriptor that is compiled when the build runs on JDK 9 or newer. When you add a new public package or rely on additional reflective access, remember to: + +- Update `fesod/src/main/java9/module-info.java` and export any new public packages that should be reachable by module consumers. +- Extend the Java 9+ Surefire arguments (`surefire.jdk9plus.args` property in `fesod/pom.xml`) if new reflective code requires `--add-opens` access to JDK or third-party modules. +- Run `ModuleDefinitionTest` to confirm the module stays open and reflective access continues to work. Maven will execute it automatically via `./mvnw test -pl fesod` on JDK 11+. +- Keep Java 8 compatibility: all new code must still compile with the Java 8 toolchain even after updating the module descriptor. diff --git a/website/i18n/zh-cn/docusaurus-plugin-content-docs-community/current/development.md b/website/i18n/zh-cn/docusaurus-plugin-content-docs-community/current/development.md index 3501697f8..1432e2db7 100644 --- a/website/i18n/zh-cn/docusaurus-plugin-content-docs-community/current/development.md +++ b/website/i18n/zh-cn/docusaurus-plugin-content-docs-community/current/development.md @@ -66,3 +66,12 @@ FastExcel 使用 [Spotless](https://github.com/diffplug/spotless) 检查并修 ```bash mvn spotless:apply ``` + +## 模块化维护注意事项 + +Fesod 在 JDK 9 及以上版本会额外编译 JPMS 模块描述文件。新增对外公开的包或依赖新的反射访问时,请同步完成以下调整: + +- 修改 `fesod/src/main/java9/module-info.java`,为需要暴露给使用方的包添加新的 `exports`。 +- 如果新增代码需要访问 JDK 或三方模块的内部类型,请在 `fesod/pom.xml` 的 `surefire.jdk9plus.args` 中补充相应的 `--add-opens`。 +- 运行或关注 `ModuleDefinitionTest`,确保模块保持 `open` 状态且反射能力未被破坏;在 JDK 11+ 环境执行 `./mvnw test -pl fesod` 时会自动覆盖该测试。 +- 继续保持 Java 8 兼容性:即使更新了模块描述符,新代码仍需通过 Java 8 编译器编译。