Java Glassfish 4.1中Object.afterUnmarshal的捕获异常

Java Glassfish 4.1中Object.afterUnmarshal的捕获异常,java,jaxb,glassfish,glassfish-4.1,Java,Jaxb,Glassfish,Glassfish 4.1,我目前正在JDK1.8.0-40上运行Glassfish 4.1。我使用的是javaee-web-api-7.0和jersey-media-moxy-2.22。我正在将JSON和XML从JAXB注释的java对象编组/解编组到JAXB注释的java对象 我设置了一个ContextResolver来提供一个带有自定义ValidationEventHandler的Unmarshaller,它将从属性设置器收集异常,并抛出一个带有聚合验证错误的BadRequestException。这部分工作正常 但

我目前正在JDK1.8.0-40上运行Glassfish 4.1。我使用的是javaee-web-api-7.0和jersey-media-moxy-2.22。我正在将JSON和XML从JAXB注释的java对象编组/解编组到JAXB注释的java对象

我设置了一个
ContextResolver
来提供一个带有自定义
ValidationEventHandler
Unmarshaller
,它将从属性设置器收集异常,并抛出一个带有聚合验证错误的
BadRequestException
。这部分工作正常

但是,对于检查未设置属性的对象,我还有
beforemashal
afterUnmarshal
方法(必须设置属性的规则因属性值而异,这导致我排除了对架构的验证)。如果从
afterUnmarshal
方法引发异常,则
ValidationEventHandler
不会看到该异常,而是冒泡到
exceptionapper

是否有任何方法可以捕获单个对象上的
beforemashal
afterUnmarshal
方法中的异常,并将它们发送到
ValidationEventHandler

我认为可以实现一个
MessageBodyReader
来捕获异常,使用
Unmarshaller.getEventHandler
,手动调用
ValidationEventHandler.handleEvent
,如果
handleEvent
返回false,则抛出一个
BadRequestException
[编辑:如果从
解组器.unmarshal中引发异常,则无法继续解组,因此唯一可能的方法是终止处理]。但这会丢失事件位置信息,我并不特别喜欢实现我自己的
MessageBodyReader
。我希望有一种更简单的内置方法来实现这一点,但我还没有发现


提前感谢您的帮助。

经过一番挖掘和头痛之后,我终于找到了一个解决方案

步骤1(可选) 编辑:您不必通过修补Jersey来实现此行为。我在任何地方都找不到它的文档,但是
org.glassfish.Jersey.internal.inject.Custom
注释将提供者标记为Custom(而仅
@provider
是不够的)。您还必须通过将
CommonProperties.MOXy\u JSON\u FEATURE\u disable
设置为true来禁用MOXy,如果您的提供程序用于
application/JSON
。因此,您只需执行以下操作:

@Custom
@Provider
public class MyCustomMessageBodyReader...
[...]
这是我解决方案中最不喜欢的部分,但也避免了大量代码重复。Jersey用于选择
MessageBodyReader/Writer
的排序算法无法对应用程序提供商进行优先级排序(我可以找到)。我想扩展
AbstractRootElementJaxbProvider
以重用其功能,但这意味着我无法使其比提供的Jersey更具体
XmlRootElementJaxbProvider
。因为默认情况下Jersey仅根据媒体类型距离、对象类型距离以及提供者是否注册为自定义提供者进行排序(通过
@Provider
注释检测到的提供程序未注册为自定义提供程序),将始终选择Jersey实现,而不是my
MessageBodyReader/Writer

我从Github签出了Jersey 2.10.4源代码,并修补了
MessageBodyFactory
,以利用
@Priority
注释作为
MessageBodyReader/Writer
选择算法的一部分。

步骤3 最后,我扩展了
org.glassfish.jersey.message.internal.AbstractRootElementJaxbProvider
以覆盖
readFrom
方法

