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:
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:
As usual: YMMV
- 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
andcom.ibm.xsp.core
to be able to extendcom.ibm.commons.Extension
. Unfortunately the plug-ins expose Java commons logging, so the classloader complaint and didn't load the AXIS classes - 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
andcom.ibm.xsp.core
. This didn't yield any better result - 3rd test was to import the AXIS jars into a plug-in and only depend on
com.ibm.commons
andcom.ibm.xsp.core
but not the AXIS plug-in. Didn't work either - 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.
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 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 ( ) ;
}
}
}
}
}
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:
<managed-bean-name>dictionary </managed-bean-name>
<managed-bean-class>demo.Dict2Test </managed-bean-class>
<managed-bean-scope>request </managed-bean-scope>
</managed-bean>
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).
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 ( ) ;
As usual: YMMV
Posted by Stephan H Wissel on 29 March 2012 | Comments (e) | categories: XPages