Sie sind auf Seite 1von 48

Calling WCF web services

from PowerBuilder Classic


through a proxy web
service
BRUCE ARMSTRONG
ABOUT THE PRESENTER
POWERBUILDER CLASSIC AND
WCF

Agenda CREATE THE WCF PROXY SERVICE


TEST THE SERVICE
CALLING THE SERVICE FROM
POWERBUILDER CLASSIC
About the Presenter

 Development lead with Integrated Data Services since 2004


 Independent consultant for 15 years doing PowerBuilder development for companies such as Rockwell, Hughes, Boeing,
Western Asset Management, Investment Technology Group and Johnson & Johnson.
 Using PowerBuilder since version 1.0.B
 Charter member of TeamSybase (formerly TeamPS)
 PowerBuilder MVP
 SAP Mentor
 Contributing author to SYS-CON's PowerBuilder 4.0 Secrets of the Masters.
 Editor of SAMs' PowerBuilder 9: Advanced Client/Server Development
 Contributed numerous articles to the PowerBuilder Developer's Journal (PBDJ) and the ISUG Tech Journal
 Editor-in-chief of PBDJ from 2004 to 2013
 Served as a technical editor for the ISUG Tech Journal
 Has done sessions on PowerBuilder at most TechWaves since 2004 and at other user group meetings, including meetings in
Columbia, Germany, Belgium, Switzerland, England, Italy and France.
WCF Web Services

 Microsoft introduced WCF web services in .Net 3.0 to provide support for a
much creater variety of SOAP services.
 In particular, WCF web services:
 Can be accessed through protocols other than HTTP, such as TCP or named
pipes.
 Added support for web services standards such as WS-Security, WS-Addressing,
WS-ReliableMessaging, WS-Coordination and WS-AtomicTransaction.
PowerBuilder Classic and WCF

 PowerBuilder Native's support for web services is that it is based on


ASP.Net Web Services. That technology, introduced with .Net 1.0, doesn't
provide support for anything other than simple XML over HTTP SOAP
services.
PowerBuilder Classic and WCF

 One option for accessing WCF from PowerBuilder Classic, which I included
in a PBDJ article from January of 2012, is to create a WCF client using
PowerBuilder.Net or Visual Studio.Net, packaging that up in a .Net
assembly, and then exposing the assembly to PowerBuilder Native as a
COM object via COM Callable Wrappers.
 That technique works, but it requires either:
 Configuration of the desktop (requiresadding the assembly to the GAC and
creation of registry entries)
OR
 A side-by-side assembly deploy and creation of manifest files to support registry
free COM (rather complicated)
PowerBuilder Classic and WCF

 What we're going to look at today is another approach for consuming


more advanced web services from PowerBuilder Native, but one which
won't require desktop configuration or complicated manifest creation.
 What is will require is a web server inside your firewall that you can deploy
a proxy WCF service on.
 The proxy web service can be created using either Visual Studio.Net or
PowerBuilder.Net. The sample uses PowerBuilder.Net
Create the WCF
proxy service
 For this example, we're going to use the Amazon Web Services Product
Advertising API as the more complicated web service that we can't
access directly from PowerBuilder Native.
 Amazon requires custom headers -- including signing of the request – that
PowerBuilder Classic doesn’t support.
 It’s based upon the example that I used in the PBDJ article from 2012.
 We're going to use very similar code, so I won't explain what all of it does. You
can refer back to the 2012 article for that explanation.
 What I will explain though are the differences between the code we're going to
use for this example and the code from 2012.
 Create a WCF Service target
 When you get to the page in the
wizard that asks you for the object to
create, select Custom Nonvisual and
give it a name
 Although the "Finish" button on the
wizard is enabled at this point I would
recommend clicking "Next" instead
until you get to the Hosting Options
page.
 On that page, select "Console ("Self-
Hosted") application rather than the
default of IIS.
 Now click "Finish"
Create a WCF Client Proxy target

 We'll come back to the WCF Service in


