July 18, 2013 5 Comments
I recently discovered an issue in two concurrent projects that presented an interesting problem. The reason I have never seen this before is probably because I have never tried to use the Invoke Web Service activity in Livecycle before for a production system. It’s a simple enough activity with all of the usual parts to it and on the outside seems to work fairly well especially during development. The issue doesn’t present itself until you try to prepare it for deployment to multiple environments. I will try to explain (stick with me here)
In a typical environment you will configure system values that change via configuration parameters. This allows you to package an LCA up for deployment to another system and set the configuration parameters after deployment for that system. In any normal business you usually find a number of environments, for example, Development, Test (or SIT – system integration testing), UAT (user acceptance testing) and Production – all quite typical.
Getting back to the Web Service activity, you notice that in the first page that a number of the useful items like username, password, targetUrl, etc are all configurable via XPath which in turn allows for a configuration variable to be used here. But strangely, Adobe have left out XPath for WSDL URL.
Why is this a problem?
If WSDL URL is hard-coded and you try to migrate the application to another server, every time your InvokeWebService activity is called, it will try to call the original hard-coded WSDL URL for that activity during execution. So if your web services follow the same deployment pattern as your Livecycle applications i.e. DEV > SIT > UAT > PRD, when you execute the activity in your SIT environment, the DEV WSDL will be loaded by Livecycle before it is invoked. Which if it is unavailable, causes the whole activity to fail. So you can see that once it hits PROD and someone brings down the DEV web services for whatever reason, the PROD instance will fail.
Why is this built like this? Who knows. How do we work around it? Read on…
The hack (sorry – undocumented workaround)
Inside the activity, if you change the InvokeWebService activity Options parameter to a variable type you will notice that it accepts a WebServiceSettingBean as input.
A quick Google of the bean name and you get a few scattered posts on Adobe forums asking for help on parameters but no complete guide on how to use it or what all of the parameters are. It’s because no public API exists for this bean. You can use the object inspector after creating a bean variable and see what the bean parameters are but it crucially won’t tell you what the variable types are. Below is a full list of the variables it accepts and their types:
- String wsdl
- String username
- String password
- String port
- String portBindingLocalName
- String targetUrl
- String operationName
- String timeOut
- String inputRequest (or org.w3c.dom.document – haven’t tried this)
- String inputRequestTest
- List (com.adobe.idp.Document) attachments
- Map<String, String> wsdlMap
- boolean enableMTOM
- boolean authenticatePreemptively
Most of the above parameters can be figured out from the InvokeWebService activity component, but some I had to play with to get it to work properly.
- port & portBindingLocalName must be set to the actual web service portBindingLocalName otherwise you will get the following error:
2013-07-16 13:51:26,214 ERROR [com.adobe.workflow.AWS] (http-0.0.0.0-8080-2) An exception was thrown with name com.adobe.idp.dsc.webservice.exception.WebServiceConfigurationException message:Configuration error - port specified is unavaliable: while invoking service WebService and operation invoke and no fault routes were found to be configured.
note: The portBindingLocalName is never shown in the WebServiceSettingBean editor. There are two ways to find it:
1. Look into the WSDL xml and you will see:
<wsdl:binding name="[portBindingLocalName]" type="...">
2. Use a soap tester like SoapUI that shows the binding name when it sets up the WSDL.
1. Create a WebServiceSettingBean variable and assign it as the input Options for the InvokeWebService activity.
2. Create a simple process that maps all of the bean parameters from input variables to create the bean. This also allows you to use template parameters for the inputRequest that use embedded XPath.
3. In your process, call the Create Settings Bean process where you set all of the Bean variables, then call the InvokeWebService activity using the Bean created in #2 as input.
I have provided an example application so you can see the workaround in action. It creates an example WSDL that it calls itself to show how the settings are applied to the bean and then invoked. Adjust the server name and port and administrator account details as necessary. They are all default for a turnkey ES4 install.
Easily the best way to create your inputRequest and get the portBindingLocalName is using SoapUI. Using the Generate button in the activity doesn’t give you all of the input variables for the WSDL you are accessing. Plug the WSDL URL into SoapUI and you will get a proper view of the WSDL request query so you can XPath it as necessary.
Beware of the ?blob=base64 bug in later version though. I will do a post to highlight this issue in the future. For now I use version 3.6.1 for most of my work.