我们知道使用SpringBoot可以帮助我们快速建立一个原型;笔者当前使用的SpringBoot的版本是1.4.3.RELESE. 当前使用的ODataJClient的版本是0.9.0. 如果SpringBoot和包含ODataJClient的单元测试分开跑,SpringBoot和ODataJClient都能通过Maven或者Gradle编译,也都能透过测试。 但是如果把SpringBoot和ODataJClient集成起来,就会出现下面的问题。报错的原因如下:
Caused by: java.util.ServiceConfigurationError: javax.xml.stream.XMLEventFactory: Provider com.fasterxml.aalto.stax.EventFactoryImpl not a subtype
at java.util.ServiceLoader.fail(ServiceLoader.java:239)
at java.util.ServiceLoader.access$300(ServiceLoader.java:185)
at java.util.ServiceLoader$LazyIterator.nextService(ServiceLoader.java:376)
at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:404)
at java.util.ServiceLoader$1.next(ServiceLoader.java:480)
at javax.xml.stream.FactoryFinder$1.run(FactoryFinder.java:353)
at java.security.AccessController.doPrivileged(Native Method)
at javax.xml.stream.FactoryFinder.findServiceProvider(FactoryFinder.java:341)
at javax.xml.stream.FactoryFinder.find(FactoryFinder.java:313)
at javax.xml.stream.FactoryFinder.find(FactoryFinder.java:227)
at javax.xml.stream.XMLEventFactory.newInstance(XMLEventFactory.java:64)
at org.hibernate.boot.jaxb.internal.MappingBinder.
at org.hibernate.boot.spi.XmlMappingBinderAccess.
at org.hibernate.boot.MetadataSources.
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:54)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:353)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:373)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:362)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1648)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1585)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:554)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1081)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:856)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:761)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:371)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:111)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83)
at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:189)
at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:131)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:228)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:247)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:283)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeWithRerun(JUnit4Provider.java:173)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:153)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:128)
at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:203)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:155)
通过下面的分析,可知
mvn dependency:tree -Dverbose
原来缺少jackson-dataformat-xml的依赖(其实不缺少,只不过是odatajclient-engine也依赖jackson-dataformat-xml(2.2.3),其版本比SpringBoot默认的版本低)
所以odatajclient-engine的覆盖了SpringBoot的jackson-dataformat-xml(2.8.5)的版本,从而报错。所以解决方案是显式的在SpringBoot的项目中加入下面的依赖。
注意,其必须在加在odatajclient-engine的Maven或者Gradle依赖前面。这样SpringBoot抛出的上面异常就会消除。但是,还有坑。
这个时候SpringBoot的问题解决了,但是odatajclient调用oData V3(比如DSC 5.x的服务)的单元测试就会失败,
调用示范代码:
import java.io.InputStream;
import java.util.List;
import org.junit.Test;
import com.msopentech.odatajclient.engine.communication.request.retrieve.ODataEntitySetRequest;
import com.msopentech.odatajclient.engine.communication.request.retrieve.ODataRetrieveRequestFactory;
import com.msopentech.odatajclient.engine.communication.response.ODataRetrieveResponse;
import com.msopentech.odatajclient.engine.data.ODataEntity;
import com.msopentech.odatajclient.engine.data.ODataEntitySet;
import com.msopentech.odatajclient.engine.format.ODataPubFormat;
import com.msopentech.odatajclient.engine.uri.ODataURIBuilder;
public class ODataTest {
String testODataServiceRootURL="http://dsc-pull-server:8080/PSDSCPullServer.svc/";
@Test
public void testName() {
ODataURIBuilder uriBuilder = new ODataURIBuilder(testODataServiceRootURL).appendEntityTypeSegment("Nodes(AgentId='19940477-E202-11E6-80B8-BE117D36B7A4')/Reports");
ODataEntitySetRequest req = ODataRetrieveRequestFactory.getEntitySetRequest(uriBuilder.build());
req.setFormat(ODataPubFormat.JSON);
req.getResponseTemplate();
ODataRetrieveResponse res = req.execute();
InputStream input=res.getRawResponse();
@SuppressWarnings("rawtypes")
// JsonSerializer jsonSerializer= new StdSerializer();
// System.out.println( res.getStatusCode());
ODataEntitySet entitySet = res.getBody();
List lsODataEntity=entitySet.getEntities();
for(ODataEntity odataEntity:lsODataEntity){
odataEntity.getProperties();
}
System.out.println(entitySet.getCount());
}
}
结果如下:
java.lang.VerifyError: Bad type on operand stack
Exception Details:
Location:
com/msopentech/odatajclient/engine/data/Deserializer.toODataErrorFromXML(Ljava/io/InputStream;)Lcom/msopentech/odatajclient/engine/data/xml/XMLODataError; @22: invokespecial
Reason:
Type 'com/fasterxml/aalto/stax/OutputFactoryImpl' (current frame, stack[5]) is not assignable to 'com/msopentech/javax/xml/stream/XMLOutputFactory'
Current Frame:
bci: @22
flags: { }
locals: { 'java/io/InputStream' }
stack: { uninitialized 0, uninitialized 0, uninitialized 4, uninitialized 4, 'com/fasterxml/aalto/stax/InputFactoryImpl', 'com/fasterxml/aalto/stax/OutputFactoryImpl' }
Bytecode:
0x0000000: bb00 0259 bb00 0359 bb00 0459 b700 05bb
0x0000010: 0006 59b7 0007 b700 08bb 0009 59b7 000a
0x0000020: b700 0b4c 2b2a 1300 5bb6 0010 c000 5bb0
0x0000030: 4cbb 0012 5912 5c2b b700 14bf
Exception Handler Table:
bci [0, 47] => handler: 48
Stackmap Table:
same_locals_1_stack_item_frame(@48,Object[#116])
at com.msopentech.odatajclient.engine.data.ODataReader.readEntitySet(ODataReader.java:63)
at com.msopentech.odatajclient.engine.communication.request.retrieve.ODataEntitySetRequest$ODataEntitySetResponseImpl.getBody(ODataEntitySetRequest.java:89)
at com.msopentech.odatajclient.engine.communication.request.retrieve.ODataEntitySetRequest$ODataEntitySetResponseImpl.getBody(ODataEntitySetRequest.java:61)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
很奇怪的输出,原因是新加的jackson-dataformat-xml版本,覆盖了odatajclient-engine中依赖的版本(因为SpringBoot中的jackson-dataformat-xml是高版本,会自动覆盖odatajclient-engine所依赖的低版本)。
那么解决方案是什么?解决方式就是重新编译odatajclient-engine的jar包,把其依赖的jackson-dataformat-xml的版本改成最新的版本,在重新编译。在odatajclient-engine-xml项目中,修改jackson-dataformat-xml的版本为2.8.5,与SpringBoot的1.4.3-RLEASE版本保持一致。
4.0.0
com.msopentech.odatajclient
odatajclient
0.9.1-SNAPSHOT
Client OData library for Java: Engine XML parser
Java client API for OData 3.0 services: Engine XML parser
com.msopentech.odatajclient
odatajclient-engine-xml
jar
${project.parent.basedir}
com.fasterxml.jackson.dataformat
jackson-dataformat-xml
2.8.5
com.fasterxml
aalto-xml
..
META-INF
LICENSE