a bit. The next thing we want to do is
create a WCF client proxy in the WCF
Service target.
 When you get to the page that asks
for the service URL (at least at the time
of the writing of this presentation) we
need to do a bit of a hack.
 What you would normally do here is
enter the URL that the Amazon Web
Services WSDL is located
 The problem isn't with PowerBuilder. It's
actually with the Amazon Web Services.
 The bug that was filed on the problem back in
2011 is available here, and it also discusses the
solution. I'm not quite sure why Amazon hasn't
fixed it yet.
error CS0030: Cannot convert  The problem is that what Amazon says it's
type 'AmazonService.AWSECommerceServiceAPI.ImageSet[]' going to return in the WSDL for the ImageSets
to 'AmazonService.AWSECommerceServiceAPI.ImageSet'
property of an Item and what it actually
does return are two different things.
 Perhaps Java or other web service clients
handle that gracefully, but C# doesn't.
 If you create a proxy directly off Amazon's
WSDL and call the ItemSearch method, what
you'll get is a .Net exception.
 The solution is to download the WSDL
to your workstation, open it in an
editor (I recommend Notepad++) and
then change line 584 as shown at left
Change:
 Now reference that file on your
<xs:element name="ImageSets" minOccurs="0" maxOccurs="unbounded">
workstation rather than the original
To:
Amazon WSDL in the web service
<xs:element name="ImageSets" minOccurs="0" maxOccurs="1">
client proxy wizard.
 Once you've created the proxy project, run it
to create the WCF client.
 The project will end up generating something
like 11 different non-visual user objects.
 That's because the Amazon WSDL contains
11 different service binding, one for each of
the country websites ( CA, CN, DE, ES, FR, IN,
IT, JP, UK and US ). There is also a default one
set to US.
 If you're in the US, keep the first one and
delete the rest. If you're in a different country
use the one that is for your country and delete
the others.
 We need a data type to use to return the
data from the proxy service.
 I created a custom non-visual user object
called n_item and added the following as
instance variables on that object as shown at
left
string Title
string Author  I defined them all as strings because that's the
string Publisher data type that Amazon uses to return them.
string ISBN
string
string
PublicationDate
Price
 I'm using a non-visual object rather than a
structure here.
 For purposes of returning data from a web
service, non-visual object handle
representations of nulls better than structures
do.
 On the non-visual object that was created by the WCF Service wizard
create a function that uses the WCF client to talk to Amazon and then
returns the data to PowerBuilder Native
 Call the method of_search
 For this example, don’t pass any arguments
 Return an array of the n_item non-visual object
Now for some code

 You'll need to adjust the sample based


on what namespace you used for your
WCF proxy and WCF service.
string _accessKeyId = "XXXXXXXXXXXXXXXX“
string _secretKey = "YYYYYYYYYYYYYYY“
string _associatetag = "ZZZZZZZZZZZZZZZ“

string _responseGroup[]
 I used "wcfclient" for the client and
wcfclient.ItemSearch _itemsearch "TeamSybase" for the service.
wcfclient.ItemSearchRequest _itemsearchrequest
wcfclient.ItemSearchRequest _isr[]  I also renamed the non visual object
created by the WCF client project to just
wcfclient.ItemSearchResponse _itemsearchresponse
TeamSybase.awsecommerceservice _client
"awsecommerceservice". The original
name generated by the WCF client
System.Collections.IEnumerator _itemsenum
System.Collections.IEnumerator _itemenum
wcfclient.Item _item project was quite long.
wcfclient.Items _items

You’ll also need to replace the first three


TeamSybase.n_item items[]

integer index = 1
variables with your own Amazon Web
Services information
 You'll need to sign up with their web
services program for the first two items
and their associates program for the
third.
 Once you've signed up with their web
services program, you'll find the
access key and secret key under
Security Credentials
 You get the secret key when you
generate the access key
 Your associate tag is located in the
upper right hand corner of your
Amazon Associates account page.
 As will be noted a bit later, I changed
