Calling a SOAP based Web Service in a Mashup

New in Lawson Smart Office is the support for calling a web service in a Mashup. Now we did have REST support in 9.1.3 and using the template XML functionality it was possible to make a SOAP request to get data. What is new in 10.0.0 is the ability to point to a WSDL, call it and get the TYPED objects back. So you don’t work with XML, but the actual types that are described in the WSDL. How can you try this new feature?

You use one of the Data Service controls in your mashup. For example select Insert Control -> DataService -> DataListPanel. As a service type you can choose between REST, MI (M3 API) and WS. Now if you want to use M3 APS I would recommend the MIPanels but the access to M3 API is available as a Data Service as well.

After selecting the type WS you will have the following screen in the Mashup designer.

This is the standard configuration for all data service. There is a number of operations and a bucket of properties that you can set on each operation.
Notice the Configure… button down to the right. Press it to get help with configuring most of the properties. For a complete reference check out the SDK Documentation found under Help in the Designer and search for WSDataService.

Enter the Wsdl Uri and press Load to download the WSDL and to create a dynamic proxy against it. The wsdl must be available at this URL for as long as you plan to use the Mashup. A dynamic proxy will be created once during each LSO session so the wsdl has to be available from the Client PC when actually running the mashup. So the URL has to be accessible both in design time and in runtime.

