在 JBoss Wildfly 中使用 EJB 和 JAR 进行 EAR 部署 - 如何从 EJB 项目中加载文件夹中的所有文件或 JAR 中的资源?
EAR deployment with EJB and JAR in JBoss Wildfly - how to load all files in folder or resources in JAR from within EJB project?
我想从两个不同的部署中加载和处理 json 模式文件,第一个是带有 JAX-RS 端点的 WAR 和第二个 EAR 和 Singleton-EJB + 包含模式文件的资源 JAR(我读过只有在将资源文件捆绑在 EAR 中的单独 JAR 中时,才能打包用于 EJB 的资源文件。
开发环境是eclipse 2019-03 with JBoss Wildfly 16.
WAR 使用 JAX-RS 端点部署
WAR 部分没问题,我有一个 @ApplicationScoped
Bean,可以通过 ServletContext 访问位于 src/main/webapp/schemas/
的模式文件,请参见以下代码片段:
@ForWarDeployment
@ApplicationScoped
public class JsonSchemaValidatorWar extends JsonSchemaValidatorBase {
...
@PostConstruct
public void init() {
Consumer<Path> readSchema = schemaFile -> {
String schemaName = schemaFile.getName(schemaFile.getNameCount() - 1).toString();
JsonSchema js = jvs.readSchema(schemaFile);
map.put(schemaName, js); // this is a concurrent hash map in base class
log.info("Schema " + schemaName + " added: " + js.toJson());
};
URI schemaFolder;
try {
schemaFolder = servletContext.getResource("/schemas").toURI();
try (Stream<Path> paths = Files.walk(Paths.get(schemaFolder))) {
paths.filter(Files::isRegularFile).forEach(readSchema);
}
} catch (URISyntaxException | IOException e) {
throw new RuntimeException("Error loading schema files!", e);
}
}
第一个请求的输出:
... (default task-1) Schema person.schema.json added: {"$id": ...
使用 EJB 和资源 JAR 的 EAR 部署
EJB 部分很棘手,我还没有找到读取所有模式文件的解决方案。
我目前拥有的是一个多模块的maven项目,结构如下:
- parent
- | ear
- | ejb3
- | resources
pom.xml 父项目
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>mdv</groupId>
<artifactId>json-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>json-ejb3</module>
<module>json-ear</module>
<module>json-resources</module>
</modules>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
</project>
pom.xml 耳朵项目
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>mdv</groupId>
<artifactId>json-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>json-ear</artifactId>
<packaging>ear</packaging>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>mdv</groupId>
<artifactId>json-ejb3</artifactId>
<version>${project.version}</version>
<type>ejb</type>
</dependency>
<dependency>
<groupId>mdv</groupId>
<artifactId>json-resources</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ear-plugin</artifactId>
<version>3.0.1</version>
<configuration>
<version>7</version>
<defaultLibBundleDir>lib</defaultLibBundleDir>
<earSourceDirectory>${basedir}/src/main/resources</earSourceDirectory>
<outputFileNameMapping>@{artifactId}@@{dashClassifier?}@.@{extension}@</outputFileNameMapping>
</configuration>
</plugin>
</plugins>
</build>
</project>
pom.xml 资源项目
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>mdv</groupId>
<artifactId>json-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>json-resources</artifactId>
</project>
pom.xml 用于 ejb3 项目
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>mdv</groupId>
<artifactId>json-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>json-ejb3</artifactId>
<packaging>ejb</packaging>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<artifactId>maven-ejb-plugin</artifactId>
<version>3.0.1</version>
<configuration>
<ejbVersion>3.2</ejbVersion>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<!-- contains a json schema processing library and the class JsonSchemaValidatorEjb -->
<groupId>mdv</groupId>
<artifactId>json</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
在 EJB 中加载模式文件的问题
我想在一个 @ApplicationScoped
bean 中加载模式文件以用于 Singleton EJB,相应的 class 是 JsonSchemaValidatorService
:
package mdv;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.inject.Inject;
import json.ForEjbDeployment;
import json.IJsonSchemaValidator;
@Singleton
@Startup
public class JsonSchemaValidatorService {
Logger log = Logger.getLogger("JsonSchemaValidatorService");
@Inject
@ForEjbDeployment
IJsonSchemaValidator jsonSchemaValidator;
// this is where json schema files should be loaded
public JsonSchemaValidatorService() {
//
}
@PostConstruct
public void init() {
log.info("Started JsonSchemaValidatorService.");
log.info("Loaded schemas in jsonSchemaValidator: " + jsonSchemaValidator.getLoadedSchemas());
}
}
在 EJB 环境中加载 json 模式文件的 class 是这个 bean:
package json;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.function.Consumer;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.resource.spi.IllegalStateException;
import org.leadpony.justify.api.JsonSchema;
@ForEjbDeployment
@ApplicationScoped
public class JsonSchemaValidatorEjb extends JsonSchemaValidatorBase {
Logger log = Logger.getLogger("JsonSchemaValidator");
public JsonSchemaValidatorEjb() {
//
}
@PostConstruct
public void init() {
try {
// This is where I can't manage to get a list of the json schema files and process them
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
try(
final InputStream is = loader.getResourceAsStream("schemas");
final InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
final BufferedReader br = new BufferedReader(isr)) {
log.info("schema files in directory: ");
br.lines().forEach(x -> log.info(x));
}
} catch (Exception e) {
throw new RuntimeException("Error trying to parse schema files!", e);
}
}
}
没有抛出异常,但在提供的目录中也找不到任何文件,例如"schemas"。 EJB启动后缩短的输出为:
[JsonSchemaValidatorService] Started JsonSchemaValidatorService.
[JsonSchemaValidator] schema files in directory:
[JsonSchemaValidatorService] Loaded schemas in jsonSchemaValidator: {}
部署的ear的文件结构是这样的:
- lib
| - icu4j.jar
| - javax.json-api.jar
| - javax.json.jar
| - json-resources.jar // jar with resources, in this case the schemas
| | - schemas
| | | - person.schema.json
| - json.jar // jar containing @ApplicationScoped beans for war und ejb
| - justify.jar // json schema processing library used
- META-INF
| - maven
| | ...
| - schemas
| | - person.schema.json
| - application.xml
| - MANIFEST.MF
- schemas
| -person.schema.json
- json-ejb3.jar
如您所见,我已成功将 schemas
文件夹和单个 json 架构文件捆绑在多个位置,但 none 有效。
这有可能实现吗?
我在 getResourceAsStream("schemas")
指定的路径有误吗?
目标是在启动时加载所有现有的 json 模式文件,将它们解析为 JsonSchema 对象一次,以便稍后验证它们(顺便说一下,它将是一个消息驱动的 bean)。
How to list the files inside a JAR file? 中回答了迭代模式的方法。
鉴于此,没有理由不能将这些资源与您的 EJB 放在同一个 jar 中。如果您需要从 EAR 中的其他 jar 或 WAR 访问它们,它们只需要位于 EAR/lib 目录中的单独 jar 中。
你不需要关心 jboss-deployment-structure.xml
除非你正在做一些时髦的事情 - 但你不是。
此外,您无法从 EAR 本身内部读取资源,例如您的 ear/META-INF/schemas
。这算作时髦,你仍然需要上面指出的解决方案来迭代。
最后我找到了一个很好的解决方案,它可以在 Servlet 和 EJB 上下文中工作,并且无需区分它们。
由于我无法从 EJB 中列出 schemas 文件夹中的文件,而是访问和读取单个文件,我想出了使用 auto-generated 的想法 – 在构建时 – 包含所有 JSON 模式文件的列表并使用它来处理模式
将 EJB 移动到 WAR 部署
首先,我听从了@IllyaKysil 的建议,将我的 EJB 从 EAR 部署移动到已经存在且正在运行的 WAR 部署
将模式文件移入 JAR
原始方法在 WAR 和 EAR 部署中都有 JSON 模式文件。我现在在 JAR 项目的 src/main/resources/schemas
文件夹中有文件,我在 WAR 项目中有 Maven 依赖项。存档的结果结构是:
| jee7-test.war
| - WEB-INF
| | - lib
| | | - json-validator-0.0.1-SNAPSHOT.jar
| | | | - schemas
| | | | | - person.schema.json
| | | | | - schemaList.txt
在构建时生成 schemaList.txt
使用 maven antrun 插件在 src/main/resources/schemas
中创建一个文件,每个文件在 schemas 目录中在单独的行上扩展名为 .schema.json
:
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<phase>generate-sources</phase>
<configuration>
<target>
<fileset id="schemaFiles"
dir="src/main/resources/schemas/" includes="*.schema.json" />
<pathconvert pathsep="${line.separator}"
property="schemaFileList" refid="schemaFiles">
<map from="${basedir}\src\main\resources\schemas\" to="" />
</pathconvert>
<echo
file="${basedir}\src\main\resources\schemas\schemaList.txt">${schemaFileList}</echo>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
生成的文件内容为:
person.schema.json
阅读schemaList.txt和解析模式
最后一步是读取包含 JSON 个模式文件列表的文件并处理每一行以解析相应的模式文件:
@ApplicationScoped
public class JsonSchemaValidator implements IJsonSchemaValidator {
protected JsonValidationService jvs = JsonValidationService.newInstance();
protected ConcurrentHashMap<String, JsonSchema> schemaMap = new ConcurrentHashMap<String, JsonSchema>();
private Logger log = Logger.getLogger("JsonSchemaValidator");
public JsonSchemaValidator() {
//
}
private String SCHEMA_FOLDER = "schemas/";
private String SCHEMA_LIST_FILE = "schemaList.txt";
@PostConstruct
public void init() {
try {
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
// load file containing list of JSON schema files
try (final InputStream is = loader.getResourceAsStream(SCHEMA_FOLDER + SCHEMA_LIST_FILE);
final InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
final BufferedReader br = new BufferedReader(isr)) {
// each line is a name of a JSON schema file that has to be processed
br.lines().forEach(line -> readSchema(line, loader));
}
log.info("Number of JsonSchema objects in schemaMap: " + schemaMap.size());
log.info("Keys in schemaMap: ");
schemaMap.forEachKey(1L, key -> log.info(key));
} catch (Exception e) {
throw new RuntimeException("Error trying to parse schema files!", e);
}
}
private void readSchema(String schemaFileName, ClassLoader classLoader) {
// only use part of the file name to first dot, which leaves me with "person"
// for "person.schema.json" file name
String schemaName = schemaFileName.substring(0, schemaFileName.indexOf("."));
JsonSchema js = jvs.readSchema(classLoader.getResourceAsStream(SCHEMA_FOLDER + schemaFileName));
// put JsonSchema object in map with schema name as key
schemaMap.put(schemaName, js);
log.info("Schema " + schemaName + " added: " + js.toJson());
}
@Override
public List<Problem> validate(String json, String schemaName) {
List<Problem> result = new ArrayList<Problem>();
JsonSchema jsonSchema = schemaMap.get(schemaName);
JsonReader reader = jvs.createReader(new StringReader(json), jsonSchema, ProblemHandler.collectingTo(result));
reader.read();
return result;
}
@Override
public Map<String, JsonSchema> getLoadedSchemas() {
return Collections.unmodifiableMap(schemaMap);
}
}
结果
JSON 来自输入的字符串现在可以根据 JSON 模式进行验证,而无需一遍又一遍地解析模式
@Inject
IJsonSchemaValidator jsv;
...
List<Problem> problems = jsv.validate(inputJson, "person");
创建 JsonSchemaValidator 实例后的日志输出:
Schema person added: {"$id":"....}
Number of JsonSchema objects in schemaMap: 1
Keys in schemaMap:
person
我想从两个不同的部署中加载和处理 json 模式文件,第一个是带有 JAX-RS 端点的 WAR 和第二个 EAR 和 Singleton-EJB + 包含模式文件的资源 JAR(我读过只有在将资源文件捆绑在 EAR 中的单独 JAR 中时,才能打包用于 EJB 的资源文件。
开发环境是eclipse 2019-03 with JBoss Wildfly 16.
WAR 使用 JAX-RS 端点部署
WAR 部分没问题,我有一个 @ApplicationScoped
Bean,可以通过 ServletContext 访问位于 src/main/webapp/schemas/
的模式文件,请参见以下代码片段:
@ForWarDeployment
@ApplicationScoped
public class JsonSchemaValidatorWar extends JsonSchemaValidatorBase {
...
@PostConstruct
public void init() {
Consumer<Path> readSchema = schemaFile -> {
String schemaName = schemaFile.getName(schemaFile.getNameCount() - 1).toString();
JsonSchema js = jvs.readSchema(schemaFile);
map.put(schemaName, js); // this is a concurrent hash map in base class
log.info("Schema " + schemaName + " added: " + js.toJson());
};
URI schemaFolder;
try {
schemaFolder = servletContext.getResource("/schemas").toURI();
try (Stream<Path> paths = Files.walk(Paths.get(schemaFolder))) {
paths.filter(Files::isRegularFile).forEach(readSchema);
}
} catch (URISyntaxException | IOException e) {
throw new RuntimeException("Error loading schema files!", e);
}
}
第一个请求的输出:
... (default task-1) Schema person.schema.json added: {"$id": ...
使用 EJB 和资源 JAR 的 EAR 部署
EJB 部分很棘手,我还没有找到读取所有模式文件的解决方案。
我目前拥有的是一个多模块的maven项目,结构如下:
- parent
- | ear
- | ejb3
- | resources
pom.xml 父项目
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>mdv</groupId>
<artifactId>json-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>json-ejb3</module>
<module>json-ear</module>
<module>json-resources</module>
</modules>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
</project>
pom.xml 耳朵项目
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>mdv</groupId>
<artifactId>json-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>json-ear</artifactId>
<packaging>ear</packaging>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>mdv</groupId>
<artifactId>json-ejb3</artifactId>
<version>${project.version}</version>
<type>ejb</type>
</dependency>
<dependency>
<groupId>mdv</groupId>
<artifactId>json-resources</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ear-plugin</artifactId>
<version>3.0.1</version>
<configuration>
<version>7</version>
<defaultLibBundleDir>lib</defaultLibBundleDir>
<earSourceDirectory>${basedir}/src/main/resources</earSourceDirectory>
<outputFileNameMapping>@{artifactId}@@{dashClassifier?}@.@{extension}@</outputFileNameMapping>
</configuration>
</plugin>
</plugins>
</build>
</project>
pom.xml 资源项目
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>mdv</groupId>
<artifactId>json-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>json-resources</artifactId>
</project>
pom.xml 用于 ejb3 项目
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>mdv</groupId>
<artifactId>json-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>json-ejb3</artifactId>
<packaging>ejb</packaging>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<artifactId>maven-ejb-plugin</artifactId>
<version>3.0.1</version>
<configuration>
<ejbVersion>3.2</ejbVersion>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<!-- contains a json schema processing library and the class JsonSchemaValidatorEjb -->
<groupId>mdv</groupId>
<artifactId>json</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
在 EJB 中加载模式文件的问题
我想在一个 @ApplicationScoped
bean 中加载模式文件以用于 Singleton EJB,相应的 class 是 JsonSchemaValidatorService
:
package mdv;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.inject.Inject;
import json.ForEjbDeployment;
import json.IJsonSchemaValidator;
@Singleton
@Startup
public class JsonSchemaValidatorService {
Logger log = Logger.getLogger("JsonSchemaValidatorService");
@Inject
@ForEjbDeployment
IJsonSchemaValidator jsonSchemaValidator;
// this is where json schema files should be loaded
public JsonSchemaValidatorService() {
//
}
@PostConstruct
public void init() {
log.info("Started JsonSchemaValidatorService.");
log.info("Loaded schemas in jsonSchemaValidator: " + jsonSchemaValidator.getLoadedSchemas());
}
}
在 EJB 环境中加载 json 模式文件的 class 是这个 bean:
package json;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.function.Consumer;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.resource.spi.IllegalStateException;
import org.leadpony.justify.api.JsonSchema;
@ForEjbDeployment
@ApplicationScoped
public class JsonSchemaValidatorEjb extends JsonSchemaValidatorBase {
Logger log = Logger.getLogger("JsonSchemaValidator");
public JsonSchemaValidatorEjb() {
//
}
@PostConstruct
public void init() {
try {
// This is where I can't manage to get a list of the json schema files and process them
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
try(
final InputStream is = loader.getResourceAsStream("schemas");
final InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
final BufferedReader br = new BufferedReader(isr)) {
log.info("schema files in directory: ");
br.lines().forEach(x -> log.info(x));
}
} catch (Exception e) {
throw new RuntimeException("Error trying to parse schema files!", e);
}
}
}
没有抛出异常,但在提供的目录中也找不到任何文件,例如"schemas"。 EJB启动后缩短的输出为:
[JsonSchemaValidatorService] Started JsonSchemaValidatorService.
[JsonSchemaValidator] schema files in directory:
[JsonSchemaValidatorService] Loaded schemas in jsonSchemaValidator: {}
部署的ear的文件结构是这样的:
- lib
| - icu4j.jar
| - javax.json-api.jar
| - javax.json.jar
| - json-resources.jar // jar with resources, in this case the schemas
| | - schemas
| | | - person.schema.json
| - json.jar // jar containing @ApplicationScoped beans for war und ejb
| - justify.jar // json schema processing library used
- META-INF
| - maven
| | ...
| - schemas
| | - person.schema.json
| - application.xml
| - MANIFEST.MF
- schemas
| -person.schema.json
- json-ejb3.jar
如您所见,我已成功将 schemas
文件夹和单个 json 架构文件捆绑在多个位置,但 none 有效。
这有可能实现吗?
我在 getResourceAsStream("schemas")
指定的路径有误吗?
目标是在启动时加载所有现有的 json 模式文件,将它们解析为 JsonSchema 对象一次,以便稍后验证它们(顺便说一下,它将是一个消息驱动的 bean)。
How to list the files inside a JAR file? 中回答了迭代模式的方法。
鉴于此,没有理由不能将这些资源与您的 EJB 放在同一个 jar 中。如果您需要从 EAR 中的其他 jar 或 WAR 访问它们,它们只需要位于 EAR/lib 目录中的单独 jar 中。
你不需要关心 jboss-deployment-structure.xml
除非你正在做一些时髦的事情 - 但你不是。
此外,您无法从 EAR 本身内部读取资源,例如您的 ear/META-INF/schemas
。这算作时髦,你仍然需要上面指出的解决方案来迭代。
最后我找到了一个很好的解决方案,它可以在 Servlet 和 EJB 上下文中工作,并且无需区分它们。
由于我无法从 EJB 中列出 schemas 文件夹中的文件,而是访问和读取单个文件,我想出了使用 auto-generated 的想法 – 在构建时 – 包含所有 JSON 模式文件的列表并使用它来处理模式
将 EJB 移动到 WAR 部署
首先,我听从了@IllyaKysil 的建议,将我的 EJB 从 EAR 部署移动到已经存在且正在运行的 WAR 部署
将模式文件移入 JAR
原始方法在 WAR 和 EAR 部署中都有 JSON 模式文件。我现在在 JAR 项目的 src/main/resources/schemas
文件夹中有文件,我在 WAR 项目中有 Maven 依赖项。存档的结果结构是:
| jee7-test.war
| - WEB-INF
| | - lib
| | | - json-validator-0.0.1-SNAPSHOT.jar
| | | | - schemas
| | | | | - person.schema.json
| | | | | - schemaList.txt
在构建时生成 schemaList.txt
使用 maven antrun 插件在 src/main/resources/schemas
中创建一个文件,每个文件在 schemas 目录中在单独的行上扩展名为 .schema.json
:
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<phase>generate-sources</phase>
<configuration>
<target>
<fileset id="schemaFiles"
dir="src/main/resources/schemas/" includes="*.schema.json" />
<pathconvert pathsep="${line.separator}"
property="schemaFileList" refid="schemaFiles">
<map from="${basedir}\src\main\resources\schemas\" to="" />
</pathconvert>
<echo
file="${basedir}\src\main\resources\schemas\schemaList.txt">${schemaFileList}</echo>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
生成的文件内容为:
person.schema.json
阅读schemaList.txt和解析模式
最后一步是读取包含 JSON 个模式文件列表的文件并处理每一行以解析相应的模式文件:
@ApplicationScoped
public class JsonSchemaValidator implements IJsonSchemaValidator {
protected JsonValidationService jvs = JsonValidationService.newInstance();
protected ConcurrentHashMap<String, JsonSchema> schemaMap = new ConcurrentHashMap<String, JsonSchema>();
private Logger log = Logger.getLogger("JsonSchemaValidator");
public JsonSchemaValidator() {
//
}
private String SCHEMA_FOLDER = "schemas/";
private String SCHEMA_LIST_FILE = "schemaList.txt";
@PostConstruct
public void init() {
try {
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
// load file containing list of JSON schema files
try (final InputStream is = loader.getResourceAsStream(SCHEMA_FOLDER + SCHEMA_LIST_FILE);
final InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
final BufferedReader br = new BufferedReader(isr)) {
// each line is a name of a JSON schema file that has to be processed
br.lines().forEach(line -> readSchema(line, loader));
}
log.info("Number of JsonSchema objects in schemaMap: " + schemaMap.size());
log.info("Keys in schemaMap: ");
schemaMap.forEachKey(1L, key -> log.info(key));
} catch (Exception e) {
throw new RuntimeException("Error trying to parse schema files!", e);
}
}
private void readSchema(String schemaFileName, ClassLoader classLoader) {
// only use part of the file name to first dot, which leaves me with "person"
// for "person.schema.json" file name
String schemaName = schemaFileName.substring(0, schemaFileName.indexOf("."));
JsonSchema js = jvs.readSchema(classLoader.getResourceAsStream(SCHEMA_FOLDER + schemaFileName));
// put JsonSchema object in map with schema name as key
schemaMap.put(schemaName, js);
log.info("Schema " + schemaName + " added: " + js.toJson());
}
@Override
public List<Problem> validate(String json, String schemaName) {
List<Problem> result = new ArrayList<Problem>();
JsonSchema jsonSchema = schemaMap.get(schemaName);
JsonReader reader = jvs.createReader(new StringReader(json), jsonSchema, ProblemHandler.collectingTo(result));
reader.read();
return result;
}
@Override
public Map<String, JsonSchema> getLoadedSchemas() {
return Collections.unmodifiableMap(schemaMap);
}
}
结果
JSON 来自输入的字符串现在可以根据 JSON 模式进行验证,而无需一遍又一遍地解析模式
@Inject
IJsonSchemaValidator jsv;
...
List<Problem> problems = jsv.validate(inputJson, "person");
创建 JsonSchemaValidator 实例后的日志输出:
Schema person added: {"$id":"....}
Number of JsonSchema objects in schemaMap: 1
Keys in schemaMap:
person