如何在WAR中遍历EAR内所有EJB模块读取menu.js文件?
Hey there! Great question—this is a common scenario when building modular EAR applications where you want to aggregate configuration from multiple modules without tight coupling. Here's a step-by-step solution that leverages Java's classloader mechanism to dynamically find and read all menu.js files across your EJB modules:
First, ensure every EJB module places its menu.js file in a consistent, classloader-accessible location. The best practice is to put it in the META-INF directory of each EJB JAR. This way, we can reliably look it up using the classloader later.
Your EJB module project structure should look like this:
ejb-module-1/ └── src/ └── main/ └── resources/ └── META-INF/ └── menu.js
In your WAR's common class (e.g., a utility bean or startup servlet), use the context classloader to enumerate all instances of META-INF/menu.js across the entire EAR. This works because in Jakarta EE, the EAR's classloader is the parent of both WAR and EJB module classloaders, so the WAR can access resources from all EJB modules.
Here's a practical code example using Jakarta JSON Processing (JSON-P) for parsing:
import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Enumeration; import jakarta.json.Json; import jakarta.json.JsonArray; import jakarta.json.JsonReader; public class MenuAggregator { public JsonArray aggregateAllMenus() throws IOException { // Use the context classloader to access all EAR module resources ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); Enumeration<URL> menuResources = classLoader.getResources("META-INF/menu.js"); JsonArray.Builder combinedMenu = Json.createArrayBuilder(); while (menuResources.hasMoreElements()) { URL menuUrl = menuResources.nextElement(); // Safely read and parse each module's menu.js try (InputStream inputStream = menuUrl.openStream(); JsonReader jsonReader = Json.createReader(inputStream)) { JsonArray moduleMenu = jsonReader.readArray(); // Add all entries from this module to the combined menu moduleMenu.forEach(combinedMenu::add); } catch (Exception e) { // Gracefully handle errors (log and skip problematic modules) System.err.println("Failed to process menu from module: " + menuUrl); e.printStackTrace(); } } return combinedMenu.build(); } }
- Classloader Selection: Using
Thread.currentThread().getContextClassLoader()is safer thangetClass().getClassLoader()in Jakarta EE environments—it ensures you're using the classloader that can see all modules in the EAR. - Path Consistency: Stick to the same resource path (like
META-INF/menu.js) across all EJB modules. If you need to change the path, update the string ingetResources()accordingly. - JSON Parsing Alternatives: If you prefer Jackson over JSON-P, replace the parsing logic with
ObjectMapper.readValue(inputStream, JsonNode.class)to read the array. - Startup Execution: To aggregate menus once at app startup, call this method from a
@StartupEJB or aServletContextListenerin the WAR.
- Deploy your EAR with multiple EJB modules, each containing
META-INF/menu.js. - Invoke the
aggregateAllMenus()method from your WAR (e.g., via a REST endpoint or servlet). - The returned JSON array will contain all menu entries from every EJB module in the EAR.
内容的提问来源于stack exchange,提问作者Daniele Licitra

