wissel.net

Usability - Productivity - Business - The web - Singapore & Twins

Webservices in XPages - AXIS vs. CXF


I wrote about using Apache AXIS to connect to a web service before. It worked like a charm when you import the AXIS library into the NSF. In the past few days I tried to move this code into an extension library and I had initially very little success. Here is what I tried and what finally worked:
  1. The AXIS libraries are provided as a plug-in in the Domino server, so my first take was to declare a dependency in my plug-in to that plug-in. To successfully create a Extension Library I had to declare dependencies to com.ibm.commons and com.ibm.xsp.core to be able to extend com.ibm.commons.Extension. Unfortunately the plug-ins expose Java commons logging, so the classloader complaint and didn't load the AXIS classes
  2. Second attempt was to split the code into two plug-ins: one that depended on the AXIS plug-in and another one that depended on the former and com.ibm.commons and com.ibm.xsp.core. This didn't yield any better result
  3. 3rd test was to import the AXIS jars into a plug-in and only depend on com.ibm.commons and com.ibm.xsp.core but not the AXIS plug-in. Didn't work either
  4. Finally I tried to switch from AXIS to the newer and more flexible Apache CXF. CXF is able to provide transport over many protocols, not only SOAP and REST but JMS and others too. Initially I felt it was too complex for my little requirement, especially since the list of jar dependencies is quite long.
    Turns out CXF is the optimal solution. I used CXF 2.5.2 and the provided wsdl2java utility. The command line I used is [path-to-cxf-install]/bin/wsdl2java -frontend jaxws21 -client your.wsdl. The -client parameter is optional, but generates a nice sample client that shows the call parameters for all web service methods.
    I found I either need to include the WSDL file in the jar or point to the URL where the WSDL file is available online to get CXF to run. Now for the best part: All dependencies including CXF are available in Domino (I tested on 8.5.3), so other than the Java code generated I didn't had to import, depend etc. on anything (it actually might work even in a Java agent). So CXF is the way to go.
Update: As Bernd pointed out in the comments, it is important to update the permissions ( [Notes program directroy]/jvm/lib/security/java.policy ):
grant {
permission java.lang.RuntimePermission "setContextClassLoader";
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
};

For testing I used a dictionary service that returns the definitions found for a given word. My class (the part I had to write myself) is rather short:
package demo ;

import java.io.ByteArrayOutputStream ;
import java.io.IOException ;
import java.io.OutputStream ;
import java.io.OutputStreamWriter ;
import java.net.URL ;
import java.util.List ;
import javax.xml.namespace.QName ;
import com.aonaware.services.webservices.Definition ;
import com.aonaware.services.webservices.DictService ;
import com.aonaware.services.webservices.DictServiceSoap ;
import com.aonaware.services.webservices.WordDefinition ;

public class Dict2Test {
    public final static String WORD_TO_LOOK_FOR = "Trust" ;
    public final static String WSDL_URL = "http://services.aonaware.com/DictService/DictService.asmx?WSDL" ;
    public static final QName SERVICE_NAME = new QName ( "http://services.aonaware.com/webservices/", "DictService" ) ;

    public static void main ( String [ ] args ) throws IOException {
        Dict2Test dt = new Dict2Test ( ) ;
        ByteArrayOutputStream out = new ByteArrayOutputStream ( ) ;
        dt. getWordInfo (out, WORD_TO_LOOK_FOR ) ;
        System. out. println (out. toString ( ) ) ;
    }

