Search code examples
javaspringspring-bootjaxbwebservicetemplate

JAXB.marshal blocking webservice call


I am using Spring WebserviceTemplate to make SOAP call to a service. I ran performance test to see how it behaves under load. I also have a interceptor to copy the header parameters from my incoming request over to the service I am calling.

@Component
public class HeaderPropagationInterceptor implements ClientInterceptor {

    @Override
    public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
        SoapMessage request = (SoapMessage) messageContext.getRequest();
        Result result = request.getSoapHeader().getResult();
        JAXB.marshal(getRequestHeader(), result);
        return true;
    }

When I ran the performance test , I see the below statement blocking for 4-5 seconds

JAXB.marshal(getRequestHeader(), result);

Is there a reason why this might be blocking?


Solution

  • The JAXB utility class will create the JAXBContext the first time it is called (expensive operation). It will be weakly cached, which means if memory runs low the context may be recycled, and recreated on the following call. You really should create your context and keep it explicitly. Something like this (as already suggested in the comments by others) should solve your problem:

    @Component
    public class HeaderPropagationInterceptor implements ClientInterceptor
    {
        private JAXBContext jaxbContext;
    
        @PostConstruct
        public void createJaxbContext() {
            try {
                jaxbContext = JAXBContext.newInstance(RequestHeader.class);
            }
            catch(JAXBException e) {
                throw new IllegalStateException("Unable to create JAXBContext.", e);
            }
        }
    
        @Override
        public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
            SoapMessage request = (SoapMessage) messageContext.getRequest();
            Result result = request.getSoapHeader().getResult();
            jaxbContext.createMarshaller().marshal(getRequestHeader(), result);
            return true;
        }
    }
    

    You have to replace the RequestHeader.class with the actual class used by your code. If performance needs to be improved further, it's also possible to use a thread-local for reusing the marshaller, but you should probably do further profiling to verify that is really a bottleneck. Good luck with your project!