SalesForce.com JAX-WS & Netbeans
The process for using SalesForce.com (SFDC) webservices in Netbeans isn’t as intuitive as you would think. Most of the SFDC documentation examples are built using Apache Axis vis-a-vis JAX-WS. The good news is that it’s possible to get Netbeans (and therefore JAX-WS) to properly build a WS client for SFDC.
Once you’ve downloaded your organizations enterprise or partner WSDL, in Netbeans, right-click on the project and select New -> Web Service Client (if it’s not there then
New -> Other -> Web Services Category, and then
Web Service Client). Select Local File and Browse to the location of your downloaded WSDL. Specify your package and leave the Client Style as JAX-WS and “Generate Dispatch Code” unselected.
Netbeans will attempt to do it’s thing and fail with an error – however, it’s not the only thing standing between you and a working web service client in Netbeans. Below is the error that I received.
In this case, it’s complaining about a ComplexType and element that share the same name. There are two different ways to solve the problem and really only one that works with Netbeans. To properly fix the issue (and have Netbeans “see” the fix) you need to create a JAX-WS binding document to prevent the naming collision from occurring. To do this create a new file and cut and paste this into it. You may have to adjust the
@targetNamespace='urn:enterprise.soap.sforce.com' to whichever version (enterprise or partner) your using.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
<bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://java.sun.com/xml/ns/jaxws" wsdlLocation="../wsdl/enterprise.wsdl"> <bindings node="//xsd:schema[@targetNamespace='urn:enterprise.soap.sforce.com']"> <jaxb:globalBindings underscoreBinding="asCharInWord" /> <jaxb:schemaBindings> <jaxb:nameXmlTransform> <jaxb:typeName suffix="Type" /> </jaxb:nameXmlTransform> </jaxb:schemaBindings> </bindings> <enableWrapperStyle>false</enableWrapperStyle> <enableAsyncMapping>false</enableAsyncMapping> </bindings>
But we’re not done quite yet. Even though the first attempt failed, Netbeans created a new “Web Service References” node in the Projects tree. If you expand that node, you’ll see your webservice. Right-click on the service and select “Edit Web Service Attributes”. Select the “WSDL Customization” tab and scroll all the way to the bottom section named “External Binding Files”. Expand it, and click “Add”. Answer “Yes” to the dialog and then find the xml file you created earlier that contained the binding changes.
Once you select “OK”, a dialog will appear indicating that a refresh of the client is underway. At that point, Netbeans will attempt to re-create the client. If all goes well you should have a working SFDC client in Netbeans.
The security model in the SFDC API is interesting. In order to properly utilize the service, you must do two things after logging in. One, re-point to a URL that actually contains your organization, and two, make all subsequent calls to that new URL with a session ID that is provided as a result of the login process. The good thing is it’s documented. The bad thing is it’s not documented well at all for JAXWS. What follows is a condensed version of what it took to make it really work.
Repoint to the correct URL
You log into one URL and as part of the LoginResponse you get the real URL of your organization. All subsequent calls made after logging in must use this new URL. It’s in the documentation and described well. However, the redirection of subsequent calls requires the user (caller) to change the
ENDPOINT_ADDRESS_PROPERTY to the new URL. The SFDC documentation that uses Axis, shows that with Axis the ability to manipulate a SOAP binding property is relatively straightforward. Unfortunately it’s not quite so simple with JAXWS.
All of the documentation regarding using JAXWS in SFDC focuses on the use of the non-standard
com.sun.xml.internal.ws.developer.WSBindingProvider to get access to the SOAP binding properties to insert the new endpoint URL once the login has successfully completed. For some reason, if you attempt to use this from glassfish, the downcast of the port to
WSBindingProvider causes an exception preventing it from working properly and successfully getting the port to re-point to the proper endpoint URL. However, I was able to figure a way around the WSBindingProvider to get access to the SOAP header to set the proper endpoint address. I’ll detail that step in another post.
Provide the proper session ID
This workaround was somewhat simpler even though the reasons are just a complex. The short answer is that the SOAP headers are not implicitly defined in the Salesforce.com WSDL. Because the SOAP headers are defined in the WSDL binding section instead of the portType section, they don’t become available and you won’t be able to get set the proper session ID wihout getting really creative. The solution is very straightforward. In the
WSImport Options tab where you set the jaxb options, set one more
WSImport Option as well. Add in
When done successfully, the method signatures will now include a parameter for the SessionHeader (where you set the sessionID).
I’m going to show a full working example of all of this in a subsequent post for those who want/need more information.
Upon further testing it appears that wsimport will disregard the
-XautoNameResolution if you also provide a mapping file because they’re actually attempting to do the same thing. Furthermore, if you just use
-XautoNameResolution without the mapping, wsimport will work properly however Netbeans doesn’t “see” the fix and still complains even though you can compile and run. Removing the references for
-XautoNameResolution for now.