Работа консультанта иногда опасна и трудна. Кроме передовых технологий приходится иметь дело с кровавым энтерпрайзом. Вот на днях приключилось адаптировать веб приложение для работы в IBM WebSphere 8.x.
Приложение ничего так работает в обычном сервлет контейнере. Обычный Spring + CXF с веб сервисами, но когда его запаковали в ухо архив (ear файл), и установили в этот самый энтерпрайз контейнер, стартовать оно не захотело, бам:
Caused by: org.apache.cxf.bus.extension.ExtensionException: Could not load extension class org.apache.cxf.ws.policy.AssertionBuilderRegistryImpl. at org.apache.cxf.bus.extension.Extension.tryClass(Extension.java:183) at org.apache.cxf.bus.extension.Extension.getClassObject(Extension.java:199) at org.apache.cxf.bus.extension.ExtensionManagerImpl.activateAllByType(ExtensionManagerImpl.java:144) at org.apache.cxf.bus.extension.ExtensionManagerBus.<init>(ExtensionManagerBus.java:179) at org.apache.cxf.bus.extension.ExtensionManagerBus.<init>(ExtensionManagerBus.java:191) at org.apache.cxf.bus.spring.SpringBus.<init>(SpringBus.java:45) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:86) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:58) at java.lang.reflect.Constructor.newInstance(Constructor.java:542) at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:148) ... 108 more Caused by: java.lang.IncompatibleClassChangeError: org.apache.neethi.AssertionBuilderFactory at java.lang.ClassLoader.defineClassImpl(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:324) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:155) at com.ibm.ws.classloader.CompoundClassLoader._defineClass(CompoundClassLoader.java:857) at com.ibm.ws.classloader.CompoundClassLoader.localFindClass(CompoundClassLoader.java:765) at com.ibm.ws.classloader.CompoundClassLoader.loadClass(CompoundClassLoader.java:588) at java.lang.ClassLoader.loadClass(ClassLoader.java:731) at java.lang.ClassLoader.defineClassImpl(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:324) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:155) at com.ibm.ws.classloader.CompoundClassLoader._defineClass(CompoundClassLoader.java:857) at com.ibm.ws.classloader.CompoundClassLoader.localFindClass(CompoundClassLoader.java:765) at com.ibm.ws.classloader.CompoundClassLoader.loadClass(CompoundClassLoader.java:588) at java.lang.ClassLoader.loadClass(ClassLoader.java:731) at org.apache.cxf.bus.extension.Extension.tryClass(Extension.java:164)
Здесь решение оказалось довольно простым, покопавшись по форумам, нашёл, что в WebSphere тоже используется Apache Neethi. Но другой версии. Ну вы понимаете. Я бы ожидал, что по умолчанию у веб приложения другой classpath, свой. Но IBM считает по-другому. В общем надо сходить сюда:
Enterprise Applications > your_ear_app > Class loader Class loader order -> Classes loaded with local class loader first (parent last) WAR class loader policy -> Single class loader for application
И потом сюда:
Enterprise Applications > your_ear_app > Manage Modules > your_war_module Class loader order -> Classes loaded with local class loader first (parent last)
К сожалению, эти вещи нельзя настроить по-нормальному, через какой-нибудь дескриптор в архиве, или манифест. Только ручками и мышкой.
После этого я прошел на шаг дальше, и появилась другая ошибка:
java.lang.VerifyError: JVMVRFY013 class loading constraint violated; class=org/apache/cxf/jaxws/binding/soap/SOAPBindingImpl, method=getMessageFactory()Ljavax/xml/soap/Mes sageFactory;, pc=0 at java.lang.J9VMInternals.verifyImpl(Native Method) at java.lang.J9VMInternals.verify(J9VMInternals.java:94) at java.lang.J9VMInternals.initialize(J9VMInternals.java:169) at org.apache.cxf.jaxws.support.JaxWsEndpointImpl.createJaxwsBinding(JaxWsEndpointImpl.java:532) at org.apache.cxf.jaxws.support.JaxWsEndpointImpl.<init>(JaxWsEndpointImpl.java:157) at org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean.createEndpoint(JaxWsServiceFactoryBean.java:249) at org.apache.cxf.frontend.AbstractWSDLBasedEndpointFactory.createEndpoint(AbstractWSDLBasedEndpointFactory.java:173) at org.apache.cxf.frontend.ServerFactoryBean.create(ServerFactoryBean.java:159) at org.apache.cxf.jaxws.JaxWsServerFactoryBean.create(JaxWsServerFactoryBean.java:211) at org.apache.cxf.jaxws.EndpointImpl.getServer(EndpointImpl.java:456) at org.apache.cxf.jaxws.EndpointImpl.doPublish(EndpointImpl.java:334) at org.apache.cxf.jaxws.EndpointImpl.publish(EndpointImpl.java:251) at org.apache.cxf.jaxws.spi.ProviderImpl.createAndPublishEndpoint(ProviderImpl.java:155) at javax.xml.ws.Endpoint.publish(Endpoint.java:255)
Тут всё оказывается немного сложнее и не так линейно решается. Но общий подход такой: лезем в исходники jar файла, где лежит класс org.apache.cxf.jaxws.binding.soap.SOAPBindingImpl, смотрим, какая зависимость содержит класс javax.xml.soap.MessageFactory, это SOAP API, которое включено в JDK 8, но можно его и с собой носить, если не уверены, что будете запускаться на Java 8. В моём случае оно затягивалось через org.apache.servicemix.specs.saaj-api. Этот класс загружен booloader JVM и потом мы его пытаемся загрузить из стороннего jar файла. Его надо исключить:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <configuration> <packagingExcludes>WEB-INF/lib/org.apache.servicemix.specs.saaj-api*</packagingExcludes> </configuration> </plugin> </plugins> </build>
Как-то так. К счастью больше проблем не возникло и после переупаковки приложение заработало. Удачного поиска.