How to set custom ValidationEventHandler on JAXB unmarshaller when using annotations
Asked Answered
M

1

6

We’re using JAX-WS in combination with JAXB to receive and parse XML web service calls. It’s all annotation-based, i.e. we never get hold of the JAXBContext in our code. I need to set a custom ValidationEventHandler on the unmarshaller, so that if the date format for a particular field is not accepted, we can catch the error and report something nice back in the response. We have a XMLJavaTypeAdapter on the field in question, which does the parsing and throws an exception. I can’t see how to set a ValidationEventHandler onto the unmarshaller using the annotation-based configuration that we have. Any ideas?

Note: same question as this comment which is currently unanswered.

Michael answered 20/5, 2014 at 4:52 Comment(5)
@BlaiseDoughan I was hoping I could get your opinion on whether what we're attempting is even possible. Any thoughts?Michael
Anyone? It seems that these "holes" in the annotation support for some features (in combination with the normally-a-good-thing aspect of annotations which take the bootstrapping details away from you) mean that you can't really use annotations if you need those features. I must be looking at this wrong, I'm sure.Michael
Did you manage to get any info about this issue? Struggling with same problem here.Magnetostriction
Unfortunately not. I literally scoured the internet for days on this issue. @BlaiseDoughan seems to be the guru in this area but unfortunately I haven't been able to get a response. In the end, we're going with the partial (read: crappy) solution where the XMLJavaTypeAdapter does do the parsing and throws exceptions where it needs to, and these just get translated into SOAP faults in the response. The response text DOES say "blah was not a valid date" or whatever, BUT it doesn't indicate which field (problem if you have multiple same data type), and it includes ugly stack trace.Michael
Ok, thanks a lot. Will add answer here if I will be more lucky.Magnetostriction
I
2

I have been struggling with this issue during the last week and finally i have managed a working solution. The trick is that JAXB looks for the methods beforeUnmarshal and afterUnmarshal in the object annotated with @XmlRootElement.

..
@XmlRootElement(name="MSEPObtenerPolizaFechaDTO")
@XmlAccessorType(XmlAccessType.FIELD)

public class MSEPObtenerPolizaFechaDTO implements Serializable {
..

public void beforeUnmarshal(Unmarshaller unmarshaller, Object parent) throws JAXBException, IOException, SAXException {
        unmarshaller.setSchema(Utils.getSchemaFromContext(this.getClass()));
        unmarshaller.setEventHandler(new CustomEventHandler());
  }

  public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) throws JAXBException {
        unmarshaller.setSchema(null);
        unmarshaller.setEventHandler(null);
  }

Using this ValidationEventHandler:

public class CustomEventHandler implements ValidationEventHandler{

      @Override
      public boolean handleEvent(ValidationEvent event) {
            if (event.getSeverity() == event.ERROR ||
                        event.getSeverity() == event.FATAL_ERROR)
            {
                  ValidationEventLocator locator = event.getLocator();
                  throw new RuntimeException(event.getMessage(), event.getLinkedException());
            }
            return true;
      }
}

}

And this is the method getSchemaFromContext created in your Utility class:

  @SuppressWarnings("unchecked")
  public static Schema getSchemaFromContext(Class clazz) throws JAXBException, IOException, SAXException{
        JAXBContext jc = JAXBContext.newInstance(clazz);
        final List<ByteArrayOutputStream> outs = new ArrayList<ByteArrayOutputStream>();
        jc.generateSchema(new SchemaOutputResolver(){
              @Override
              public Result createOutput(String namespaceUri,
                         String suggestedFileName) throws IOException {
              ByteArrayOutputStream out = new ByteArrayOutputStream();
              outs.add(out);
              StreamResult streamResult = new StreamResult(out);
              streamResult.setSystemId("");
              return streamResult;
              }
        });
        StreamSource[] sources = new StreamSource[outs.size()];
        for (int i = 0; i < outs.size(); i++) {
              ByteArrayOutputStream out = outs.get(i);
              sources[i] = new StreamSource(new ByteArrayInputStream(out.toByteArray()), "");
        }
        SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        return sf.newSchema(sources);
  }
Indignant answered 3/6, 2015 at 10:43 Comment(1)
Your solution works great ! I get the error message and I can modify it also, If possible can you please help me that how can I get the field because of which error has occurred ? I want to append the field name in the error messageIllumination

© 2022 - 2024 — McMap. All rights reserved.