the ResponseGroup for the request form
"Small" (2012) to "Medium" (today).
// create a WCF Amazon ECS client  That's because in 2012 I was just returning
_client = create TeamSybase.awsecommerceservice titles and today I want more information (
authors, publisher, publication date, etc) that
// Configure the client
_client.wcfConnectionObject.EndpointAddress.URL =
required getting more data back in the
"https://webservices.amazon.com/onca/soap?Service=AWSECommerceService" service response.
_client.wcfConnectionObject.BasicHttpBinding.Security.SecurityMode =  The medium response exceeds the default
PBWCF.BasicHttpSecurityMode.TRANSPORT! size that WCF allows for the size of the
_client.wcfConnectionObject.BasicHttpBinding.MaxReceivedMessageSize = message (65,536 bytes).
1000000
 I've increased the size of
MaxReceivedMessageSize significantly to
allow for the additional data.
 As just noted, in 2012 I used "Small" for
responseGroup because I only
wanted the titles. I'm using "Medium"
// prepare an ItemSearch request
_itemsearchrequest = create wcfclient.ItemSearchRequest
now to get more data.
_responseGroup[1] = "Medium“
_itemsearchrequest.SearchIndex = "Books“  As a result of changes that Amazon
_itemsearchrequest.Title = "PowerBuilder“
_itemsearchrequest.ResponseGroup = _responseGroup made in you must provide them with
_itemsearch = create wcfclient.ItemSearch
_isr[1] = _itemsearchrequest an associates tag in every Product
_itemSearch.Request = _isr
_itemSearch.AWSAccessKeyId = _accessKeyId Advertising API web service request.
_itemsearch.AssociateTag = _associatetag
_client.wcfConnectionObject.SoapMessageHeader.AddMessageHeaderItem(&
"AWSAccessKeyId",&
"http://security.amazonaws.com/doc/2007-01-01/",&
_accessKeyId,&
PBWCF.WCFHMAC.NONE!,&
"")

_client.wcfConnectionObject.SoapMessageHeader.AddMessageHeaderItem(&
"Signature",&
"http://security.amazonaws.com/doc/2007-01-01/",&
_secretKey,&
PBWCF.WCFHMAC.HMACSHA256!,&
"")
SetPointer ( HourGlass! )  Once again I'm returning more data in
Try
// issue the ItemSearch request
this example than I did in 2012, so
_itemsearchresponse = _client.ItemSearch(_itemsearch); we're using a type to collect the data
// write out the results
_itemsenum = _itemsearchresponse.Items.GetEnumerator()
and accessing more attributes.
do while _itemsenum.MoveNext()  Because I'm trying to keep this simple,
_items = _itemsenum.Current I'm only pulling the first author for a
_itemenum = _items.Item.GetEnumerator()
do while _itemenum.MoveNext() book if there are multiple authors.
_item = _itemenum.Current
items[index] = create TeamSybase.n_item
items[index].Title = _item.ItemAttributes.Title
items[index].Author = _item.ItemAttributes.Author[1]
items[index].Publisher = _item.ItemAttributes.Publisher
items[index].ISBN = _item.ItemAttributes.ISBN
items[index].PublicationDate = _item.ItemAttributes.PublicationDate
 The ListPrice may be null.
 Not checking for the null that and
if not IsNull ( _item.ItemAttributes.ListPrice ) then trying to assign the value directly will
items[index].Price = _item.ItemAttributes.ListPrice.FormattedPrice
end if cause a runtime exception
index = index + 1
loop
loop
catch ( System.Exception e )
items[1] = create TeamSybase.n_item
items[1].Title = e.Message
end try

return items
 Now that we've defined the method for our web service we need to go back
to the web service project object that was created by the wizard.
 When the wizard was first run, there were no methods on the non-visual object
and so there was nothing to expose in the service. Now we have a method to
expose
 Check the checkbox in front of our new method so the project knows what to
