Showing posts with label web service. Show all posts
Showing posts with label web service. Show all posts

Tuesday, October 30, 2012

Calling Apex web service - something additional to developer force apex quick start guide

In Apex code quick start guide from developer force, there's examples about the Book customized object. However, it's not clearly described about how to invoke a web service built with Apex.
Here I'll continue using the Book object as an example and explain how to call such web service with soapUI.
1. Create a web service for the Book example:
global class BookWebService{
    webService static Id createBook(String name, Decimal price){
        Book__c book=new Book__c(Name=name,Price__c=price);
        insert book;
        return book.Id;
    }
}
2. Generate wsdl from the BookWebService class, and save it locally.
3. Generate partner wsdl from Develop --> API page, and save it locally.
4. In SoapUI, create a project by adding the above 2 wsdls.
5. Invoke login operation based on the partner wsdl with the credential to SFDC.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:partner.soap.sforce.com">
   <soapenv:Header>
   </soapenv:Header>
   <soapenv:Body>
      <urn:login>
         <urn:username>zzzzzzzzzzz</urn:username>
         <urn:password>xxxxxxxxxxx</urn:password>
      </urn:login>
   </soapenv:Body>
</soapenv:Envelope>

6. Copy the sessionid from the response and make the input for the createBook web service:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:book="http://soap.sforce.com/schemas/class/BookWebService">
   <soapenv:Header>
      <book:AllowFieldTruncationHeader>
         <book:allowFieldTruncation>true</book:allowFieldTruncation>
      </book:AllowFieldTruncationHeader>
      <book:SessionHeader>
         <book:sessionId>00DE0000000adhV!ARQAQLsTZiERi36QEprxQx0F_xI826qAM9W4AQNwCdMnKO18GIrjijaRIvjFsnxYF148Rr_4EFknbzjwerkFhW7enghQPRCN</book:sessionId>
      </book:SessionHeader>
   </soapenv:Header>
   <soapenv:Body>
      <book:createBook>
         <book:name>Book Test</book:name>
         <book:price>100.10</book:price>
      </book:createBook>
   </soapenv:Body>
</soapenv:Envelope>

7. Invoke the web service, and get the following response:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:book="http://soap.sforce.com/schemas/class/BookWebService">
   <soapenv:Header>
      <book:AllowFieldTruncationHeader>
         <book:allowFieldTruncation>true</book:allowFieldTruncation>
      </book:AllowFieldTruncationHeader>
      <book:SessionHeader>
         <book:sessionId>00DE0000000adhV!ARQAQLsTZiERi36QEprxQx0F_xI826qAM9W4AQNwCdMnKO18GIrjijaRIvjFsnxYF148Rr_4EFknbzjwerkFhW7enghQPRCN</book:sessionId>
      </book:SessionHeader>
   </soapenv:Header>
   <soapenv:Body>
      <book:createBook>
         <book:name>Book Test</book:name>
         <book:price>100.10</book:price>
      </book:createBook>
   </soapenv:Body>
</soapenv:Envelope>

8. Query SFDC with the returned Id:


Monday, April 12, 2010

customized web service invocation with one bpm tool

Lately, I've been working on a customized java service for one bpm tool (Cordys) to invoke platform web services. Here are the implementation details:
1. A method that takes xml message as a string:
public static String invoke(String endpoint, String userName, String password,
String requestMessage) {
SOAPMessage soapResponse = invoke(endpoint, userName, password,
convertStringToSoapMessage(requestMessage));

return convertSoapMessageToString(soapResponse);
}

2. A method that handles SoapMessage data type:
public static SOAPMessage invoke(String endpoint, String userName,
String password, SOAPMessage soapMsg) {
try {
// Create the connection
SOAPConnectionFactory soapConnFactory = SOAPConnectionFactory
.newInstance();
SOAPConnection conn = soapConnFactory.createConnection();

CordysAuthenticator credential = new CordysAuthenticator(userName, password);
// CordysAuthenticator is an "Authenticator" implementation
Authenticator.setDefault(credential);

SOAPMessage soapResponse = conn.call(soapMsg, endpoint);
conn.close();
return soapResponse;
} catch (SOAPException e) {
System.out.println("SOAPException : " + e);
return null;

}
}

3. A method to convert String to SoapMessage:
public static SOAPMessage convertStringToSoapMessage(String requestString) {

try {

// Use SAAJ to convert Document to SOAPElement
// Create SoapMessage
MessageFactory msgFactory = MessageFactory.newInstance();
SOAPMessage message = msgFactory.createMessage();
SOAPPart part=message.getSOAPPart();

byte[] soapBytes=requestString.getBytes();
ByteArrayInputStream stream = new ByteArrayInputStream(soapBytes);
StreamSource source = new StreamSource(stream);
part.setContent(source);
message.saveChanges();

// This returns the SOAPBodyElement
// that contains ONLY the Payload
System.out.print("request message String:"+ requestString);
return message;

} catch (SOAPException e) {
System.out.println("SOAPException : " + e);
return null;

}

}

4. A method to convert SoapMessage to string
public static String convertSoapMessageToString(SOAPMessage soapMessage) {
try {
String result = "";

// Create transformer
TransformerFactory transFactory = TransformerFactory.newInstance();
Transformer transformer = transFactory.newTransformer();
// TransformerConfigurationException


// Get reply content
Source sourceMsg = soapMessage.getSOAPPart().getContent();
// SOAPException

// Set output transformation
StringWriter writer = new StringWriter();
StreamResult resultStream = new StreamResult(writer);
transformer.transform(sourceMsg, resultStream);
// TransformerException
result = writer.toString();
writer.close();
// IOException

return convertToXmlString(result);
} catch (...){}
}


There might be error message such as "invalid soap messages" when the message is sent to the web gateway. This might be caused by the doublequote used in the name space declaration of the xml message. In order to solve the problem, we can use the method String.replaceAll("\"","\\\""); to replace doublequotes with escaped doublequotes.

Monday, May 18, 2009

A tricky problem for xml validation

There's an interesting article about XML Schema nillable=”true” vs minOccurs=”0″ in Dimithu's blog. minOccurs is an xml attribute that most people understand; but for nillable="true", people might understand its meaning; however, how to use it in a correct way is not well known. By "correct way", I mean "the way that complies with W3C specifications".
Actually, a lot of software vendors are not clear about its usage as well. This leads to interoperability problems. Some BPM/SOA vendors implement different validation rules for a "nillable" tag, which are not in accordance with W3C specifications. 

Imagine an application is built which utilizes web services from different external system, and those web services require web service validation. The problem occurs that some systems send out the message which they consider as valid, while it's rejected by other systems due to different interpretation of "wsdl/soap validity". 

In short, the problem can be described as follows:
When an element is defined in wsdl using  xs:element name="nilint" nillable="true" type="xs:int"

The nilint element in the soap message should look like this if it's an empty node:
nilint xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/
If it's not an empty node, we can remove the contained attribute because by default the value for nil is "false".

Considering the aforementioned scenario, if some development tools that are used to build the SOA application don't provide support for the nil tag, we need to find some way to workaround the issue. The normal way to do it is to create an interceptor class whenever the soap message is being sent out.
This looks like a minor issue, but indeed a lot tools have problems with it; for example, wsdl validation function in SoapUI does not work in a correct way.