Jon Whitefield
Jon Whitefield

Reputation: 211

Jax-ws exception serialization failure

I'm facing an issues which I cannot find a solution too (or I'm tackling the wrong way). When certain exceptions are generated from my service it appears the serialization is failing. This results in getting a wrapped exception which is a pain as calling clients receive a poor error and I imagine I'm loosing useful info from the original exception.

In the bellow example its an S3 bucket access issue, but I have had similar problems with some SQL exceptions.

It would be useful if I could keep my services for intervening directly. The ideal would be to add some custom exception serialization code.

> com.amazonaws.services.simpleworkflow.flow.DataConverterException:
> Failure serializing
> "com.sun.xml.internal.ws.fault.ServerSOAPFaultException: Client
> received SOAP Fault from server: Access Denied (Service: Amazon S3;
> Status Code: 403; Error Code: AccessDenied; Request ID: AAAAAAAAAAAA)
> Please see the server log to find more detail regarding exact cause of
> the failure." of type "class
> com.sun.xml.internal.ws.fault.ServerSOAPFaultException" when mapping
> key "null" at
> com.amazonaws.services.simpleworkflow.flow.JsonDataConverter.throwDataConverterException(JsonDataConverter.java:90)
> at
> com.amazonaws.services.simpleworkflow.flow.JsonDataConverter.toData(JsonDataConverter.java:78)
> at
> com.amazonaws.services.simpleworkflow.flow.pojo.POJOActivityImplementation.throwActivityFailureException(POJOActivityImplementation.java:102)
> at
> com.amazonaws.services.simpleworkflow.flow.pojo.POJOActivityImplementation.execute(POJOActivityImplementation.java:67)
> at
> com.amazonaws.services.simpleworkflow.flow.generic.ActivityImplementationBase.execute(ActivityImplementationBase.java:46)
> at
> com.amazonaws.services.simpleworkflow.flow.worker.SynchronousActivityTaskPoller.execute(SynchronousActivityTaskPoller.java:196)
> at
> com.amazonaws.services.simpleworkflow.flow.worker.ActivityTaskPoller$2.run(ActivityTaskPoller.java:92)
> at
> java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
> at
> java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
> at java.lang.Thread.run(Thread.java:748) Caused by:
> com.fasterxml.jackson.databind.JsonMappingException: Type id handling
> not implemented for type org.w3c.dom.Node (by serializer of type
> com.fasterxml.jackson.databind.ext.DOMSerializer) (through reference
> chain:
> com.sun.xml.internal.ws.fault.ServerSOAPFaultException["fault"]) at
> com.fasterxml.jackson.databind.SerializerProvider.mappingException(SerializerProvider.java:1084)
> at
> com.fasterxml.jackson.databind.JsonSerializer.serializeWithType(JsonSerializer.java:159)
> at
> com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:695)
> at
> com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:675)
> at
> com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeWithType(BeanSerializerBase.java:566)
> at
> com.fasterxml.jackson.databind.ser.impl.TypeWrappedSerializer.serialize(TypeWrappedSerializer.java:32)
> at
> com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:130)
> at
> com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:3559)
> at
> com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:2927)
> at
> com.amazonaws.services.simpleworkflow.flow.JsonDataConverter.toData(JsonDataConverter.java:72)
> ... 8 more

Thanks for any suggestions

Upvotes: 3

Views: 1193

Answers (1)

gpeche
gpeche

Reputation: 22504

Jackson is trying to serialize some instance of com.sun.xml.internal.ws.fault.ServerSOAPFaultException. This exception is a subclass of javax.xml.ws.soap.SOAPFaultException, which happens to have a getter for retrieving the SOAPFault. As a SOAPFault is a DOM Node, Jackson decides to use a DOMSerializer and also encode type information so that at deserialization time it can find out which concrete type fault was an instance of. The problem is that DOMSerializer does not support this type information thing, so you get com.fasterxml.jackson.databind.JsonMappingException.

I think the best way to work around this issue would be to register a custom serializer/deserializer pair for SOAPFault. You could then serialize to Strings and deserialize creating new instances via one of the SOAPFactory.createFault(...) methods. Don't forget to also implement JsonSerializer.serializeWithType(...) so that Jackson can properly do type handling.

A (very rough) draft would be

public class SOAPFaultSerializer extends StdSerializer<SOAPFault> {

    public SOAPFaultSerializer() {
        this(null);
    }

    public SOAPFaultSerializer(Class<SOAPFault> t) {
        super(t);
    }

    @Override
    public void serialize(SOAPFault fault, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {

        jgen.writeStartObject();
        // serialize "interesting" SOAPFault information
        jgen.writeStringField("faultActor", fault.getFaultActor());
        jgen.writeStringField("faultCode", fault.getFaultCode());
        ...
        jgen.writeEndObject();
    }

    @Override
    public void serializeWithType(JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) throws IOException, JsonGenerationException {
        typeSer.writeTypePrefixForObject(this, jgen, SOAPFault.class);
        serialize(value, jgen, provider);
        typeSer.writeTypeSuffixForObject(this, jgen);
    }
}

public class SOAPFaultDeserializer extends StdDeserializer<SOAPFault> { 

    public SOAPFaultDeserializer() { 
        this(null); 
    } 

    public SOAPFaultDeserializer(Class<?> vc) { 
         super(vc); 
    }

    @Override
    public SOAPFault deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
         JsonNode node = jp.getCodec().readTree(jp);
         // deserialize "interesting" SOAPFault information
         String faultActor = node.get("faultActor").asText();
         String faultCode = node.get("faultCode").asText();
         ...
         SOAPFactory factory = SOAPFactory.newInstance();
         SOAPFault fault = factory.createFault();
         // fill in SOAPFault with deserialized fields
         fault.setFaultActor(faultActor);
         fault.setFaultCode(faultCode);
         ...
         return fault;
    }
}

Upvotes: 1

Related Questions