2

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?

Punter Vicky
  • 15,954
  • 56
  • 188
  • 315
  • Did you see the javadoc of class [`JAXB`](https://docs.oracle.com/javase/8/docs/api/javax/xml/bind/JAXB.html)? It says: "*Generally speaking, the performance is not necessarily optimal. It is expected that people who need to write performance critical code will use the rest of the JAXB API directly.*" – Thomas Fritsch Sep 04 '17 at 02:41
  • Thanks , but it usually responds within 100 milliseconds . I see this issue only when I try to load test. Does this by any chance synchronized which us causing this delay ? When I opened the class , I saw the method is static but not synchronized – Punter Vicky Sep 04 '17 at 02:45
  • Where does `JAXB.marshal` come from? Is that a third-party API? – kolossus Sep 04 '17 at 02:47
  • 1
    Creating the `JAXBContext` is usually the most time-consuming part. The rest of marshalling/unmarshalling is not that slow. – Thomas Fritsch Sep 04 '17 at 02:49
  • 1
    Since the most time-consuming action is the `JAXBContext` creation, did you try to change the code by creating the `JAXBContext` after the interceptor creation (in a `@PostConstruct` method) and then trying to marshalling/unmarshalling the SOAP message? Maybe in your perfomance test when there are several threads trying to create the context they generate a bottleneck – Angelo Immediata Sep 04 '17 at 07:45
  • Thank you , jaxb marshall is in 3rd party API. I see that the thread is blocked in this particular statement when I checked via appd. I'll try to look at JAXB context as well. appreciate all your help. – Punter Vicky Sep 04 '17 at 08:31
  • 1
    You need only one `JAXBContext` - @PunterVicky. It's threadsafe, and as its been said - very expensive to instantiate. – kolossus Sep 04 '17 at 10:52

1 Answers1

3

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!

Per Huss
  • 4,755
  • 12
  • 29
  • I'd think it is not allowed to have `throws JAXBException` on a `@PostConstruct` method. See [this](https://stackoverflow.com/questions/23153742/im-gettting-warning-from-postconstruct-annotated-init-method) and [this](https://stackoverflow.com/questions/8740234/postconstruct-checked-exceptions) question. So you'd need to remove the `throws` clause and instead add some `try`/`catch` logic in your method. – Thomas Fritsch Sep 08 '17 at 12:13
  • @ThomasFritsch Yes, you're right. I'll update the example! – Per Huss Sep 08 '17 at 12:20