wtorek, 14 lipca 2009

Websphere 6.1, SWS i MTOM

Websphere w wersji 6.1 obsługuje jedynie SAAJ w wersji 1.2. Aby móc korzystac z MTOM potrzebny jest SAAJ 1.3. Z tego co znalazłem w sieci jest dostępny odpowiedni feature pack, który ma obsługiwac SAAJ 1.3, MTOM oraz wiele więcej. Jednak instalowanie feature packów od IBM często kończy się różnie (tzn czasami nie działają, a jeszcze częściej trzeba instalowac kolejne packi/łaty itd). Dlatego zdecydowałem się na "własną instalację" SAAJ 1.3.
Na podstawie moich wcześniejszych walk z SWS pod Websphere, wszystko okazało się całkiem proste.
Do jarów wchodzących w skład shared-library (definujących biblioteki nowego classloadera)dodajemy dodatkowo:

Dodatkowo pozwalamy SWS na automatyczne wykrycie implementacji javax.xml.soap.MessageFactory - czyli z naszej konfiguracji springowej wywalamy bean "messageFactory".

Alternatywnie, MTOM równie działa, gdy wymienione wyżej biblioteki wrzucimy do WEB-INF/lib oraz ustawimy ładowanie klas z bibliotek aplikacji przed tymi z bibliotek serwera (PARENT-LAST dla modułu webowego).

W przypadku ustawienia PARENT_LAST i używania JAXB 2 pojawiają się jednak pewne problemy, które objawiają się wyjątkiem:


Caused by: java.lang.VerifyError
at com.sun.xml.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl.<clinit>(RuntimeBuiltinLeafInfoImpl.java:224)
at java.lang.J9VMInternals.initializeImpl(Native Method)
at java.lang.J9VMInternals.initialize(J9VMInternals.java:194)
at com.sun.xml.bind.v2.model.impl.RuntimeTypeInfoSetImpl.<init>(RuntimeTypeInfoSetImpl.java:61)
at com.sun.xml.bind.v2.model.impl.RuntimeModelBuilder.createTypeInfoSet(RuntimeModelBuilder.java:127)
at com.sun.xml.bind.v2.model.impl.RuntimeModelBuilder.createTypeInfoSet(RuntimeModelBuilder.java:79)
at com.sun.xml.bind.v2.model.impl.ModelBuilder.<init>(ModelBuilder.java:151)
at com.sun.xml.bind.v2.model.impl.RuntimeModelBuilder.<init>(RuntimeModelBuilder.java:87)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:422)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl..<init.>(JAXBContextImpl.java:286)
at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:139)
at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:117)
at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:188)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:79)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:618)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:133)
at javax.xml.bind.ContextFinder.find(ContextFinder.java:286)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:372)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:337)
at org.springframework.oxm.jaxb.Jaxb2Marshaller.createJaxbContextFromContextPath(Jaxb2Marshaller.java:311)

Po dłuższej chwili debugowania problemem okazała się klasa javax.xml.namespace.QName.
JAXB 2 przy starcie dobiera się do stałych klasy javax.xml.datatype.DatatypeConstants.
Klasa ta sama w sobie jest ładowana z Websphere(../runtimes/base_v61/java/jre/lib/xml.jar), a typem tych stałych jest klasa javax.xml.namespace.QName, która też znajdują się w Webpshere (../runtimes/base_v61/lib/j2ee.jar). Czyli przy odwołaniu się do do klasy javax.xml.datatype.DatatypeConstants, zostanie ona wraz ze swoimi zależnosciami ( m.in. klasą javax.xml.namespace.QName ) załadowana przez classloader Websphere.
Dodatkowo klasa javax.xml.namespace.QName znajduje się w Jsr173_api-1.0.jar/stax-api-1.0.1.jar, a przy ustawieniu Parent-Last nasza aplikacja najpierw załaduje ją właśnie stamtąd. Dojdzie do sytuacji, w której "skontatkują" się ze sobą 2 klasy (javax.xml.namespace.QName)o tej samej nazwie, o potencjalnie różnych definicjach, załadowane przez 2 różne classloadery.
Rozwiązaniem okazuję się wywalenie z naszej aplikacji biblioteki Jsr173_api-1.0.jar/stax-api-1.0.1.jar i podegranie na jej miejsce stax-api-1.0-2.jar(ta nie zawiera w sobie żadnych klas z pakietu jaxax.xml.namespace)

UPDATE: na jednym ze środowisk pomimo w/w ustawień leciały wyjątki:

Caused by:
java.lang.NoClassDefFoundError: javax.xml.transform.stax.StAXResult
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.getOutputHandler(TransformerImpl.java:416)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:334)
at org.springframework.xml.transform.TransformerObjectSupport.transform(TransformerObjectSupport.java:71)
at org.springframework.ws.wsdl.wsdl11.provider.InliningXsdSchemaTypesProvider.getSchemaElement(InliningXsdSchemaTypesProvider.java:113)
at org.springframework.ws.wsdl.wsdl11.provider.InliningXsdSchemaTypesProvider.addTypes(InliningXsdSchemaTypesProvider.java:101)
at org.springframework.ws.wsdl.wsdl11.ProviderBasedWsdl4jDefinition.afterPropertiesSet(ProviderBasedWsdl4jDefinition.java:233)
at org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition.afterPropertiesSet(DefaultWsdl11Definition.java:170)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1369)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1335)
... 39 more
Caused by:
java.lang.ClassNotFoundException: javax.xml.transform.stax.StAXResult
at java.net.URLClassLoader.findClass(URLClassLoader.java:496)
at com.ibm.ws.bootstrap.ExtClassLoader.findClass(ExtClassLoader.java:132)
at java.lang.ClassLoader.loadClass(ClassLoader.java:631)
at com.ibm.ws.bootstrap.ExtClassLoader.loadClass(ExtClassLoader.java:87)
at java.lang.ClassLoader.loadClass(ClassLoader.java:597)
at com.ibm.ws.classloader.ProtectionClassLoader.loadClass(ProtectionClassLoader.java:58)
at com.ibm.ws.classloader.ProtectionClassLoader.loadClass(ProtectionClassLoader.java:54)
at com.ibm.ws.classloader.CompoundClassLoader.loadClass(CompoundClassLoader.java:394)
at java.lang.ClassLoader.loadClass(ClassLoader.java:597)
... 48 more

Klasa org.springframework.xml.transform.TransformerObjectSupport korzysta z wywołania javax.xml.transform.TransformerFactory.newInstance(), przy czym faktyczna implementacja jest określona za pomocą mechanizmu ServiceLoader API. Okazało się, że w obu jarach: jaxp-ri-1.4.2.jar oraz xalan-2.7.0.jar, istnieją wpisy definiujące implementacje dla interfejsu javax.xml.transform.TransformerFactory. Dotychczas "szczęśliwie" wczytywana była implementacja org.apache.xalan.processor.TransformerFactoryImpl z xalan-2.7.0.jar, ale na jednym ze środowisk zaczytano com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl z jaxp-ri-1.4.2.jar. Nie miałem zbytnio ochoty wgłębiać się w ten temat (można poprzez system property wymusić konkretną implementację , może można określić kolejność ładowania przez ServiceLoader API) usunąłem plik META-INF\services\javax.xml.transform.TransformerFactory z jaxp-ri-1.4.2.jar i po kłopocie :)