    public void getWordInfo ( OutputStream out, String theWord ) {
        OutputStreamWriter w ;
        DictService ds ;
        DictServiceSoap dss ;
        w = new OutputStreamWriter (out ) ;
        try {
            ds = new DictService ( new URL (WSDL_URL ),SERVICE_NAME ) ;
            dss = ds. getDictServiceSoap ( ) ;
            WordDefinition wd = dss. define (theWord ) ;
            List <Definition > allDef = wd. getDefinitions ( ). getDefinition ( ) ;
            if (allDef. isEmpty ( ) ) {
                w. append ( "<h1>No definition found for " ) ;
                w. append (theWord ) ;
                w. append ( "</h1>\n" ) ;
            } else {
                w. append ( "<h1>You were looking for: " ) ;
                w. append (theWord ) ;
                w. append ( "</h1>\n<ul>" ) ;
                for (Definition oneD : allDef ) {
                    w. append ( "\n<li>" ) ;
                    w. append (oneD. getWordDefinition ( ) ) ;
                    w. append ( "</li>" ) ;
                }
                w. append ( "\n</ul>" ) ;
            }
        } catch ( Exception e ) {
            try {
                w. append ( "<h1>" + e. getMessage ( ) + "</h1>" ) ;
            } catch ( IOException e1 ) {
                e1. printStackTrace ( ) ;
            } finally {
                try {
                    w. close ( ) ;
                } catch ( IOException ex ) {
                    ex. printStackTrace ( ) ;
                }
            }
        }
    }
}
To test the class I defined it as a managed bean in faces-config.xml:
<managed-bean>
    <managed-bean-name>dictionary </managed-bean-name>
    <managed-bean-class>demo.Dict2Test </managed-bean-class>
    <managed-bean-scope>request </managed-bean-scope>
</managed-bean>
and created an XAgent with the following codein the BeforeRenderResponse event:
var proceedWithBeanCall = true ;
var x = context. getUrlParameter ( "word" ) ;

if (x == null || x == "" ) {
    x = "Parameter [word] missing, use ..XAgentDemo4?word=somevalue" ;
    proceedWithBeanCall = false ;
}

// The external context and the response object
var exCon = facesContext. getExternalContext ( ) ;
var response = exCon. getResponse ( ) ;

// Deliver uncached result
response. setContentType ( "text/html" ) ;
response. setHeader ( "Cache-Control" , "no-cache" ) ;

// The Output Stream for Binary Output
var out = response. getOutputStream ( ) ;

if (proceedWithBeanCall ) {
    try {
          dictionary. getWordInfo (out , x ) ;
        } catch (innerErr ) {
            x = innerErr. message ;
            proceedWithBeanCall = false ;
      } 
}

if ( !proceedWithBeanCall ) {     
    var w = new java. io. OutputStreamWriter (out ) ;
    w. write ( "<h1>Didn't work the way we wanted!</h1>\n" ) ;
    w. write ( "<h2>" ) ;
    w. write (x ) ;
    w. write ( "</h2>\n" ) ;
    w. close ( ) ;
}

// Done
facesContext. responseComplete ( ) ;
out. close ( ) ;
Note the signature of the working method. By using the output stream as a parameter the function can be easily tested outside of XPages (see the main() function in the class) as well as integrated into XPages (the approach is called dependency injection).
As usual: YMMV

Posted by on 29 March 2012 | Comments (e) | categories: XPages

