Search

Mobile tag

About Me

I am the "IBM Collaboration & Productivity Advisor" for IBM Asia Pacific. I'm based in Singapore.
Reach out to me via:
Follow notessensei on Twitter
(posts)
Skype
Sametime
IBM
Facebook
LinkedIn
XING
Amazon Store
Amazon Kindle

Twitter

Domino Upgrade

VersionSupport end
5.0
6.0
6.5
7.0
Upgrade to 8.5x now!
(see the full Lotus lifcyle) To make your upgrade a success use the Upgrade Cheat Sheet.
Contemplating to replace Notes? You have to read this! (also available on Slideshare)

Languages

Other languages on request.

Visitors

Useful Tools

Get Firefox
Use OpenDNS
The support for Windows XP is coming to an end and has . Time to consider an alternative to move on. sounds like a lot of time, but, like an object in a mirror, it is closer than you think.

« Reuse web agents that use PRINT - Bean edition | Main| Story Telling »

Webservices in XPages - AXIS vs. CXF

QuickImage 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 code
in 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

Comments

Gravatar Image1 - 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
grant {
permission java.lang.RuntimePermission "setContextClassLoader";
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
};

Bernd

Gravatar Image2 - 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 ?

Gravatar Image3 - 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

Gravatar Image4 - 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.

Gravatar Image5 - 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 ?

Gravatar Image6 - 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...?

Gravatar Image7 - Btw good to see the "Show 'n Tell" image is still used nowadays; I posted it at that time Emoticon.

Gravatar Image8 - @Martin: did you amend the java.policy as per Bernd's comment?

Gravatar Image9 - 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

Gravatar Image10 - Some extra information can be found here: { Link }

Regards,
Martin

Gravatar Image11 - 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?

Gravatar Image12 - Very interesting post and comments, thanks for sharing.

Cristian


Gravatar Image13 - 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

Post A Comment

Please note: Comments without a valid and working eMail address will be removed. This is my site, so I decide what stays here and what goes.

:-D:-o:-p:-x:-(:-):-\:angry::cool::cry::emb::grin::huh::laugh::rolleyes:;-)

Disclaimer

This site is in no way affiliated, endorsed, sanctioned, supported, nor enlightened by Lotus Software nor IBM Corporation. I may be an employee, but the opinions, theories, facts, etc. presented here are my own and are in now way given in any official capacity. In short, these are my words and this is my site, not IBM's - and don't even begin to think otherwise. (Disclaimer shamelessly plugged from Rocky Oliver)
© 2003 - 2013 Stephan H. Wissel - some rights reserved as listed here: Creative Commons License
Unless otherwise labeled by its originating author, the content found on this site is made available under the terms of an Attribution/NonCommercial/ShareAlike Creative Commons License, with the exception that no rights are granted -- since they are not mine to grant -- in any logo, graphic design, trademarks or trade names of any type. Code samples and code downloads on this site are, unless otherwise labeled, made available under an Apache 2.0 license. Other license models are available on written request and written confirmation.