WebServices
Introduction
The REQUEA Platform is a service oriented platform. Every entity definition can be mapped as a WebService using the SOAP protocol.
Entity Properties become attributes in the Document schema definition.
Operations become SOAP operations.
Calling REQUEA as a Web Service
The SOAP Servlet generates automatically WSDL and accept SOAP request over HTTP(s) based on the Entity definition.
The syntax for the WebService servlet is the following:
http://[server]/dysoweb/ws/wsdl.ws?doc=[entity name]
So for example:
http://localhost:8080/dysoweb/ws/wsdl.ws?doc=custTest
Using Visual Studio 2010
You can map automatically the REQUEA Web service as a service reference in Visual Studio:
invoking the client
Here is a sample of client code:
BookClient client = new BookClient();
client.ClientCredentials.UserName.UserName = "RequeaDev";
client.ClientCredentials.UserName.Password = "RequeaDev";
custBook bk = new custBook();
bk.custTitle = tbCouleur.Text;
bk.custAuthor = tbNom.Text;
bk = client.Save(bk);
lblSysId.Text = bk.sysId;
Note that the authentication is BASIC (HTTPS) and requires an update of your app.config to specify the HTTPS basic authentication:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="Book" closeTimeout="00:01:00" openTimeout="00:01:00"
receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false"
bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="Transport">
<transport clientCredentialType="Basic" realm="" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://myserver/dysoweb/ws/soap.ws"
binding="basicHttpBinding" bindingConfiguration="Book" contract="RequeaService.Book"
name="Book" />
</client>
</system.serviceModel>
</configuration>
For an http connexion: (Not recommanded for security reasons unless in controled intranet environments):
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="Book" closeTimeout="00:01:00" openTimeout="00:01:00"
receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false"
bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Basic" realm="" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://myserver/dysoweb/ws/soap.ws"
binding="basicHttpBinding" bindingConfiguration="Book" contract="RequeaService.Book"
name="Book" />
</client>
</system.serviceModel>
</configuration>
Calling a Web Service in REQUEA
Invoking a Web Service from REQUEA requires that you adapt the semantics of the Web Service and wrap it as a JavaScript Plugin service.
The wrapping code should be written in Java.
There are (at least) two ways to wrap the code and invoke the Web Service:
- using Apache Axis
- using SaaJ and XML
Using Apache Axis
Using Apache Axis you can generate Java wrappers around the Web Service and invoke those wrapper from the Java code.
In this example, we will use the ViaMichelin SOAP API.
1 - Generate the stubs
This is done with the Axis tool WSDL2Java:
This will generate some java classes, that you should include in your plugin project:
com\viamichelin\ws\geo\GeoCoordinates.java
com\viamichelin\ws\localization\Address.java
com\viamichelin\ws\localization\FindLocation.java
com\viamichelin\ws\localization\FindLocations.java
com\viamichelin\ws\localization\GeocodingRequest.java
com\viamichelin\ws\localization\InputAddress.java
com\viamichelin\ws\localization\Location.java
com\viamichelin\ws\localization\Poi.java
com\viamichelin\ws\localization\PoiDatasheet.java
com\viamichelin\ws\localization\PoiDescriptionList.java
com\viamichelin\ws\localization\PoiId.java
com\viamichelin\ws\localization\PoiMetaNumList.java
com\viamichelin\ws\localization\PoiMetaStringList.java
com\viamichelin\ws\localization\service\Geocoding.java
com\viamichelin\ws\localization\service\GeocodingService.java
com\viamichelin\ws\localization\service\GeocodingServiceLocator.java
com\viamichelin\ws\localization\service\GeocodingSoapBindingStub.java
com\viamichelin\ws\object\Integer.java
Calling the Web Service
The call is done within the plugin script in a jsFunction_execute for example:
InputAddress[] addressList = new InputAddress[1];
addressList[0] = inputAddress;
request.setAddressesList(addressList);
// call the web service
GeocodingServiceLocator locator = new GeocodingServiceLocator();
Geocoding service = locator.getGeocoding();
String login = registry.getParam("com.requea.viamichelin.login");
String passwd = registry.getParam("com.requea.viamichelin.password");
if(login == null || passwd == null) {
throw new RegistryException("Viamichelin service not properly configured. Missing login or password");
}
String url = registry.getParam("com.requea.viamichelin.url");
if(url != null) {
locator.setGeocodingEndpointAddress(url);
}
try {
FoundLocationList result = service.getLocationsList(request, login+"|"+passwd)[0];
if(result.getFoundLocations().length > 0) {
FoundLocation loc = result.getFoundLocations()[0];
return new GeocodedLocation(loc.getLocationDesc());
} else {
// nothing found
return null;
}
} catch(Exception e) {
throw new EndUserException(e);
}
Setting up classpath and dependencies
in your pom.xml, you need to add the axis dependencies for inclusion in your bundle:
<groupId>axis</groupId>
<artifactId>axis</artifactId>
<version>1.4</version>
<scope>provided</scope>
</dependency>
and for the bundle class path:
.,
lib/axis-1.4.jar,
lib/axis-jaxrpc-1.4.jar,
lib/axis-saaj-1.4.jar,
lib/axis-wsdl4j-1.5.1.jar,
lib/commons-discovery-0.2.jar
</Bundle-ClassPath>
Using SAAJ
Using SAAJ, you can invoke the Web service without having to generate stubs.
It is a lower level protocol, allowing for a finer control of the WebService invokation, at the expense of the simplicity.
Here is a snipet of code that does a SAAJ invokation:
ClassLoader cl = th.getContextClassLoader();
// build a GetList request
try {
th.setContextClassLoader(this.getClass().getClassLoader());
String userName = registry.getParam("username");
String password = registry.getParam("password");
String strURL = registry.getParam("endpoint");
SOAPConnectionFactory factory = SOAPConnectionFactory.newInstance();
SOAPConnection connection = factory.createConnection();
URL endpoint = new URL(strURL);
// this web services requires the SOAPAction.
MessageFactory mf = MessageFactory.newInstance();
SOAPMessage message = mf.createMessage();
MimeHeaders hd = message.getMimeHeaders();
hd.addHeader("SOAPAction", "urn:WS/"+action);
SOAPHeader header = message.getSOAPHeader();
// adds auth info in the header
Element elAuth = header.getOwnerDocument().createElementNS(
"urn:WS", "AuthenticationInfo");
elAuth.setAttribute("xlmns", "urn:WS");
XMLUtils.addElement(elAuth, "userName", userName);
XMLUtils.addElement(elAuth, "password", password);
header.appendChild(elAuth);
// include the request in the body
SOAPBody body = message.getSOAPBody();
body.appendChild(body.getOwnerDocument().adoptNode(
elRequest.cloneNode(true)));
SOAPMessage response = connection.call(message, endpoint);
// fault?
Element elBody = response.getSOAPBody();
Element elFault = XMLUtils.getChild(elBody, "Fault");
if(elFault != null) {
log.severe(XMLUtils.getChildText(elFault, "faultstring"));
throw new EndUserException(XMLUtils.getChildText(elFault, "faultstring"));
}
Element elResp = XMLUtils.getChild(elBody, "urn:WS", respName);
// close the connection
connection.close();
return elResp;
} catch(Exception e) {
throw new RegistryException(e);
} finally {
th.setContextClassLoader(cl);
}
Required dependencies in your project:
<groupId>javax.xml</groupId>
<artifactId>saaj-api</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>com.sun.xml.messaging.saaj</groupId>
<artifactId>saaj-impl</artifactId>
<version>1.3</version>
</dependency>
and the plugin section in the pom.xml
<groupId>com.requea.dysoapp</groupId>
<artifactId>maven-dysoapp-bundle</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-Name>${pom.name}</Bundle-Name>
<Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
<Export-Package />
<Import-Package>
org.apache.commons.logging,
org.osgi.framework,
com.requea.app,
com.requea.util,
com.requea.util.xml,
org.w3c.dom,
javax.net.*,
javax.crypto.*,
javax.xml.*,
javax.activation,
org.mozilla.javascript;version=1.7.2,
javax.imageio.*,
org.xml.*,
!org.jvnet.*,
!com.sun.*
</Import-Package>
<Bundle-Activator>com.requea.myplugin.Activator</Bundle-Activator>
<Embed-Dependency>*;scope=compile|runtime</Embed-Dependency>
</instructions>
</configuration>
</plugin>