Comments

  1. posted by Ferhat Bulut on Tuesday 12 June 2012 AD:
    Hi Stephen,

    What do you think about jax-ws method ?
    I used and its working but i amnot sure its best way or not about performance issue etc. we have http crash problem about my app and dont know what is the reason. Maybe web service integration or another thing, opened pmr and still searching for the reason.

    I used this method to execute webservice from xpages : { Link }

    I just want to know your ideas about webservice execution ?
  2. posted by Bernd Hort on Tuesday 17 July 2012 AD:
    Hi Stephan,
    I get an exception
    javax.xml.ws.WebServiceException: class com.aonaware.services.webservices.DictionaryListExtendedResponse do not have a property of the name {http ://services.aonaware.com/webservices/}DictionaryListExtendedResult
    Caused by: javax.xml.bind.JAXBException: {http ://services.aonaware.com/webservices/}DictionaryListExtendedResult is not a valid property on class com.aonaware.services.webservices.DictionaryListExtendedResponse
    using the code in a XPage.

    If I ran the code directly from Eclipse everything works fine.

    Is there any magic trick?

    Thanx
    Bernd
  3. posted by Bernd Hort on Tuesday 31 July 2012 AD:
    Hi Stephan,
    Thank you for the hint using a jar-File instead of adding the generated code to WebContent/WEB-INF/src folder.

    After adding the following lines to the [Notes program directroy]/jvm/lib/security/java.policy file it worked. Emoticon smile.gif
    grant {
    permission java.lang.RuntimePermission "setContextClassLoader";
    permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
    };

    Bernd

  4. posted by Steve Lapthorn on Tuesday 18 September 2012 AD:
    Great post, it was really helpful to get my web service consumer up and running against a MS based webservice (which the built-in "import WSDL" function of Notes 8.5.3 failed to import)

    I also added "-exsh true" to the wsdl2java command when I needed to generate the client code to set soap header values.

  5. posted by Rickard Tauson on Monday 10 December 2012 AD:
    Thanks for a great post, trying to implement some logging into the request using either a cxf.xml implementation or a programmatic approach. In both cases the use of wsdl4j is needed from CXF, the domino server uses wsdl4j 1.5.1 (I think) due to dependecy on a old axis version. Have you found any solution on logging request / response on a domino server using xPage and cxf ?
  6. posted by Martin Vereecken on Tuesday 22 January 2013 AD:
    Hi Stephan,

    Thank you for pointing me to CXF! I'm not a Java specialist, so it took me a while to get things rolling... I have some questions:
    1) Am I right that you're saying that we only have to create a jar with the classes generated by CXF, and import it and that we don't have to add anything else? To me it seems I have to add all CXF related jars into the lib\ext-directory...?
    2) Even then, it still doesn't work. I can get it to work in Eclipse, with the Notes jvm as JRE and CXF classes added, but in Domino, I get:
    WARNING: Interceptor for http://arcelormittal.com/documentum/objectservices Objectservice #http://arcelormittal.com/documentum/objectservices Create has thrown exception, unwinding now
    Throwable occurred: java.lang.NullPointerException: NullPointerException invokinghttp://svsim045.sidmar.be/documentum.service/objectservice.asmx null at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)

    Any idea what I could have forgotten? I think it's strange sun classes are used. Like they get in the way of the apache classes or so...?
  7. posted by Martin Vereecken on Tuesday 22 January 2013 AD:
    Btw good to see the "Show 'n Tell" image is still used nowadays; I posted it at that time Emoticon smile.gif.
  8. posted by Stephan H. Wissel on Wednesday 23 January 2013 AD:
    @Martin: did you amend the java.policy as per Bernd's comment?
  9. posted by Martin Vereecken on Wednesday 23 January 2013 AD:
    Stephan, I did add the "grants" to the java.policy file. But, what really seemed the problem: I did my tests locally (in my Notes 9 client). When I ran the agent scheduled on the server, it worked!! Or the java classes are different on client versus server, or the classes are different in version 9...
    Anyway: thanks a lot for this great article, it helped me in getting this to work tremendously! I'll post my findings in an article on my site too.

    Kind regards,
    Martin
  10. posted by Martin Vereecken on Wednesday 23 January 2013 AD:
    Some extra information can be found here: { Link }

    Regards,
    Martin
  11. posted by don van zijtveld on Thursday 24 January 2013 AD:
    Dear Stephan,

    thanks for this post (reminded via Martin Vereecken to it)!

    we want to connect with a REST API (webservice) that supports both JSON and XML and we need to authenticatie via OAuth2.0.

    Now I previously thought that we needed to use the new Extension Library for this but now I'm not sure anymore.

    Is Apache CXF a good alternative for this?
  12. posted by Cristian D'Aloisio on Saturday 26 January 2013 AD:
    Very interesting post and comments, thanks for sharing.

    Cristian


  13. posted by Lee McMullen on Thursday 31 January 2013 AD:
    Thanks again for the post Stephan, very useful as I mentioned on Twitter.

    A question for you, your article doesn't mention updating the java.policy file as suggested by Bernd in the first comment. Is that because you did something different which meant the permissions didn't require updating or was it because your policy file had already been updated previously?

    Cheers,
    Lee
  14. posted by Lauri Laanti on Wednesday 14 May 2014 AD:
    Thanks for an informative post! Just one addition: I noticed that with the wsdl2java tool you can create the whole JAR automatically with wsdl file included with -clientJar option.