@Override
protected Object readFrom(
        Class<Object> type,
        MediaType mediaType,
        Unmarshaller u,
        InputStream entityStream)
        throws JAXBException
{
    final SAXSource source = getSAXSource(spf.provide(), entityStream);
    ValidationEventCollector eventCollector = new ValidationEventCollector();
    ValidatingUnmarshallerListener listener = new ValidatingUnmarshallerListener(eventCollector);
    u.setEventHandler(eventCollector);
    u.setListener(listener);

    final Object result;
    if (type.isAnnotationPresent(XmlRootElement.class))
    {
        result = u.unmarshal(source);
    }
    else
    {
        result = u.unmarshal(source, type).getValue();
    }

    if (eventCollector.hasEvents())
    {
        HttpError error = new HttpError(Response.Status.BAD_REQUEST);

        for (ValidationEvent event : eventCollector.getEvents())
        {
            error.addMessage(ValidationUtil.toString(event));
        }

        throw new WebApplicationException(error.toResponse());
    }

    return result;
}
@覆盖
从中读取受保护对象(
类类型,
MediaType MediaType,
破译,
输入流(实体流)
抛出异常
{
最终SAXSource=getSAXSource(spf.provide(),entityStream);
ValidationEventCollector eventCollector=新的ValidationEventCollector();
ValidatingUnmarshallerListener侦听器=新的ValidatingUnmarshallerListener(eventCollector);
u、 setEventHandler(eventCollector);
u、 setListener(listener);
最终目标结果;
if(type.isAnnotationPresent(XmlRootElement.class))
{
结果=u.unmarshal(来源);
}
其他的
{
结果=u.unmarshal(源,类型).getValue();
}
if(eventCollector.hasEvents())
{
HttpError error=新的HttpError(Response.Status.BAD_请求);
对于(ValidationEvent事件:eventCollector.getEvents())
{
error.addMessage(ValidationUtil.toString(事件));
}
抛出新的WebApplicationException(error.toResponse());
}
返回结果;
}

经过一番挖掘和头痛之后,我终于找到了一个解决方案

步骤1(可选) 编辑:您不必通过修补Jersey来实现此行为。我在任何地方都找不到它的文档,但是
org.glassfish.Jersey.internal.inject.Custom
注释将提供者标记为Custom(而仅
@provider
是不够的)。您还必须通过将
CommonProperties.MOXy\u JSON\u FEATURE\u disable
设置为true来禁用MOXy,如果您的提供程序用于
application/JSON
。因此,您只需执行以下操作:

@Custom
@Provider
public class MyCustomMessageBodyReader...
[...]
这是我解决方案中最不喜欢的部分,但也避免了大量代码重复。Jersey用于选择
MessageBodyReader/Writer
的排序算法无法对应用程序提供商进行优先级排序(我可以找到)。我想扩展
AbstractRootElementJaxbProvider
以重用其功能,但这意味着我无法使其比提供的Jersey更具体
XmlRootElementJaxbProvider
。因为默认情况下Jersey仅根据媒体类型距离、对象类型距离以及提供者是否注册为自定义提供者进行排序(提供
@Override
protected Object readFrom(
        Class<Object> type,
        MediaType mediaType,
        Unmarshaller u,
        InputStream entityStream)
        throws JAXBException
{
    final SAXSource source = getSAXSource(spf.provide(), entityStream);
    ValidationEventCollector eventCollector = new ValidationEventCollector();
    ValidatingUnmarshallerListener listener = new ValidatingUnmarshallerListener(eventCollector);
    u.setEventHandler(eventCollector);
    u.setListener(listener);

    final Object result;
    if (type.isAnnotationPresent(XmlRootElement.class))
    {
        result = u.unmarshal(source);
    }
    else
    {
        result = u.unmarshal(source, type).getValue();
    }

    if (eventCollector.hasEvents())
    {
        HttpError error = new HttpError(Response.Status.BAD_REQUEST);

        for (ValidationEvent event : eventCollector.getEvents())
        {
            error.addMessage(ValidationUtil.toString(event));
        }

        throw new WebApplicationException(error.toResponse());
    }

    return result;
}