expose.
 I've also give the service an alias of "Search" that is a bit more web service
friendly than "of_search".
 I should probably also alias the web service name as well, but haven't done so
for this example. Nor have I changed the target namespace, which you
should do for a production web service.
 There's a few other things I've customized
here, and you'll need to do the same.
 For changes to the service attributes
(that apply to all of the operations) you'll
use the Service Attribute button to bring
up that dialog to select those options
 For changes to the operations attributes
(which only apply to specific methods of
the service) you'll use the Operations
Attribute button and it's resulting dialog.
 Under Service Behavior attributes, I've
enabled IncludeExceptionDetailInFaul
ts.
 I've done that primarily for debugging
purposes. Once you've got the web
service working and want to move it to
production you'll want to turn that off.
 I've also checked the XmlSerializerFormat option.
 By default, WCF services use the DataContractSerializer.
 ASP.Net web services (what PowerBuilder Native uses for web services)
uses the XmlSerializerFormat and can't properly handle the response from
the DataContractSerializer.
 The XMLSerializerFormat option in WCF allows us to create a WCF service
that uses a serializer that older technology clients like PowerBuilder Native
can understand.
 Under Operation Attributes,
the OperationContract Name attribute is
set for us by the project when we alias
the name of the method.
 What I've done in addition to that is set
the STAOperationalBehavior attribute.
 That forces the method to be invoked
under a single threaded apartment (STA)
threads. By default, WCF uses multi-
threaded apartment (MTA) threads.
 The only reason we need to do this is
that, for reasons I'm not clear on,
PowerBuilder.Net includes references
to WPF classes when you create a
The calling thread must be STA, because web service proxy.
many UI components require this.  Since WPF is UI technology, if you don't
select this options you'll get a .Net
exception
 Do a full build on the WCF Service.
 Once you've done that, you can go
back into the WCF Service project, go
to the Run tab, and point the
"Application" property to the
wcfservice_host.exe file that got
deployed with the build.
 Right click on the WCF Service project
and select "Run" whenever you want
to run the service. It will run in a
command prompt window.
Test the service
 We’re going to use SOAPUI (the open
source version) for that, both because
it makes generating a "client" for the
new service very easy and because it
will show us any exceptions being
thrown by the service.
 Create a new project in SOAPUI,
select Add WSDL, and then copy the
WSDL information from the WCF
service project into the WSDL Location
prompt in SOAPUI
 SOAPUI will create a sample request
for you automatically.
 Run that, and you should get data
back
 If not, resolve the errors shown from
the returned exception.
 Assuming all is good, it's time to use
this from PowerBuilder Classic.
Calling the
service from
PowerBuilder
Classic
 Create a new workspace and a new target of type application in
PowerBuilder Native.
 Create a new window (lets say w_main).
 Add the following to the open event of the application:
Open ( w_main )
 That's the bare minimum of a PowerBuilder Native application.
 We’re going to a Grid Datawindow
that uses our new proxy web service.
 Select New -> DataWindow -> Grid
 For source, select Web Service
 On the next page in the wizard, enter
the same value for the WSDL that you
gave SOAPUI earlier.
 On the next page, there should only
be one service, select it.
 Now you need to select the method
you want to call.
 Choose the Search method (or
whatever you called it).
 On the next page you'll need to select
what data the datawindow is going to
show.
 In this case there's only one option, the
result set from the service.
 Select it and hit Next.
 Continue through the remainder of the
wizard and PowerBuilder will create the
DataWindow for you.
 You can preview it and it should display
the data we saw in SOAPUI.
 Drop that new datawindow onto the w_main
window. Just to make things simple we're only
going to add two more lines of code. In the
open event of the window add this:
dw_1.Retrieve()
 And in the resize event of the window add this
dw_1.Resize ( newwidth - 100,
newheight - 100 )
 We're done. Run the app and you should see
the data being returned from the Amazon
web services into our PowerBuilder Native
application.
Questions?

Das könnte Ihnen auch gefallen