java.lang.ArrayStoreException 在 E2E 测试中由带有自定义验证组的自定义约束注释引起
java.lang.ArrayStoreException in E2E Test caused by custom Constraint Annotation with Custom Validation Group
我正在为我的 REST 应用程序编写 E2E 测试。因此,位于前端的测试调用已部署的后端,除了一个小问题外,一切正常:
当调用接收到的 Response
上的 readEntity
方法时,会发生 java.lang.ArrayStoreException
。我的观察表明问题出在自己编写的约束注释中:
我已经为 Bean-Validation 编写了自己的 Constraint-Annotation @Future
,我的 POJO 使用此 Annotation 来验证 LocalDateTime
是否在未来。我还提供了两个简单的接口,用作验证组:
public interface ExistingInstance extends Default {
}
和
public interface NewInstance extends Default {
}
当我在我的自定义约束注释中使用这些接口之一作为验证组时,如下所示:
public class Book {
@Future(groups=NewInstance.class)
private LocalDateTime validFrom;
private LocalDateTime validTo;
// Getters & Setters...
}
我收到这个异常:
java.lang.ArrayStoreException
at sun.reflect.annotation.AnnotationParser.parseClassArray(AnnotationParser.java:736)
at sun.reflect.annotation.AnnotationParser.parseArray(AnnotationParser.java:543)
at sun.reflect.annotation.AnnotationParser.parseMemberValue(AnnotationParser.java:367)
at sun.reflect.annotation.AnnotationParser.parseAnnotation2(AnnotationParser.java:298)
at sun.reflect.annotation.AnnotationParser.parseAnnotations2(AnnotationParser.java:132)
at sun.reflect.annotation.AnnotationParser.parseAnnotations(AnnotationParser.java:84)
at java.lang.reflect.AccessibleObject.getAnnotationsFromCache(AccessibleObject.java:313)
at java.lang.reflect.Field.declaredAnnotations(Field.java:1167)
at java.lang.reflect.Field.getDeclaredAnnotations(Field.java:1160)
at com.fasterxml.jackson.databind.introspect.AnnotatedFieldCollector._findFields(AnnotatedFieldCollector.java:86)
at com.fasterxml.jackson.databind.introspect.AnnotatedFieldCollector._findFields(AnnotatedFieldCollector.java:71)
at com.fasterxml.jackson.databind.introspect.AnnotatedFieldCollector.collect(AnnotatedFieldCollector.java:48)
at com.fasterxml.jackson.databind.introspect.AnnotatedFieldCollector.collectFields(AnnotatedFieldCollector.java:43)
at com.fasterxml.jackson.databind.introspect.AnnotatedClass._fields(AnnotatedClass.java:371)
at com.fasterxml.jackson.databind.introspect.AnnotatedClass.fields(AnnotatedClass.java:343)
at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector._addFields(POJOPropertiesCollector.java:493)
at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.collectAll(POJOPropertiesCollector.java:421)
at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.getPropertyMap(POJOPropertiesCollector.java:386)
at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.getProperties(POJOPropertiesCollector.java:233)
at com.fasterxml.jackson.databind.introspect.BasicBeanDescription._properties(BasicBeanDescription.java:164)
at com.fasterxml.jackson.databind.introspect.BasicBeanDescription.findProperties(BasicBeanDescription.java:239)
at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._findCreatorsFromProperties(BasicDeserializerFactory.java:328)
at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._constructDefaultValueInstantiator(BasicDeserializerFactory.java:272)
at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory.findValueInstantiator(BasicDeserializerFactory.java:223)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:261)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:150)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:414)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:349)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:264)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
at com.fasterxml.jackson.databind.DeserializationContext.findContextualValueDeserializer(DeserializationContext.java:558)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.createContextual(CollectionDeserializer.java:188)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.createContextual(CollectionDeserializer.java:28)
at com.fasterxml.jackson.databind.DeserializationContext.handlePrimaryContextualization(DeserializationContext.java:765)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.resolve(BeanDeserializerBase.java:535)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:293)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:591)
at com.fasterxml.jackson.databind.ObjectReader._prefetchRootDeserializer(ObjectReader.java:2340)
at com.fasterxml.jackson.databind.ObjectReader.forType(ObjectReader.java:723)
at com.fasterxml.jackson.jaxrs.base.ProviderBase.readFrom(ProviderBase.java:804)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.invokeReadFrom(ReaderInterceptorExecutor.java:233)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:212)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:132)
at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1072)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:885)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:845)
at org.glassfish.jersey.client.ClientResponse.readEntity(ClientResponse.java:340)
at org.glassfish.jersey.client.InboundJaxrsResponse.call(InboundJaxrsResponse.java:104)
at org.glassfish.jersey.internal.Errors.process(Errors.java:292)
at org.glassfish.jersey.internal.Errors.process(Errors.java:274)
at org.glassfish.jersey.internal.Errors.process(Errors.java:205)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:365)
at org.glassfish.jersey.client.InboundJaxrsResponse.runInScopeIfPossible(InboundJaxrsResponse.java:244)
at org.glassfish.jersey.client.InboundJaxrsResponse.readEntity(InboundJaxrsResponse.java:101)
at my.package.structure.books.BooksClient.readEntityFromResponse(BooksClient.java:309)
at my.package.structure.books.BooksClient.getBooks(BooksClient.java:95)
at my.package.structure.books.BooksClientTest.getBooks_Test(BooksClientTest.java:43)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:90)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:55)
at java.lang.reflect.Method.invoke(Method.java:508)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:436)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod(TestMethodTestDescriptor.java:170)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda1.00000000126A8D40.execute(Unknown Source)
at org.junit.jupiter.engine.execution.ThrowableCollector.execute(ThrowableCollector.java:40)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:166)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:113)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:58)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively(HierarchicalTestExecutor.java:112)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor$$Lambda3.000000001120E090.execute(Unknown Source)
at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.executeRecursively(HierarchicalTestExecutor.java:108)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.execute(HierarchicalTestExecutor.java:79)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively(HierarchicalTestExecutor.java:120)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor$$Lambda6.000000001148A600.accept(Unknown Source)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:195)
at java.util.stream.ReferencePipeline.accept(ReferencePipeline.java:186)
at java.util.Iterator.forEachRemaining(Iterator.java:127)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1812)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:523)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:513)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:162)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:185)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:245)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:429)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively(HierarchicalTestExecutor.java:120)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor$$Lambda3.000000001120E090.execute(Unknown Source)
at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.executeRecursively(HierarchicalTestExecutor.java:108)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.execute(HierarchicalTestExecutor.java:79)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively(HierarchicalTestExecutor.java:120)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor$$Lambda6.000000001148A600.accept(Unknown Source)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:195)
at java.util.stream.ReferencePipeline.accept(ReferencePipeline.java:186)
at java.util.Iterator.forEachRemaining(Iterator.java:127)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1812)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:523)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:513)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:162)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:185)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:245)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:429)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively(HierarchicalTestExecutor.java:120)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor$$Lambda3.000000001120E090.execute(Unknown Source)
at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.executeRecursively(HierarchicalTestExecutor.java:108)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.execute(HierarchicalTestExecutor.java:79)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:55)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:43)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:170)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:154)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:90)
at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)
现在是令人困惑的部分:
如果我在 Java 提供的现有注释上使用我的验证组接口,例如 @NotNull
:
public class Book {
@NotNull(groups=NewInstance.class)
private LocalDateTime validFrom;
private LocalDateTime validTo;
// Getters & Setters...
}
它运行得非常好。即使我使用 JDK 中提供的 class,例如 Default.class
作为我的自定义注释中的一个组:
public class Book {
@NotNull(groups=Default.class)
private LocalDateTime validFrom;
private LocalDateTime validTo;
// Getters & Setters...
}
有效!
我错过了什么?
我使用:
- Java v8
- JavaEE v7
- 泽西客户端 v2.33
- 球衣 HK2 v2.33
- Hibernate 验证器 v5.4.3
注意:我知道注解`@Future`已经提供了,但是在Java 8和JavaEE 7中它不支持'new'时间-API.
编辑
这是我重现错误的代码:
服务器端:
返回实体的包装器
public class Wrapper<T> {
private T returnValue;
private String exception;
private Integer processingState;
private String processingMessage;
//Getter & Setter
}
@Path("books")
public class BookService {
@EJB
private BookRepo repository;
@Path("all")
public Response getAllBooks() {
Response response = null;
try {
// findAll returns a List wrapped inside the above shown Wrapper class
response = Response.status(200).entity(new GenericEntity<Wrapper<List<Book>>>(repository.findAll()){}).build();
} catch (Exception e) {
response = Response.status(500).build();
}
return response;
}
}
客户端:
public class BooksClient {
private ObjectMapper mapper;
private Client client;
private WebTarget baseTarget;
public BooksClient(String endpointAdress) {
this.mapper = new ObjectMapper();
this.mapper.registerModule(new JavaTimeModule());
this.mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
JacksonJsonProvider provider = new JacksonJsonProvider(this.mapper);
this.client = ClientBuilder.newClient(new ClientConfig().register(provider));
this.baseTarget = this.client.target(endpointAdress);
}
public Wrapper<List<Book>> getBooks() {
WebTarget bookTarget = baseTarget.path("all");
Response response = bookTarget.request(MediaType.APPLICATION_JSON).get();
return this.readEntityFromResponse(response);
}
private Wrapper<List<Book>> readEntityFromResponse(Response response) {
Wrapper<List<Book>> result = null;
GenericType<List<Book>> responseType = new GenericType<Wrapper<List<Book>>>(){};
result = response.readEntity(responseType); // <--- root of Exception
return result;
}
}
测试Class
public class BooksClientTest {
@Spy
private BooksClient client = new BooksClient("http://localhost:1234/ws/books");
@Test
public void getBooks_Test() {
Wrapper<List<Book>> result = this.client.getBooks();
assertThat(result).isNotNull();
}
}
自定义注释:
/**
* Validates a Time Value.
*/
@Documented
@Retention(RUNTIME)
@Target(FIELD)
@Constraint(validatedBy = { FutureLocalDateTimeValidator.class, FutureLocalDateValidator.class,
FutureLocalTimeValidator.class, FutureZonedDateTimeValidator.class })
public @interface Future {
String message() default "Error while validating the date!";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
对应的验证人:
public class FutureLocalDateTimeValidator implements ConstraintValidator<Future, LocalDateTime> {
@Override
public void initialize(Future constraintAnnotation) {
// not needed.
}
@Override
public boolean isValid(LocalDateTime value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
LocalDateTime today = LocalDateTime.now();
return value.isAfter(today);
}
}
我尝试了不同的自写注释,结果完全相同。
应用程序部署在 WAS 9.0 上
如果我使用实际的前端,不会发生错误。
希望这对您有所帮助,在此先感谢!
我找不到确切的版本(行号不匹配),但 the code found on docjar.com 足以解释发生了什么。
private static Object parseClassArray(int length,
ByteBuffer buf,
ConstantPool constPool,
Class<?> container) {
Object[] result = new Class<?>[length];
boolean typeMismatch = false;
int tag = 0;
for (int i = 0; i < length; i++) {
tag = buf.get();
if (tag == 'c') {
result[i] = parseClassValue(buf, constPool, container);
} else {
skipMemberValue(tag, buf);
typeMismatch = true;
}
}
return typeMismatch ? exceptionProxy(tag) : result;
}
private static Object parseClassValue(ByteBuffer buf,
ConstantPool constPool,
Class<?> container) {
int classIndex = buf.getShort() & 0xFFFF;
try {
try {
String sig = constPool.getUTF8At(classIndex);
return parseSig(sig, container);
} catch (IllegalArgumentException ex) {
// support obsolete early jsr175 format class files
return constPool.getClassAt(classIndex);
}
} catch (NoClassDefFoundError e) {
return new TypeNotPresentExceptionProxy("[unknown]", e);
}
catch (TypeNotPresentException e) {
return new TypeNotPresentExceptionProxy(e.typeName(), e.getCause());
}
}
有趣的是方法parseClassValue
并不总是return一个Class<?>
;当 NoClassDefFoundError
或 TypeNotPresentException
发生时,它将 return 一个 TypeNotPresentExceptionProxy
实例代替。
由于 parseClassArray
不检查这个特殊的 return 值,而是无条件地将结果存储到 Class<?>[]
数组中,因此可能会出现 ArrayStoreException
。
这显然是一个错误,但它也提供了一个提示,说明为什么您并不总是遇到这个特定问题(显然,一般来说,开发人员并不多)。
仅当在具有 Class
类型数组的注释值中引用 不存在 类型时才会发生。因此,您可能会关注在故障环境中哪种类型不可用的问题。检查这一点的一种方法是在错误情况下强制提前终止,例如
public class Book {
// now, it will fail at class initialization time if the type is missing
static final Class<?> DUMMY = NewInstance.class;
@Future(groups=NewInstance.class)
private LocalDateTime validFrom;
private LocalDateTime validTo;
// Getters & Setters...
}
我发现了问题。我正在使用
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.3.Final</version>
</dependency>
和
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
根据此 answer,这些是不兼容的。
我将版本升级到:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
现在一切正常。感谢您的帮助!
我正在为我的 REST 应用程序编写 E2E 测试。因此,位于前端的测试调用已部署的后端,除了一个小问题外,一切正常:
当调用接收到的 Response
上的 readEntity
方法时,会发生 java.lang.ArrayStoreException
。我的观察表明问题出在自己编写的约束注释中:
我已经为 Bean-Validation 编写了自己的 Constraint-Annotation @Future
,我的 POJO 使用此 Annotation 来验证 LocalDateTime
是否在未来。我还提供了两个简单的接口,用作验证组:
public interface ExistingInstance extends Default {
}
和
public interface NewInstance extends Default {
}
当我在我的自定义约束注释中使用这些接口之一作为验证组时,如下所示:
public class Book {
@Future(groups=NewInstance.class)
private LocalDateTime validFrom;
private LocalDateTime validTo;
// Getters & Setters...
}
我收到这个异常:
java.lang.ArrayStoreException
at sun.reflect.annotation.AnnotationParser.parseClassArray(AnnotationParser.java:736)
at sun.reflect.annotation.AnnotationParser.parseArray(AnnotationParser.java:543)
at sun.reflect.annotation.AnnotationParser.parseMemberValue(AnnotationParser.java:367)
at sun.reflect.annotation.AnnotationParser.parseAnnotation2(AnnotationParser.java:298)
at sun.reflect.annotation.AnnotationParser.parseAnnotations2(AnnotationParser.java:132)
at sun.reflect.annotation.AnnotationParser.parseAnnotations(AnnotationParser.java:84)
at java.lang.reflect.AccessibleObject.getAnnotationsFromCache(AccessibleObject.java:313)
at java.lang.reflect.Field.declaredAnnotations(Field.java:1167)
at java.lang.reflect.Field.getDeclaredAnnotations(Field.java:1160)
at com.fasterxml.jackson.databind.introspect.AnnotatedFieldCollector._findFields(AnnotatedFieldCollector.java:86)
at com.fasterxml.jackson.databind.introspect.AnnotatedFieldCollector._findFields(AnnotatedFieldCollector.java:71)
at com.fasterxml.jackson.databind.introspect.AnnotatedFieldCollector.collect(AnnotatedFieldCollector.java:48)
at com.fasterxml.jackson.databind.introspect.AnnotatedFieldCollector.collectFields(AnnotatedFieldCollector.java:43)
at com.fasterxml.jackson.databind.introspect.AnnotatedClass._fields(AnnotatedClass.java:371)
at com.fasterxml.jackson.databind.introspect.AnnotatedClass.fields(AnnotatedClass.java:343)
at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector._addFields(POJOPropertiesCollector.java:493)
at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.collectAll(POJOPropertiesCollector.java:421)
at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.getPropertyMap(POJOPropertiesCollector.java:386)
at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.getProperties(POJOPropertiesCollector.java:233)
at com.fasterxml.jackson.databind.introspect.BasicBeanDescription._properties(BasicBeanDescription.java:164)
at com.fasterxml.jackson.databind.introspect.BasicBeanDescription.findProperties(BasicBeanDescription.java:239)
at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._findCreatorsFromProperties(BasicDeserializerFactory.java:328)
at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._constructDefaultValueInstantiator(BasicDeserializerFactory.java:272)
at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory.findValueInstantiator(BasicDeserializerFactory.java:223)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:261)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:150)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:414)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:349)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:264)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
at com.fasterxml.jackson.databind.DeserializationContext.findContextualValueDeserializer(DeserializationContext.java:558)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.createContextual(CollectionDeserializer.java:188)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.createContextual(CollectionDeserializer.java:28)
at com.fasterxml.jackson.databind.DeserializationContext.handlePrimaryContextualization(DeserializationContext.java:765)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.resolve(BeanDeserializerBase.java:535)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:293)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:591)
at com.fasterxml.jackson.databind.ObjectReader._prefetchRootDeserializer(ObjectReader.java:2340)
at com.fasterxml.jackson.databind.ObjectReader.forType(ObjectReader.java:723)
at com.fasterxml.jackson.jaxrs.base.ProviderBase.readFrom(ProviderBase.java:804)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.invokeReadFrom(ReaderInterceptorExecutor.java:233)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:212)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:132)
at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1072)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:885)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:845)
at org.glassfish.jersey.client.ClientResponse.readEntity(ClientResponse.java:340)
at org.glassfish.jersey.client.InboundJaxrsResponse.call(InboundJaxrsResponse.java:104)
at org.glassfish.jersey.internal.Errors.process(Errors.java:292)
at org.glassfish.jersey.internal.Errors.process(Errors.java:274)
at org.glassfish.jersey.internal.Errors.process(Errors.java:205)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:365)
at org.glassfish.jersey.client.InboundJaxrsResponse.runInScopeIfPossible(InboundJaxrsResponse.java:244)
at org.glassfish.jersey.client.InboundJaxrsResponse.readEntity(InboundJaxrsResponse.java:101)
at my.package.structure.books.BooksClient.readEntityFromResponse(BooksClient.java:309)
at my.package.structure.books.BooksClient.getBooks(BooksClient.java:95)
at my.package.structure.books.BooksClientTest.getBooks_Test(BooksClientTest.java:43)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:90)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:55)
at java.lang.reflect.Method.invoke(Method.java:508)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:436)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod(TestMethodTestDescriptor.java:170)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda1.00000000126A8D40.execute(Unknown Source)
at org.junit.jupiter.engine.execution.ThrowableCollector.execute(ThrowableCollector.java:40)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:166)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:113)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:58)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively(HierarchicalTestExecutor.java:112)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor$$Lambda3.000000001120E090.execute(Unknown Source)
at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.executeRecursively(HierarchicalTestExecutor.java:108)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.execute(HierarchicalTestExecutor.java:79)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively(HierarchicalTestExecutor.java:120)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor$$Lambda6.000000001148A600.accept(Unknown Source)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:195)
at java.util.stream.ReferencePipeline.accept(ReferencePipeline.java:186)
at java.util.Iterator.forEachRemaining(Iterator.java:127)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1812)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:523)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:513)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:162)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:185)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:245)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:429)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively(HierarchicalTestExecutor.java:120)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor$$Lambda3.000000001120E090.execute(Unknown Source)
at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.executeRecursively(HierarchicalTestExecutor.java:108)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.execute(HierarchicalTestExecutor.java:79)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively(HierarchicalTestExecutor.java:120)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor$$Lambda6.000000001148A600.accept(Unknown Source)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:195)
at java.util.stream.ReferencePipeline.accept(ReferencePipeline.java:186)
at java.util.Iterator.forEachRemaining(Iterator.java:127)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1812)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:523)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:513)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:162)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:185)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:245)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:429)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively(HierarchicalTestExecutor.java:120)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor$$Lambda3.000000001120E090.execute(Unknown Source)
at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.executeRecursively(HierarchicalTestExecutor.java:108)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.execute(HierarchicalTestExecutor.java:79)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:55)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:43)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:170)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:154)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:90)
at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)
现在是令人困惑的部分:
如果我在 Java 提供的现有注释上使用我的验证组接口,例如 @NotNull
:
public class Book {
@NotNull(groups=NewInstance.class)
private LocalDateTime validFrom;
private LocalDateTime validTo;
// Getters & Setters...
}
它运行得非常好。即使我使用 JDK 中提供的 class,例如 Default.class
作为我的自定义注释中的一个组:
public class Book {
@NotNull(groups=Default.class)
private LocalDateTime validFrom;
private LocalDateTime validTo;
// Getters & Setters...
}
有效!
我错过了什么?
我使用:
- Java v8
- JavaEE v7
- 泽西客户端 v2.33
- 球衣 HK2 v2.33
- Hibernate 验证器 v5.4.3
注意:我知道注解`@Future`已经提供了,但是在Java 8和JavaEE 7中它不支持'new'时间-API.
编辑 这是我重现错误的代码:
服务器端: 返回实体的包装器
public class Wrapper<T> {
private T returnValue;
private String exception;
private Integer processingState;
private String processingMessage;
//Getter & Setter
}
@Path("books")
public class BookService {
@EJB
private BookRepo repository;
@Path("all")
public Response getAllBooks() {
Response response = null;
try {
// findAll returns a List wrapped inside the above shown Wrapper class
response = Response.status(200).entity(new GenericEntity<Wrapper<List<Book>>>(repository.findAll()){}).build();
} catch (Exception e) {
response = Response.status(500).build();
}
return response;
}
}
客户端:
public class BooksClient {
private ObjectMapper mapper;
private Client client;
private WebTarget baseTarget;
public BooksClient(String endpointAdress) {
this.mapper = new ObjectMapper();
this.mapper.registerModule(new JavaTimeModule());
this.mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
JacksonJsonProvider provider = new JacksonJsonProvider(this.mapper);
this.client = ClientBuilder.newClient(new ClientConfig().register(provider));
this.baseTarget = this.client.target(endpointAdress);
}
public Wrapper<List<Book>> getBooks() {
WebTarget bookTarget = baseTarget.path("all");
Response response = bookTarget.request(MediaType.APPLICATION_JSON).get();
return this.readEntityFromResponse(response);
}
private Wrapper<List<Book>> readEntityFromResponse(Response response) {
Wrapper<List<Book>> result = null;
GenericType<List<Book>> responseType = new GenericType<Wrapper<List<Book>>>(){};
result = response.readEntity(responseType); // <--- root of Exception
return result;
}
}
测试Class
public class BooksClientTest {
@Spy
private BooksClient client = new BooksClient("http://localhost:1234/ws/books");
@Test
public void getBooks_Test() {
Wrapper<List<Book>> result = this.client.getBooks();
assertThat(result).isNotNull();
}
}
自定义注释:
/**
* Validates a Time Value.
*/
@Documented
@Retention(RUNTIME)
@Target(FIELD)
@Constraint(validatedBy = { FutureLocalDateTimeValidator.class, FutureLocalDateValidator.class,
FutureLocalTimeValidator.class, FutureZonedDateTimeValidator.class })
public @interface Future {
String message() default "Error while validating the date!";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
对应的验证人:
public class FutureLocalDateTimeValidator implements ConstraintValidator<Future, LocalDateTime> {
@Override
public void initialize(Future constraintAnnotation) {
// not needed.
}
@Override
public boolean isValid(LocalDateTime value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
LocalDateTime today = LocalDateTime.now();
return value.isAfter(today);
}
}
我尝试了不同的自写注释,结果完全相同。
应用程序部署在 WAS 9.0 上
如果我使用实际的前端,不会发生错误。
希望这对您有所帮助,在此先感谢!
我找不到确切的版本(行号不匹配),但 the code found on docjar.com 足以解释发生了什么。
private static Object parseClassArray(int length,
ByteBuffer buf,
ConstantPool constPool,
Class<?> container) {
Object[] result = new Class<?>[length];
boolean typeMismatch = false;
int tag = 0;
for (int i = 0; i < length; i++) {
tag = buf.get();
if (tag == 'c') {
result[i] = parseClassValue(buf, constPool, container);
} else {
skipMemberValue(tag, buf);
typeMismatch = true;
}
}
return typeMismatch ? exceptionProxy(tag) : result;
}
private static Object parseClassValue(ByteBuffer buf,
ConstantPool constPool,
Class<?> container) {
int classIndex = buf.getShort() & 0xFFFF;
try {
try {
String sig = constPool.getUTF8At(classIndex);
return parseSig(sig, container);
} catch (IllegalArgumentException ex) {
// support obsolete early jsr175 format class files
return constPool.getClassAt(classIndex);
}
} catch (NoClassDefFoundError e) {
return new TypeNotPresentExceptionProxy("[unknown]", e);
}
catch (TypeNotPresentException e) {
return new TypeNotPresentExceptionProxy(e.typeName(), e.getCause());
}
}
有趣的是方法parseClassValue
并不总是return一个Class<?>
;当 NoClassDefFoundError
或 TypeNotPresentException
发生时,它将 return 一个 TypeNotPresentExceptionProxy
实例代替。
由于 parseClassArray
不检查这个特殊的 return 值,而是无条件地将结果存储到 Class<?>[]
数组中,因此可能会出现 ArrayStoreException
。
这显然是一个错误,但它也提供了一个提示,说明为什么您并不总是遇到这个特定问题(显然,一般来说,开发人员并不多)。
仅当在具有 Class
类型数组的注释值中引用 不存在 类型时才会发生。因此,您可能会关注在故障环境中哪种类型不可用的问题。检查这一点的一种方法是在错误情况下强制提前终止,例如
public class Book {
// now, it will fail at class initialization time if the type is missing
static final Class<?> DUMMY = NewInstance.class;
@Future(groups=NewInstance.class)
private LocalDateTime validFrom;
private LocalDateTime validTo;
// Getters & Setters...
}
我发现了问题。我正在使用
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.3.Final</version>
</dependency>
和
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
根据此 answer,这些是不兼容的。
我将版本升级到:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
现在一切正常。感谢您的帮助!