Creation and Consumption of Web Services with PowerBuilder Web services support has been significantly enhanced in PowerBuilder.NET Creation & Consumption of Web Services with PB PowerBuilder 12.5 introduced a number of significant enhancements to web services support, both for creation and consumption. In particular, the following were introduced as new features in PowerBuilder.NET: • WCF client proxy • WCF service target • REST client proxy We’re going to look at what those new features provide and how to use them. We’re also going to look at how we can package some of that functionality so that it can be used from PowerBuilder Classic applications as well. Windows Communication Foundation (WCF) First though, some background. When support for a web service client was first introduced in PowerBuilder 9, it was done based on an open source library called EasySOAP. There were some limitations with that implementation, primarily because the EasySOAP library only supported SOAP 1.1, was limited to XML over HTTP transport, and provided no support for ancillary web services standards such as WS-Security. The situation was improved considerably when a web service client based on the .NET Framework classes (System.Web.Services) was introduced with PowerBuilder 10.5. That updated the support for SOAP to version 1.2, although it was still limited to XML over HTTP for transport. In addition, support for ancillary web services standard such as WSSecurity was still lacking. Such support was available for Visual Studio, but only through an add-in that PowerBuilder could not take advantage of. PowerBuilder 11 provided some of the same advances in terms of web service creation with the introduction of a .NET web service target type. One particular disadvantage of that implementation was that it required IIS to be installed on the developer’s local machine and deployed the web service to it during development. With PowerBuilder 12.5, web services support has been updated to the Window Communication Foundation that was introduced to .NET in version 3.0. Basing the web services support on these new set of classes (System.ServiceModel) provides a number of important advantages: • Support for bindings other than HTTP • Support for other message formats (e.g., JSON) • Support for WS-Security and other WS-* standards (WS-Policy, WS-Addressing, WS-SecureConversation, WS-Trust, WS-ReliableMessaging, WS-AtomicTransaction, WS-Coordination) • Supports self-hosting of services (no need to install IIS on development machines) Representative State Transfer (REST) One of the things that SOAP-based web services have drawn fire for recently is their complexity. When things like message security are important (e.g., WS-Security), SOAP provides significant advantages. But many people found the complexity of SOAP too cumbersome and have instead opted for implementing web services through REST. REST web services are accessed through standard URIs using standard HTTP methods (POST, GET, PUT, DELETE) and return data in Internet standard media types (typically XML or JSON). Because REST is an architecture, not a protocol (like SOAP), there are no official standards for it. One particular disadvantage of REST is that while there are machine readable methods that can be used to describe REST web services such as WSDL 2.0 or Web Application Description Language (WADL), most REST web services rely on human readable descriptions. That makes it a bit problematic for a tool like PowerBuilder to automatically generate client proxies for such services. Creating a WCF Client Proxy in PowerBuilder.NET Now that we’ve covered the background, let’s see how we can use these new features. We’ll start with the WCF Client Proxy. For this example, we’re going to create a client for the Amazon AWSECommerceService web service.[1] Note that you will need an account with Amazon Web Services to call the service, so you should go to their website and create one before trying these samples on your own.[2] The first thing we will do is create a WCF Client Proxy Target from the New dialog (see Figure 1). The first step in the wizard then prompts us to give our new project a name and indicate a library we want to store it in. The second step is to provide the wizard with the URL for the WSDL file used to describe the service (referenced below). In the third step of the wizard we provide a namespace for the generated proxy classes and indicate the assembly where the .NET generated code will be stored as well as the PowerBuilder library where the PowerBuilder generated client will be stored. After this, we will be shown the one service described by the WSDL and the data classes that PowerBuilder will end up generating. While there are additional optional steps in the wizard, the Finish button is enabled at this point and we can simply use it to close out of the wizard. Next, generate the actual proxy and supporting .NET assembly by running the proxy project you just created. What you should see as a result is a proxy object in the PowerBuilder library with the methods supported by the proxy (see Figure 2). What you will also see is an assembly that was automatically added to the References for the target that provides the supporting .NET classes for the proxy (see Figure 3). Note that unlike the web service client in PowerBuilder Classic, the data classes created by the WCF client are stored in the supporting .NET assembly, not in the PowerBuilder library. For this particular sample, I’ve added the WCF proxy to an existing WPF target, and will be adding a window to that WPF target that will use the WCF proxy. The first thing I’ll do after creating the new window is add a reference to the supporting assembly to the Usings portion of the window to make it easier to use the classes it contains (see Figure 4). Because the generated data classes contain .NET array classes that support the IEnumerator interface but do not provide access through an indexer, I’ve added a reference in Usings to the Systems.Collection classes as well so I can use them in the new window to loop through those arrays. Listing 1 provides the code I used to call the web service to search Amazon and then populate a listbox with the data returned by the service. Note that I’ve dummied up the accessKeyId and secretKey values; you will need to provide the ones that Amazon assigned to you when you registered to use their web services before the sample will work. Some portions of the sample bear some explanation. As indicated earlier, one of the advantages of moving to WCF as the basis for the web service client was support for ancillary standards such as WS-Security. The Amazon Web Services do in fact require that you provide your accessKey as an extra header item, that you sign the request using your secretKey, and that you use transport security to send the message. Those requirements are easily met using the WCF proxy. After we’ve created the client proxy, we enable the transport security Amazon requires as follows: _client.wcfConnectionObject.BasicHttpBinding.Security.SecurityMode = PBWCF.BasicHttpSecurityMode.TRANSPORT! To add your accessKey as an additional header item, do the following: _client.wcfConnectionObject.SoapMessageHeader.AddMessageHeaderItem(& “AWSAccessKeyId”,& “http://security.amazonaws.com/doc/2007-01-01/”,& _accessKeyId,& PBWCF.WCFHMAC.NONE!,& “”) And finally, to sign the message with your secretKey, do the following: _client.wcfConnectionObject.SoapMessageHeader.AddMessageHeaderItem(& “Signature”,& “http://security.amazonaws.com/doc/2007-01-01/”,& _secretKey,& PBWCF.WCFHMAC.HMACSHA256!,& “”) Also note that the proxy is self-contained. Unlike the web service clients that we would generate in PowerBuilder Classic, we don’t have to import any PBNI extensions into our library or use any system classes (e.g., SoapConnection). Everything we need to call the web service was generated by the proxy and can be used directly. Creating a WCF Service Target in PowerBuilder.NET Now let’s see how to create a WCF Service. The first thing we’ll do is select the WCF Service target type from the New dialog (see Figure 5). The first step of the wizard will ask you if you want to create a new WCF Service, or if you want to migrate over an existing PowerBuilder Classic .NET Web Service target. We’ll select the new client option. The second step will ask you for the project name, the library name, and the target name. The third step will allow you to modify the library search path to include other libraries. In the fourth step, you’ll assign a name to the .NET assembly that the target will eventually create. In the fifth step of the wizard, you’ll choose the type of object that will initially be created with the target and will give it a name. You can specify “(None)” if you want to skip this step. We’re going to select what probably makes the most sense, “Custom Nonvisual,” and give it a name (see Figure 6). Note that the Finish button has been enabled since the second step of the wizard. We’re going to keep going for a while because there’s one more option we want to customize. In the sixth step, we can include additional resources or resource folders. The seventh step allows you to add references to Win32 dynamic link libraries. The eighth step is the one we’re looking for though. Here we get to choose whether the application will be deployed to our local IIS server or generated as a console “self-hosted” application. We want the second option (see Figure 7). Having selected that option, there is a ninth step in the wizard that allows us to specify the name of the executable that will run the self-hosted application as well as the base address and the service URL. At this point, we can hit the Finish button to complete the project. Now we want to open the nonvisual object that was automatically created and code a method on it (see Listing 2). For this sample, I’m connecting to the Xtreme Sample Database 2008 database that appears to have been installed on my system as part of Visual Studio 2008. You might want to use the sample database provided with the SQL Anywhere 12 install as part of PowerBuilder 12.5. Note that, like a .NET Web Service application in PowerBuilder Classic, I can use the global SQLCA transaction object in my custom nonvisual. I don’t have to create a local copy of a transaction object like I needed to do for some of the earlier EAServer-based methods of deploying web services. Also note that I’m connecting and disconnecting from the database within the method. In a “real” application, you would typically use an ADO.NET connection to the database with connection pooling enabled, so that the connect and disconnect would simple acquire and return a connection from the connection pool rather than making and breaking an actual connection to the database. I’ve created a ‘d_grid’ DataWindow that retrieves the list of employees from the sample database. What I then do is transfer that data to an array of structure using the Object.Data property. To facilitate that, I’ve created a ‘DW2STRUCT’ utility that reads a DataWindow and creates a structure that matches the result set definition. The sample code for this article, as well as a number of other PowerBuilder code examples are available at: https://docs.google.com/#folders/0B7FWlsMaOo12NWIyODU4ZjktMWYwZS00MTI3LThlYmEtOTRkMGYwNjJkZjVk. There is a PB11 version of that utility available on Sybase’s CodeXchange, and a 12.5 version that I’ve updated to support PowerBuilder.NET that I will make available shortly. What the function is returning is an array of that structure. Note that one of the recent enhancements in PowerBuilder, at least in PowerBuilder.NET, that made this possible is the ability to directly return arrays from methods. At this point we need to open the deployment project that was initially created because we need to update it so that it knows to expose the method we just created (see Figure 8). Note that we can also give the web service method a more conventional CamelCase method name when we generate the service. One other change I’ll make to the project is to add a reference to the executable that launches the web service in a console to the Run tab (see Figure 9). As a result, when we select the Run option for this target, the web service hosting environment will be launched (see Figure 10) and referencing the WSDL location in a browser will return the WSDL from the running service (see Figure 11). Creating a REST Client in PowerBuilder.NET What we’ll look at next is creating a REST Client in PowerBuilder.NET. For this sample, we’re going to use some of the services from Flickr.com.[3] Once again, as with the Amazon web services, you’ll need to create an account with Flickr and obtain and apiKey before the samples will work for you. What we’re going to use specifically is the photo search method, which takes two arguments: our apiKey and the text we want to use to search for photos. Once again, we open the New dialog and this time select REST Client Proxy (see Figure 12). The first step of the wizard will have us give the project a name and pick a library to store the project in. The second step is where things get interesting. It requests a service method URL and a service method type (GET, PUT, POST or DELETE). With regard to the service method type, think of the four basic methods of CRUD operations (create, read, update and delete). By convention, the four service method types correspond to those four CRUD operations: • GET = Read • PUT = Create • POST = Update • DELETE = Delete Since we’re doing a read operation, we’ll use GET for our service method type. For the service method URL, we need to ensure that we include argument names in {} brackets in the URL at the location where we want to provide arguments at runtime. If we neglect this step, the proxy will end up creating a method with no arguments that is fixed for the one call we used to create the proxy. http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key={apikey}&text ={text}&format=rest The third step is interesting as well. Remember earlier I mentioned that few REST services actually provide a machine-readable method of describing themselves. To compensate for that, PowerBuilder gives you three different methods of defining the data structure that will be returned by the service: • Skip the step and use primitive types • Provide a schema or sample data that PowerBuilder will parse to determine the data structure • Provide an existing .NET assembly that contains the data types to expect Unfortunately, PowerBuilder doesn’t provide an explicit option to handle a WSDL 2.0 or WADL URL to describe the result data set for those methods that do provide one. In any event, we’re going to use the second option and provide some sample data from calling the method. The way we get that data is to simply use the URL referenced above with appropriate arguments. This particular method will return XML to our browser, which we can then cut and paste into the appropriate location on the wizard (see Figure 13). In this step in the wizard, we also provide the name of the assembly where PowerBuilder will generate the .NET classes it will use to handle the response from the service. In the fourth step of the wizard, we’ll tell PowerBuilder what format to expect the data in, since REST services can provide it in a number of different formats. Since this particular method returns XML, we’ll choose that option. We also need to indicate which of the classes that PowerBuilder generated from the sample data we consider to be the overall response data type. For this example, PowerBuilder found the following data types in the sample data: • restclient.rsp: The overall response type • restclient.rspPhotos: The array of photos matching our query with the overall response type • restclient.rspPhotosPhoto: An individual photo within the above array The class names are taken from the tag names used in the XML response. Select the restclient.rsp class as the response data type. The fifth step of the wizard allows us to specify the namespace for the proxy, give the proxy a name, and indicate whicch library it will be stored in. While the wizard has additional steps, we’ve provided enough for it to get started and can hit the Finish button so the proxy project is generated. After that, deploy the project so that the actual proxy is generated. Similar to the WCF Client Proxy mentioned earlier, PowerBuilder will create an assembly and automatically reference the one that contains the supporting classes (including the data type classes we’ll work with) as well as a proxy object in our PowerBuilder library. Also, as with the WCF Client Proxy sample earlier, I’ve added this proxy to an existing WPF application, and will now create a window that uses that proxy. Listing 3 contains the code for this particular sample. One thing it references is a DataWindow called ‘d_grid’ that I’ve created to display the results that contains two columns: title (a string of 256 characters) and URL (a string of 1024 characters). Only the title shows on the visible surface of the DataWindow; the URL is used internally later in the code sample to display the picture. Once again, note that like the WCF Client proxy, the REST Client proxy is selfcontained. Everything we need to use to call the service was generated by the proxy object. We just instantiate the proxy class and call methods on it. Some parts of the sample code bear explanation. The REST service we call limits the amount of hits returned in order to keep the size of the response small. It operates in a “paging” fashion where we can call it subsequent to the first call to retrieve additional “pages” of responses. The service returns two important pieces of information in this regard in the message header: photos.total – the total number of photos that matched our search and photos.perpage – the maximum number of responses returned in single request. That means that the number of photos that was returned to our initial call is the smaller of those two values, which becomes important as we attempt to loop through the data and display it. Once we know how many results we have, we loop through them and populate the DataWindow reference above with the title and the URL for the photo. Flickr stores its images in a URL schema as follows: http://farm{farm number}.static.flickr.com/{server name}/{photo id}_{photo secret}.jpg All of the information we need to construct that URL was returned by the service; we just need to assemble it together to get the actual URL. Note that the service doesn’t return actual photos to us; it just returns the data we need to construct a URL to reference the image. How do we display the photos now that we have the URL though? Since this is a WPF application, we can take advantage of the System.Windows.Controls.Image class from the.NET Framework. Its Source property takes a System.Windows.Media.ImageSource class as an argument, and the System.Windows.Media.Imaging.BitmapImage class (which is a descendant of ImageSource) accepts a URI as a constructor. As a result, we have Listing 4 that we add to the RowFocusChanging event of our DataWindow. It takes the URL from the row that just got focus, converts it to a URI, passes that to the BitmapImage class, and then assigns the result of that to the Image class, which then displays the photo in the window. Using a PowerBuilder.NET WCF Client in a PowerBuilder Classic Application All this is great for our PowerBuilder.NET applications, but what if we want to use some of this new functionality in our PowerBuilder Classic applications? Do we need to wait for the next major release to make that possible? Well, fortunately, the answer is no. Because PowerBuilder.NET can generate .NET assemblies, and because PowerBuilder Classic targets can access .NET assemblies either through conditional code blocks (.NET targets) or COM Callable Wrappers (Win32 targets), we can create assemblies in PowerBuilder.NET that take advantage of these new features and then reference them in our PowerBuilder Classic applications. Because the use of .NET assemblies is fairly straightforward in PowerBuilder Classic .NET targets, for this example I’m going to focus on using a WCF Client generated in PowerBuilder.NET in a PowerBuilder Classic Win32 target. The technique involves five steps: 1. Create a WFC client proxy 2. Wrap that in a .NET assembly target 3. Expose some methods of the WCF client proxy 4. Expose the COM Callable Wrapper (CCW) of that .NET assembly 5. Reference that CCW from the PowerBuilder Classic application We already saw how to create a WFC Client proxy for the Amazon web services earlier in this article. I’m going to use that same proxy, but this time instead of adding it to a WPF target I’ll be adding it to a .NET assembly target. In the nonvisual object in that .NET assembly target I’ll add a method called of_productsearch and populate it with almost the exact same code we used in our WPF sample (see Listing 1). The primary difference is that the keyword it will search from will be changed to an argument, and instead of populating a listbox I’ll populate an array of type String and return that from the method. Once I’ve done that, I can update the project object in the target so that it knows to expose that new method in the generated assembly (see Figure 14). The way we’re going to expose this assembly so that our PowerBuilder Classic Win32 application can see it is through a COM Callable Wrapper (CCW). For this sample, what we’re going to do is add the assembly we’re generating here to the Global Assembly Cache (GAC) using GACUTIL from the Windows SDK. We’ll also use the REGASM utility from the .NET Framework to generate the COM entries the PowerBuilder Classic Win32 application will use and add them to the registry. In order to add an assembly to the GAC, the assembly must be signed with a strong name. Therefore, we will also update the project in our .NET assembly target so that it signs our assembly with a strong name when it generates it (see Figure 15). One problem we’ll encounter when we attempt to add our assembly to the GAC is that it references the assembly that was created in support of the WCF Client. Not only must our assembly be signed, but so must every assembly it references and PowerBuilder doesn’t provide us with an option to sign that assembly during creation. Fortunately, there is still a way we can sign it. The Windows SDK provides a utility called ILDASM that can be used to disassemble an assembly back into an Intermediate Language (IL) file. Another utility called ILASM from the Windows SDK can be used to recompile IL back into an assembly, and that utility has command line options that allow us to sign the assembly with a strong name during that recompilation. All we need to do is use the strong name file that PowerBuilder generated when we signed our assembly and feed it to the ILASM utility so that it will sign the proxy assembly with a strong name as well. In our sample, where our WCF proxy assembly is called amazonservice.dll and the strong name file that PowerBuilder created for our wrapper class is called amazonwrapper.snk, we can strong name sign the WCP proxy assembly as follows: “C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\x64\ildasm.exe” /all /out=amazonservice.il amazonservice.dll ilasm /dll /key=amazonwrapper.snk amazonservice.il Now we can add both our wrapper assembly and the WCF proxy assembly to the GAC using GACUTIL: C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\NETFX 4.0 Tools\x64\gacutil.exe” /i amazonservice.dll We use REGASM to create the registry entries our PowerBuilder Classic Win32 target will use: regasm amazonwrapper.dll /regfile:amazonwrapper.reg I’ve taken the option here to create a registry entry file that we will run separately to add the entries to the target computer. Once the assemblies have been loaded into the GAC and the registry entries created, our PowerBuilder Classic Win32 application can access them easily through OLE Automation (see Listing 5). Conclusion What we’ve seen in this article is how web services support has been significantly enhanced in PowerBuilder.NET through the support for WFC Client proxies, WCF Service targets and REST Client proxies. We’ve also seen how we can use PowerBuilder.NET’s .NET Assembly target capability to make such functionality (particularly WCF and REST clients) available to PowerBuilder Classic targets. References 1. http://webservices.amazon.com/AWSECommerceService/AWSECommerceService.wsdl 2. http://aws.amazon.com/ 3. http://www.flickr.com/services/api/ About the Author Bruce Armstrong is a development lead with Integrated Data Services (www.get-integrated.com). A charter member of TeamSybase, he has been using PowerBuilder since version 1.0.B. He was a contributing author to SYS-CON’s PowerBuilder 4.0 Secrets of the Masters and the editor of SAMs’ PowerBuilder 9: Advanced Client/Server Development. brucea@sys-con.com (Listings 1–5 are on the following page.) Listing 1: Using the WCF Client string _accessKeyId = “XXXXXXXXXXXXXXXXXXXXXXX” string _secretKey = “YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY” string _responseGroup[] ItemSearch _itemsearch ItemSearchRequest _itemsearchrequest ItemSearchRequest _isr[] ItemSearchResponse _itemsearchresponse awsecommerceservice_AWSECommerceServicePortTypeClient _client IEnumerator _itemsenum IEnumerator _itemenum Item_item Items_items // create a WCF Amazon ECS client _client = create awsecommerceservice_AWSECommerceServicePortTypeClient // Configure the client _client.wcfConnectionObject.EndpointAddress.URL = “https://webservices.amazon.com/onca/soap?Service=AWSECommerceService” _client.wcfConnectionObject.BasicHttpBinding.Security.SecurityMode = PBWCF.BasicHttpSecurityMode.TRANSPORT! // prepare an ItemSearch request _itemsearchrequest = create ItemSearchRequest _responseGroup[1] = “Small” _itemsearchrequest.SearchIndex = “Books” _itemsearchrequest.Title = sle_1.Text _itemsearchrequest.ResponseGroup = _responseGroup _itemsearch = create ItemSearch _isr[1] = _itemsearchrequest _itemSearch.Request = _isr _itemSearch.AWSAccessKeyId = _accessKeyId; _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! ) Try // issue the ItemSearch request _itemsearchresponse = _client.ItemSearch(_itemsearch); lb_1.Reset() // write out the results _itemsenum = _itemsearchresponse.Items.GetEnumerator() do while _itemsenum.MoveNext() _items = _itemsenum.Current _itemenum = _items.Item.GetEnumerator() do while _itemenum.MoveNext() _item = _itemenum.Current lb_1.AddItem(_item.ItemAttributes.Title) loop loop catch ( System.Exception e ) MessageBox ( “Exception”, e.ToString() ) end try Listing 2: Sample WCF Service code DataStore lds integer li_rc long ll_rows employees emp[] SQLCA.DBMS = “ODBC” SQLCA.AutoCommit = False SQLCA.DBParm = “ConnectString=’DSN=Xtreme Sample Database 2008’,DelimitIdentifier=’Yes’” connect ; lds = create DataStore lds.DataObject = ‘d_grid’ li_rc = lds.SetTransObject ( sqlca ) ll_rows = lds.Retrieve() emp = lds.Object.Data disconnect ; destroy lds return emp Listing 3: REST Client sample code int i int count int row string url restclient.rspPhotosPhoto photo TeamSybase.n_restproxy lnv_proxy lnv_proxy = create TeamSybase.n_restproxy restclient.rsp resp resp = lnv_proxy.GetMessage ( “XXXXXXXXXXXXXXXXXXXXXXXX”, sle_search.Text ) if resp.photos.total > resp.photos.perpage then count = resp.photos.perpage else count = resp.photos.total end if for i = 1 to count photo = resp.photos.photo[i] url = “http://farm” url += photo.farm.ToString() url += “.static.flickr.com/” url += photo.server.ToString() url += “/” url += photo.id.ToString() url += “_” + photo.secret + “.jpg” row = dw_hits.InsertRow ( 0 ) dw_hits.Object.title[row] = photo.title dw_hits.Object.url[row] = url next Listing 4: REST Client sample code used to display images string url System.Windows.Media.Imaging.BitmapImage bi System.Uri uri Try url = this.Object.url[newrow] uri = Create System.Uri(url) bi = Create System.Windows.Media.Imaging.BitmapImage(uri) image_preview.Source = bi catch (System.Exception e) //ignore the error end try Listing 5: PowerBuilder Classic Win32 sample calling WCF Client exposed through CCW integer li_rc, li_index, li_count oleobject loo string products[] loo = Create oleobject SetPointer ( HourGlass! ) li_rc = loo.ConnectToNewObject ( “amazonwrapper.n_amazonwrapper” ) products = loo.ProductSearch ( “PowerBuilder” ) li_rc = loo.DisconnectObject() li_count = UpperBound ( products ) FOR li_index = 1 TO li_count lb_1.AddItem ( products[li_index] ) NEXT