The first drop down is simply a history box with previous URLs and does not affect the actual configuration. This configuration tool is also available as a stand alone tool (tool://wstest).

I’ll enter a path to a M3 MI WSDL found on our LWS server, but you can just as well use a WSDL that maps to an SQL query. You need to check out the documentation for Lawson Web Services. I’ll play around with MNS150MI and show companies and divisions my user has access too.

I start by entering the URL to the WSDL. Then I fill in some information:

I enter my login and password, other in data and press Test. If the call is successful the output tab will be opened. In the output view the output from the web service call will be generated. The output from a generated stub is always a class or a collection. Below you can see that the object returned is in fact a collection. This is because the first you see in the result view is a list and there is no description or label.

Also notice that properties are available and in my case they are all green. That is good. If it’s green then it’s a property and not a public field. In order for binding to works the object you want to access has to be a property.

A work around for binding to something that is not a property would be to set the WS.OutputPath on the property. Check the documentation for WSDataService for all configuration options.

Say I want to show this list in my Mashup. Now the DataContext will be the object returned from the Web Service. In my case a collection of objects that has the same properties as the headings in the list.

I press save and I’ll have the data for the call back in the standard configuration mode.

Lawson Web Services uses a header section to pass in context information (and also authentication information depending on version). Now you can bind in values such as current company from M3. For the current LSO user and password you enter the following magic strings LSO.USER and LSO.PASSWORD. These will be replaced in runtime with the current user and password.

However the user will be asked it she trusts the server you are connecting to. And this will happen once per session. This is because we cannot simply share credentials without some further configuration. In the Core settings there is a new setting called trustedServers. The administrator can enter a list of trusted servers and in that case the user will not see any message.

Within my DataListPanel I add a DataGrid and set it to automatically create all columns from the data.

<DataGrid Grid.Row="0" Margin="8" x:Name="datagrid" AutoGenerateColumns="True" ItemsSource="{Binding}" CanUserAddRows="False" CanUserDeleteRows="False" Style="{DynamicResource styleDataGrid}" />

Notice that the ItemsSource is set to Binding which will bind to the DataContext. I place the DataGrid within my DataListPanel and add a startup event. Now this is the complete XAML and the resulting view.

<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ui="clr-namespace:Mango.UI.Controls;assembly=Mango.UI" xmlns:mashup="clr-namespace:Mango.UI.Services.Mashup;assembly=Mango.UI">
  <Grid.Resources>
  </Grid.Resources>

  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="*" />
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
    <RowDefinition Height="*" />
    <RowDefinition Height="Auto" />
  </Grid.RowDefinitions>
  <mashup:DataListPanel Name="list">
    <mashup:DataListPanel.Events>
      <mashup:Events>
        <mashup:Event TargetName="list" SourceEventName="Startup" TargetEventName="Get" />
      </mashup:Events>
    </mashup:DataListPanel.Events>
    <mashup:DataListPanel.DataService>
      <mashup:DataService Type="WS">
        <mashup:DataService.Operations>
          <mashup:DataOperation Name="Get">
            <mashup:DataParameter Key="WS.Wsdl" Value="https://sestw147.corpnet.lawson.com:12002/jump/ATEMNB/LSC_MNS150MI?wsdl" />
            <mashup:DataParameter Key="WS.Address" Value="https://sestw147.corpnet.lawson.com:12002/jump/ATEMNB/LSC_MNS150MI" />
            <mashup:DataParameter Key="WS.Operation" Value="LstCmpDivi" />
            <mashup:DataParameter Key="WS.Contract" Value="LSC_MNS150MI" />
            <mashup:DataParameter Key="lws.user" Value="LSO.USER" />
            <mashup:DataParameter Key="lws.password" Value="LSO.PASSWORD" />
            <mashup:DataParameter Key="LstCmpDivi1.LstCmpDiviItem.USID" Value="11886" />
            <mashup:DataParameter Key="WS.CredentialSource" Value="Current" />
          </mashup:DataOperation>
        </mashup:DataService.Operations>
      </mashup:DataService>
    </mashup:DataListPanel.DataService>
    <DataGrid Grid.Row="0" Margin="8" x:Name="datagrid" AutoGenerateColumns="True" ItemsSource="{Binding}" CanUserAddRows="False" CanUserDeleteRows="False" Style="{DynamicResource styleDataGrid}" />
  </mashup:DataListPanel>
  <ui:StatusBar Name="StatusBar" Grid.Row="1" Grid.Column="0" />
</Grid>

Please note that the support for calling WS is preview state. We do not support all web services out there. If it works… it works. If it does not work you should contact support or comment in this blog and I’ll have a look but, perhaps there is a work-around or perhaps there is something that we can change to make it work. In any case it should work for many of the Web services out there.

If you want to bind to a specific property you specify the path to that property in the binding. You can copy the path from a context menu in the test program.

Check out the SDKDocumentation (available from the Help menu within the Designer), search for WSDataService.

Happy hacking!

36 thoughts on “Calling a SOAP based Web Service in a Mashup

  1. thibaudatwork

    Karin, I tried a Lawson Web Service of type SQL but the Mashup doesn’t paint the DataGrid, even though the Test in the Configure wizard correctly shows data, and also in Fiddler I see the correct SOAP Request/Response being executed. If I switch to a Lawson Web Service of type API then the Mashup paints correctly the columns, the rows, and the data. We have LSO 10.0.1.1.5. Any idea why the SQL result doesn’t show?

    Reply
    1. thibaudatwork

      I found it, I had to add the property new0Collection to the Binding, where new0 is the result set defined for that web service in LWS Studio. The new attribute for the DataGrid is ItemsSource=”{Binding new0Collection}”

      Reply
      1. rogWork

        If you have a textbox and want to bind it’s value to something that is returned from the webservice how would you do it? For example have a button that would set the textbox text on an event what would be the event parameter

      2. karinpb Post author

        Hi,
        There are two options. Either using an event on the DataPanel that will use the setProperty approach of setting a property (see example in designer). Or a binding directly. Either way you need to know how to access the resulting data and that depends on the Web service called. By using the wstest tool (tool://wstest or from the deisgner) you can see and copy the binding syntax for the value you want by right-clicking. To set a textbox value to something returned from the DataPanel would be straitforward but I’m not sure what you mean with the example “have a button that would set the textbox text”. I’m afraid I don’t have a good example already done, but there should be enough information in this blog or in any of the other blogs to achieve this.

  2. thibaudatwork

    Karin, at the beginning of your post you suggest the REST support in LSO 9.1.3 can be used to called SOAP Web Service using the “Template functionality”. Do you have an example? I thought about using XSLT to transform the XML SOAP Response to the REST XML but haven’t tried yet.

    Reply
  3. karinpb Post author

    Hi,
    I use the template approach in this post http://lawsonsmartoffice.com/2012/01/05/calling-a-web-service-using-ws-and-rest-data-services/. But you can’t XSLT transform the response Xml. You can strip the namespaces and you can use an initial XPath. There are quite a few “SOAP” web services that actually just takes a xml string as input and returns xml as a string. In those cases you might as well use REST since the data returned is specified as a string – so there is no type mapping.

    Reply
  4. Pingback: How to get the URL to Lawson Web Services in a Mashup « M3 ideas

  5. Gerwin ten Brinke

    Hi,

    I’ve build a mashup which calls a sql webservice.
    When I return one row in the sql webservice everything works fine. When I try to return all rows (100) I receive the error: “The maximum message size quota for incoming messages (65535) has been exceeded. To increase the quota, use the maxReceivedMessageSize property on the appropriate binding element.”

    Is there a way to set the maxReceivedMessageSize property?
    In the smart office SDK we can instruct the application to accept a bigger result.

    With kind regards,

    Gerwin ten Brinke

    Reply
  6. Brunel Hervé

    HI,
    Thank to share these informations. I create a datagrid and i populate it with a M3 web service type SQL. Do you have an example with a combobox instead of a datagrid. I don’t know how to bind the collection return by the web service in a combobox ?

    kind Regard

    Herve Brunel

    Reply
    1. karinpb Post author

      Hi, you bind the collection to the itemssource and then you need to set the ValuePath to reflect the property with the value. I’m not sure what your response looks like. If you have a WS I can test. Send it to me!

      Thanks for reading.

      Reply
  7. Joakim Bodin

    This was very helpful! I had the same issue as the first poster regarding an empty DataGrid from an SQL result despite a successful test during configuration. His/her solution worked for me.

    Reply
    1. Joakim Bodin

      Thanks for the heads up! I’m not looking for a sample at this time, but I’ll keep an eye on your site.

      Reply
  8. Joakim Bodin

    Gerwin ten Brinke :
    Hi,
    I’ve build a mashup which calls a sql webservice.
    When I return one row in the sql webservice everything works fine. When I try to return all rows (100) I receive the error: “The maximum message size quota for incoming messages (65535) has been exceeded. To increase the quota, use the maxReceivedMessageSize property on the appropriate binding element.”
    Is there a way to set the maxReceivedMessageSize property?
    In the smart office SDK we can instruct the application to accept a bigger result.
    With kind regards,
    Gerwin ten Brinke

    karinpb :
    No I’m sorry. I will add it as a requirement. Thanks for your input!

    I have the same issue as Gerwin, is there a way to set the maxReceivedMessageSize property yet that I might have missed? If not, do you have any suggestions for a work-around?

    Thank you for your time,

    Joakim

    Reply
    1. karinpb Post author

      Hi,
      As of LSO 10.0.3 ( I think ) there is a new property WS.MaxReceivedMessageSize that you can set. The work around for other versions is to use Rest with XmlTemplate instead of WS. Check if maxReceivedMessageSize property is available for the DataPanel in the Mashup Designer.

      Regards
      Karin

      Reply
      1. Joakim Bodin

        I’m on version 10.0.4.0.22, but I can’t find or change the WS.MaxReceivedMessageSize property. I’m trying to set it as a mashup:DataParameter, is there another way to set it?

        Thanks again,

        Joakim

      2. karinpb Post author

        You have to enter in in XAML manually. I forgot to add it in the designer! It will be available in the type-ahead options in the next version….

      3. Joakim Bodin

        I can’t seem to replicate the error, so it looks like it’s working =)

        Thanks!

        Joakim

    2. thibaudatwork

      The workaround I used was TOP n in my SQL, where n is a limit, for example SELECT TOP 100 OKCUNO FROM OCUSMA, and for better usability I also indicated the number of records returned in the Mashup.

      Reply
    3. Joakim Bodin

      I couldn’t find/change the MaxReceivedMessageSize property in the designer, and I am unfortunately unable to use anything but a WS at this point.

      Adding “TOP 100” to my SQL queries got rid of the error message, but if I actually want to show all entries I still don’t have a solution for that.

      Many thanks to both of you for all the help!

      Reply
  9. Warren Hou

    How can I get the current value of a field from DataList of WS? Is it CurrentItem[Field Name]? I tried, it seems not working

    Reply
    1. karinpb Post author

      I’m assuming you mean the selected row in a list where you use the DataService list panel and have a list defined within the control i XAML?
      To access the data behind the selected row you can use CurrentItem or a binding that takes the name of the list and uses the SelectedValue of the list.
      That looks like this:


      The selected value (as well as CurrentItem) will give you the selected rows data. How did you define the columns? xxxx should be replaced by the property that has the data. I don’t know what kind of WS you are using, what kind of objects is it returning in the list?

      If you can share a screenshot of the result tab in tool://wstest, the column name would be the name of the property. Or give me more information about the WS, like the wsdl or a view of the result XML from within a tool called SOAP UI. Then I can probably help you.

      Reply
      1. Warren Hou

        Karinpb,

        Thanks a lot. I do not know how to paste screen shot here. Can you give me your e-mail address? I will send you the screen shot of my Mashup and the details to you.

    2. karinpb Post author

      Hi, You should try with the name of the property like: CurrentItem.FieldName or if you bind directly to the list it can look like this:

      The datagrid is a wpf DataGrid.

      Reply
  10. Warren Hou

    Thanks to Karin’s help with great patience. I have got what I need. Here is a summary.

    1. SQL type of web service is important to make useful Mashup, as it gives us great flexibility.
    2. The XAML codes for manually adding columns are as follows.

    1) The codes need to be placed after

    2) The property of AutoGenerateColumns needs to be set as “False”
    3) thibaudatwork made a great comment on binding to the result collection. Otherwise the result will not show.
    4) You can add the columns you want in any sequence.

    3. The way to get the current value of a column on the current row is
    {Binding ElementName=datagrid, Path=SelectedValue.Price_List}

    The elementname is not the datalist, but the datagrid. Price_List is the column name of your SQL web service result collection.

    Reply
  11. thibaudatwork

    Karin, I get the error “Unable to complete LstByNumber. There was an error calling the web service. The HTTP Request is unauthorized with client authentication scheme ‘Anonymous’. The authentication header received from the server was ‘Basic Realm=”M3_Test”‘.” Why is that? It’s a Web Service that uses the API adapter to call CRS610MI.LstByNumber. I tested the Web Service with SoapUI and it works fine there. I also hard-coded the user/password, and I also tried LSO.USER and LSO.PASSWORD. I also tried lws.user and lws.password (with L), as well as mws.user and mws.password (with M). I also tried with and without WS.CredentialSource=Current. I tried Smart Office 10.0.4.0.38 and Smart Office 10.0.5.0.38. Any ideas? Thanks. /Thibaud

    Reply
    1. karinpb Post author

      Why don’t you use the MI-panels instead? Why use the SOAP way? Using the MI panels are faster.
      The WS data service uses WCF and generates a Proxy. It seems like you have tried all the options (the different credentials etc). I’m not sure what the header means. So no ideas. Check the SDK API documentation for WSDataService. What does the example request look like in in SOAPUI? In some version of Lawson Web Services they requires a specific header to be set. (LWs.user) not in the soap header section but as a http header. But that was a long time ago. What is the version of LWS? (assuming that is what you are using).

      If all fails you should enter a support case. But you would need to gather lots of information, WSDL, Fiddlerlogs, Mashup etc. If would be best if we could access the Web Service or the Environment.

      Regards
      Karin

      Reply
      1. thibaudatwork

        Tjena Karin. Thanks for the reply. The reason to call M3 API thru SOAP rather than directly was pure training. I was teaching Mashups and DataService, and I needed a SOAP web service to play with. That Web Service server requires HTTP Basic Authentication, so there’s no SOAP Header, just a SOAP body. I will look at your suggestions and eventually I will open a case. Thanks. /Thibaud

  12. Frode Sollie

    Karin, snella 😉
    Can you please have a look at this?
    I have one provider with two webservices I need to use. One to search for companies, and one to request report for actual company found by search.
    https://www.bisgateway.com/brg/services/NRGCompanySearch?wsdl
    https://www.bisgateway.com/brg/services/NRGCompanyReportRating?wsdl

    Both works as a charm in SoapUI (Great testing tool), but not in “tool://wstest” 😦
    I can not see any difference on them that would caus this, but only the reporting one works in wstest, not the searching one. You don’t need any user og password, as it’s the load-part in wstest i thought maybe you could debug for me 😉
    Using them in jScript results in error on the search:
    ” Exception: System.ArgumentException: The contract (BRGService) is not available in this proxy.
    ved Mango.Core.DynamicWs.WSDataService.CallUsingDynamicProxy(WSDataServiceRequest request)
    ved Mango.Core.DynamicWs.WSDataService.OnDoWorkCall(Object sender, DoWorkEventArgs e)”
    I’m using M3 13.1, BE 15.1, ISO 10.1.0.0.38

    Reply
    1. karinpb Post author

      Hi,
      I have seen your message and I’ll try and have a look when I have time. Did you check the log file in debug?
      Also check the Fiddler log if there are any hints. Compare to Soap UI. Can you please give me the input you tested with?

      Reply
    2. karinpb Post author

      Hi,
      There is an exception when generating the Proxy for the search.
      2014-02-19 16:57:43 WARN Mango.Core.DynamicWs.DynamicProxyFactory.ImportMetadata Dynamic WS: There are metadata errors in the metadata conversion for http://www.bisgateway.com/brg/services/NRGCompanySearch?wsdl. Exception is Cannot import wsdl:port
      Detail: There was an error importing a wsdl:binding that the wsdl:port is dependent on.
      XPath to wsdl:binding: //wsdl:definitions[@targetNamespace=’http://www.dnbnordic.com/brg/NRGCompanySearch/wsdl’]/wsdl:binding[@name=’NRGCompanySearchSoapBinding’]
      XPath to Error Source: //wsdl:definitions[@targetNamespace=’http://www.dnbnordic.com/brg/NRGCompanySearch/wsdl’]/wsdl:service[@name=’NRGCompanySearch’]/wsdl:port[@name=’NRGCompanySearch’]

      I did check to see if I could generate stubs in C# and that worked fine so a work around would be do compile the stubs and the script into a dll file and load that (a script can be a dll as well). Or you can use SOAP UI to get the input XML and then use the rest service to call the service. I don’t know why the dynamic proxy cannot resolve the WSDL.

      Reply
      1. Frode Sollie

        I think we found the error now. The WSDL has duplicate elements… The same element (reservedForInternalUse) is repeated twice in line 211 and 216. So WSDataService is veryvery particular about validating things…
        Hopefully my provider will change the WSDL now, that would be a quick fix 😉
        Tusen takk Karin, for rask og god hjelp!! 😀

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s