Pokazywanie postów oznaczonych etykietą spring webservices. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą spring webservices. Pokaż wszystkie posty

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 :)

piątek, 13 marca 2009

Spring WebServices on Websphere 6.1.0.x

Już jakiś czas temu stanąłem przed problemem uruchomienia Spring WebServices na Websphere 6.1.0.x. Chodziło o dołożenie do istniejącej aplikacji ear nowego modułu web (modułu webservices), który miał stanowić alternatywny interfejs dostępu do systemu. Oczywiście istniejący oraz nowy moduł web muszą delegować do współdzielonej warstwy service.
Sposób zdefiniowania konfiguracji Spring, w której to 2 contexty springowe modułów web aplikacji ear potrafią współdzielić wspólny context jest poza tematem tego wpisu.

Ze względu na problemy związane z "Jar Hell" próbując różnych ustawień i kombinacji JARów, przez długi czas przy starcie i/lub obsłudze komunikatów widywałem wyjątki: java.lang.VerifyError, java.lang.ClassCastException, java.lang.NoClassDefFoundException. W końcu udało mi się zestawić konfigurację w której działały enpointy typu PayloadRootAnnotationMethodEndpointMapping wraz z JAXB 2:
  • ze względu na to że mam 2 moduły web, których contexty mają delegować do współdzielonego contextu modułu service, jary spring framework są umiejscowione bezpośrednio pod ear, a moduły web odwołują się do nich za pomocą wpisów we własnych plikach Manifest.mf (sekcja Class-Path)
  • W katalogu WEB-INF/lib modułu web services wrzuciłem następujące jary (wszystkie skopiowane z katalogów dist oraz lib dystrybucji Spring WebServices):
    • activation-1.1.1.jar
    • jaxb-api-2.1.jar
    • jaxb-impl-2.1.5.jar
    • spring-oxm-1.5.2.jar
    • spring-oxm-tiger-1.5.2.jar
    • spring-webmvc.jar
    • spring-ws-core-1.5.2.jar
    • spring-ws-core-tiger-1.5.2.jar
    • spring-ws-support-1.5.2.jar
    • spring-xml-1.5.2.jar
    • stax-api-1.0.1.jar
    • XmlSchema-1.3.2.jar
  • zgodnie z FAQ Spring WebServices należy następnie "przykryć" wybrane jary z Websphere . Mamy następujące możliwości:
    • ustawić sposób ładowania klas na Parent-Last dla modułu webServices,
    • ustawić sposób ładowania klas na Parent-Last dla całej aplikacji,
    • zdefiniować własny classloader, który będzie nadrzędnym classloaderem dla classloadera aplikacji,
    Ze względu na to, że kilka razy miałem problemy ze względu na "maintaince" aplikacji, dla której zostały zmienione domyślne sposoby ładowania klas ("przypadkowe" ładowanie klas z servlet-api.jar z katalogu WEB-INF/lib), zdecydowałem się na zdefiniowanie własnego classloadera.
  • Definiowanie własnego classloadera jest szczegółowo opisane w dokumentacji Websphere i polega na wykonaniu następującyh kroków:
    • zdefiniowaniu shared-library, w której znajdować się będą jary zawierające klasy, które to mają być ładowane przez tworzony classloader. Nasza nowo tworzona shared-library musi zawierać następujące jary(wszystkie skopiowane z katalogu lib dystrybucji Spring WebServices):
      • wsdl4j-1.6.1.jar
      • xalan-2.7.0.jar
      • xercesImpl-2.8.1.jar
    • zdefiniowaniu nowego classloadera na poziomie serwera i przypięciu do niego utworzonej w poprzednim punkcie shared-library. Ważne jest, aby sposób ładowania klas dla tego classloader był ustawiony na Parent-Last (czyli Application-First).
  • W końcu w naszej aplikacji musimy zdefiniować bean, który będzie definiował specyficzną dla środowiska uruchomieniowego fabrykę do tworzenia obiektów SOAPMEssage:





W takiej konfiguracji korzystam z dobrodziejstw Spring WebSevices. Ze względu na to, że WAS 6.1.0.x implementuje tylko SAAJ 1.2, moje endpointy obsługują komunikaty SOAP w wersji 1.1