We've noticed in our that SOAP web-services seem to run faster when run as
spring-boot:run
as opposed to packaging up the JAR as we do for deployment and running
java -jar mySpringApp.jar
The speed up is in the order of 2-3x so obviously we would like this for our live environment.
To validate that this wasn't something in our app I've tried this with the sample app from the spring guide
git source https://github.com/spring-guides/gs-soap-service.git
Testing this with JMeter shows the same sort of speed up. I've tested this on both Windows 7 Java 1.8.0_31 and Ubuntu 14.04 platforms with 1.8.0_45-b14.
This only seems to be the case for soap services, simple html doesn't show any significant difference in performance. Any idea what could be causing this?
I tested this and it appears that the application spends a significant amount of time in the following two code paths:
at org.springframework.boot.loader.LaunchedURLClassLoader$1.hasMoreElements(LaunchedURLClassLoader.java:110)
at sun.misc.CompoundEnumeration.next(CompoundEnumeration.java:45)
at sun.misc.CompoundEnumeration.hasMoreElements(CompoundEnumeration.java:54)
at java.util.ServiceLoader$LazyIterator.hasNextService(ServiceLoader.java:354)
at java.util.ServiceLoader$LazyIterator.hasNext(ServiceLoader.java:393)
at java.util.ServiceLoader$1.hasNext(ServiceLoader.java:474)
at javax.xml.transform.FactoryFinder$1.run(FactoryFinder.java:327)
at java.security.AccessController.doPrivileged(Native Method)
at javax.xml.transform.FactoryFinder.findServiceProvider(FactoryFinder.java:323)
at javax.xml.transform.FactoryFinder.find(FactoryFinder.java:299)
at javax.xml.transform.TransformerFactory.newInstance(TransformerFactory.java:106)
at com.sun.xml.internal.messaging.saaj.util.transform.EfficientStreamingTransformer.<init>(EfficientStreamingTransformer.java:68)
at com.sun.xml.internal.messaging.saaj.util.transform.EfficientStreamingTransformer.newTransformer(EfficientStreamingTransformer.java:420)
at com.sun.xml.internal.messaging.saaj.soap.EnvelopeFactory.createEnvelope(EnvelopeFactory.java:106)
at com.sun.xml.internal.messaging.saaj.soap.ver1_1.SOAPPart1_1Impl.createEnvelopeFromSource(SOAPPart1_1Impl.java:69)
at com.sun.xml.internal.messaging.saaj.soap.SOAPPartImpl.getEnvelope(SOAPPartImpl.java:128)
at org.springframework.ws.soap.saaj.SaajSoapMessageFactory.createWebServiceMessage(SaajSoapMessageFactory.java:189)
at org.springframework.ws.soap.saaj.SaajSoapMessageFactory.createWebServiceMessage(SaajSoapMessageFactory.java:60)
at org.springframework.ws.transport.AbstractWebServiceConnection.receive(AbstractWebServiceConnection.java:92)
at org.springframework.ws.transport.support.WebServiceMessageReceiverObjectSupport.handleConnection(WebServiceMessageReceiverObjectSupport.java:87)
at org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter.handle(WebServiceMessageReceiverHandlerAdapter.java:61)
at org.springframework.ws.transport.http.MessageDispatcherServlet.doService(MessageDispatcherServlet.java:293)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:869)
and
at org.springframework.boot.loader.LaunchedURLClassLoader$1.hasMoreElements(LaunchedURLClassLoader.java:110)
at sun.misc.CompoundEnumeration.next(CompoundEnumeration.java:45)
at sun.misc.CompoundEnumeration.hasMoreElements(CompoundEnumeration.java:54)
at java.util.ServiceLoader$LazyIterator.hasNextService(ServiceLoader.java:354)
at java.util.ServiceLoader$LazyIterator.hasNext(ServiceLoader.java:393)
at java.util.ServiceLoader$1.hasNext(ServiceLoader.java:474)
at javax.xml.parsers.FactoryFinder$1.run(FactoryFinder.java:293)
at java.security.AccessController.doPrivileged(Native Method)
at javax.xml.parsers.FactoryFinder.findServiceProvider(FactoryFinder.java:289)
at javax.xml.parsers.FactoryFinder.find(FactoryFinder.java:267)
at javax.xml.parsers.DocumentBuilderFactory.newInstance(DocumentBuilderFactory.java:120)
at com.sun.org.apache.xalan.internal.xsltc.trax.SAX2DOM.<init>(SAX2DOM.java:74)
at com.sun.org.apache.xalan.internal.xsltc.runtime.output.TransletOutputHandlerFactory.getSerializationHandler(TransletOutputHandlerFactory.java:199)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.getOutputHandler(TransformerImpl.java:436)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:342)
at com.sun.xml.internal.messaging.saaj.util.transform.EfficientStreamingTransformer.transform(EfficientStreamingTransformer.java:399)
at com.sun.xml.internal.messaging.saaj.soap.EnvelopeFactory.createEnvelope(EnvelopeFactory.java:108)
at com.sun.xml.internal.messaging.saaj.soap.ver1_1.SOAPPart1_1Impl.createEnvelopeFromSource(SOAPPart1_1Impl.java:69)
at com.sun.xml.internal.messaging.saaj.soap.SOAPPartImpl.getEnvelope(SOAPPartImpl.java:128)
at org.springframework.ws.soap.saaj.SaajSoapMessageFactory.createWebServiceMessage(SaajSoapMessageFactory.java:189)
at org.springframework.ws.soap.saaj.SaajSoapMessageFactory.createWebServiceMessage(SaajSoapMessageFactory.java:60)
at org.springframework.ws.transport.AbstractWebServiceConnection.receive(AbstractWebServiceConnection.java:92)
at org.springframework.ws.transport.support.WebServiceMessageReceiverObjectSupport.handleConnection(WebServiceMessageReceiverObjectSupport.java:87)
at org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter.handle(WebServiceMessageReceiverHandlerAdapter.java:61)
at org.springframework.ws.transport.http.MessageDispatcherServlet.doService(MessageDispatcherServlet.java:293)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:869)
The interesting lines are
at javax.xml.transform.TransformerFactory.newInstance(TransformerFactory.java:106)
and
at javax.xml.parsers.DocumentBuilderFactory.newInstance(DocumentBuilderFactory.java:120)
What this means is that for every request, the SAAJ implementation in the JRE requests a new TransformerFactory
and a new DocumentBuilderFactory
. These factories are located using the JDK 1.3 service provider discovery mechanism, which involves searching for certain resources under META-INF/services
. The performance of that search is very sensitive to the characteristics of the class loaders from which these resources are looked up. That is why you see a difference between mvn spring-boot:run
and using an executable JAR. In particular, the executable JAR contains embedded JARs and looking up resources from these embedded JARs is expensive. For mvn spring-boot:run
this is not the case, which explains why it is faster.
Since this is ultimately a problem with the SAAJ implementation, one solution is to use Apache Axiom instead. To do this with the sample application from the spring guide, simply add the following code to WebServiceConfig
:
@Bean
public SoapMessageFactory messageFactory() {
return new AxiomSoapMessageFactory();
}
You also need to add the following dependency:
<dependency>
<groupId>org.apache.ws.commons.axiom</groupId>
<artifactId>axiom-impl</artifactId>
<version>1.2.15</version>
</dependency>