You may have noticed that I’ve been rather absent from the blogosphere lately. I’ve been focusing on my transition to the Sybase/SAP Mobility team. That doesn’t mean I’ve abandoned my passion for PowerBuilder – that’s the tool that built my career, and it will always be a part of me. My day-to-day responsibilities are now with the Sybase Mobility stack, which includes Sybase Unwired Platform (SUP), Afaria, and the SQL Anywhere/MobiLink/UltraLite toolset. Therefore, the content that I publish in this blog will take a decided turn towards those tools. This entry will cover the steps necessary to use the Sybase Hosted Relay Server with your local install of SUP 2.1.
The iAnywhere Relay Server
The iAnywhere Relay Server is an optional (but critical, IMO) component of the Sybase/SAP mobile infrastructure. It’s essentially a reverse-proxy server that gets installed into a corporate DMZ, and implements an “inbound port” model to help insulate the server(s) inside the firewall from malicious internet traffic. It can be used with any of our mobile solutions, including SUP, Afaria, SQL Anywhere, and MobiLink. Mobile devices don’t need to be configured with the addresses/IPs/ports of the SUP server or farm – they only need the address and port of the Relay Server. The SUP server(s) make an outbound connection to the Relay Server using regular HTTP/S traffic, meaning that there are no open ports through the inner corporate firewall. Devices make an inbound connection to the Relay Server (again, HTTP/S on ports 80 or 443), and the Relay Server brokers the conversation between device and server, much like an old-time telephone operator at a switchboard. If the Relay Server is not implemented, the only other option to get devices and servers to communicate is to open specific TCP/IP ports for the SUP server through the inner and outer firewalls, and to configure the devices with the addresses of those SUP servers. I’ve not yet met a network admin that would readily agree to this…
The Relay Server is included free-of-charge with SUP, Afaria, and SQL Anywhere, and runs inside Microsoft IIS or Apache web servers.
The Hosted Relay Server
Sybase has created a hosted implementation of the Relay Server, which is available for free to registered Sybase/SAP customers. This is designed to allow for development and test of small mobile deployments – it’s not to be used for production implementations (for obvious reasons). The URL for the hosted relay server is: http://relayserver.sybase.com/account. The first step is to submit a registration request, which will be reviewed by Sybase. Once approved, there are three main steps to connect a remote device to the SUP server farm.
1. Register the SUP Farm details
At the bottom of the account details page, you’ll find the individual hyperlinks to create the different types of server farm registrations. We’ll be creating an SUP farm. The Relay Server communicates with an SUP server “farm”, even if there’s only a single server node registered in that farm. This makes it easy to add and remove nodes from the farm, without having to reconfigure any of the mobile devices out in the field.
Click “Add New Sybase Unwired Platform Farm”, and this brings up the page shown in Figure 1.
I’ve placed the Relay Server registration page over the Sybase Control Center page, so it’s easier to see how to fill out the form. Since this example will focus on the Hybrid Web Container, we’ll create a Message-based Synchronization (MBS) farm. I’m also registering a Replication-based Sync (RBS) farm, even though this example is going to focus solely on the MBS capabilities of the Hybrid Web Container.
Give the farm a unique name. As you can see, I’ve called mine PHoranXP. The Relay Server will automatically append a suffix to the farm id, based on its type. The full name of the farm is composed as Registration Name (dot) Farm ID + suffix. Message-based farms get the letters MBS added, so its official name will be PaulHoran.PHoranXPMBS. The Replication-based Sync farm will be named PaulHoran.PHoranXPRBS. If I’d created a DCN farm, it would have been PaulHoran.PHoranXPDCN. Bottom line: don’t forget the registration name or the suffix…
Lastly, enter the server node name within the farm. This is comprised of the SUP server name that appears in the Sybase Control Center, plus the string “SUPServer1″. Mine happens to be named phoran-d630, so the server node name is phoran-d630SUPServer1. Click Create Farm to register the details to the Relay Server, and create a long alphanumeric “token” value for each farm type. You’ll be asked for this value in step 2 below. If you ever forget this value (and who could be expected to remember it?), just click on the Configuration Instructions link, and you’ll be taken to a page that lists all the pertinent info for your registration.
The remaining steps are performed in the Sybase Control Center (SCC), so open a session to the SUP server now.
2. Point SUP to the Hosted Relay Server
The SUP server establishes an outbound connection to the Relay Server through a component called the Relay Server Outbound Enabler, or RSOE. SUP needs to know the connection details to the Relay Server, and they’re configured in the Sybase Control Center. This is configured in the main server properties panel of SCC, in the Relay Servers tabpage. Figure 2 shows what this looks like:
Click the New… button to open the first screen of the registration wizard. Fill in the following values:
HTTP port: 80
HTTPS port: 443
URL suffix: accept the default value
Description: your choice…
Click the Next button to navigate to the next panel in the wizard. This screen is a little more complicated, and is a two-step process. Figure 3 shows both of the wizard screens, already filled out.
We’re now working in the top half of the the second screen. Enter the full FarmID that was registered to the specific Relay Server/Type combination in step 1. Since I’m registering a messaging server, I’ll select Messaging in the Type field, and enter PaulHoran.PHoranXPMBS as the farm ID. Enter a description, if desired. Now click the yellow plus sign to register that combination. To create a second registration (for a Replication or DCN type), you would simply change the Type field to the desired value, change the suffix in the farm ID, and click the yellow plus sign again. If you make a mistake, simply select the row, and click the red X to delete it and start over.
Now we move to the bottom half of the screen, where you’re registering the server node within the farm ID and type. Select the appropriate row in the top list (I only have one, but you might have an MBS and an RBS row registered). Since this is a Master-Detail screen, it’s important to keep the relationships straight. Enter the Server Node ID and the corresponding Token value for that node in the corresponding fields, then click the BOTTOM yellow plus sign. (If it’s grayed out or disabled, make sure you have the correct row selected in the top half of the screen.) I’ve entered phoran-d630SUPServer1, and pasted in the correct token value from the Hosted Relay Server configuration page.
When everything is done correctly, your screen should look somewhat like the right side of Figure 3 above. Click Finish to record the registration and close the wizard.
3. Create the RSOE details
Figure 4 below shows how to find the panel for recording the properties of the Relay Server Outbound Enabler (RSOE) process.
Begin by clicking the yellow plus sign. This opens a three-panel wizard dialog for entering the properties, as shown in Figure 5 below. The good news is, this one is much easier to navigate. Select Messaging in the Farm Type field, and all the other properties will be automatically filled in.
The only modifiable options on the first screen are the port values. The RSOE process connects to the SUP server on port 5001, and out to the Relay server itself on port 80 or 443. Clicking Next> allows you to provide HTTP credentials (if necessary), and specify an HTTP proxy. The third and final screen allows for setting specific processing and logging options. These can all be modified later if necessary, so just accept the defaults and click Finish to complete the process.
The last step is to actually start the RSOE process. If registration was successful, a row should appear in the RSOE list. Select it, and click the Start button. Wait several seconds, then click the Refresh button to see the status of the RSOE process. The Status field should show as “Running”.
Configuring the SUP Container on Mobile Devices
Registering Users in Sybase Control Center
The first step in hooking up a device to SUP through the Hosted Relay Server is to register that username in SCC. This is done in the Application Connections tab of the Applications panel. Figure 6 shows the registration for my iPad device.
Note that the server name is set to relayserver.sybase.com on port 80 – not the default of phoran-d630 on port 5001. Set the Application ID to HWC to link the user to the Hybrid Web apps. Give the user an appropriate activation code, and click OK.
Configuring the Connection Properties on the Device
The SUP Hybrid Web Container for iOS 4.x/5.x can be downloaded directly from the Apple Appstore here. The first thing you’ll do after downloading and installing it, is configure the connection properties in the Settings app.
Server Name: relayserver.sybase.com
Server Port: 80 (or 443, if you configured the secure HTTP connection)
Farm ID: the entire farm ID, including the registration name and suffix – mine is PaulHoran.PHoranXPMBS
User Name/Activation Code: the username and activation code that were registered above.
Password: provide a 6-digit password, or leave this blank to force the user to reenter the HWC password each time the app is reactivated.
Once these connection properties are set, and the Hybrid Web Container is launched, a connection is made to SUP through the Relay Server to download all the registered Hybrid Web apps that have been assigned to the device user.
Here’s a trivia question for you… How long ago did Sybase release the PowerDesigner Plug-in for PowerBuilder?
Your answer would probably be, “What the heck is the PowerDesigner Plug-in for PowerBuilder???” It is, arguably, the least-discussed feature of both PB and PD, and yet, with the release of PB12.NET, it could become the most important tool in your PB toolkit. As developers look to migrate their existing (and often, aging) PB applications to PB12.Net and Windows Presentation Foundation (WPF), they may encounter the need for some level of refactoring. It may be necessary to “flatten” the object hierarchy by consolidating classes, or to better isolate the presentation layer (View) from the business logic (Controller) and the data access layer (Model). My colleague Yakov Werde has published a number of great articles and videos on the ups and downs of PowerBuilder migrations. You can find them all here.
As long time PB developers know, this type of work is never fun, due to some basic restrictions built into the PB IDE itself. For example, since PB always opens objects in “edit” mode, it cannot allow an ancestor class and one of its descendent classes to be open in the painter simultaneously. The refactoring process becomes a tedious and error-prone cycle of opening the descendent, copying code to the clipboard, closing the descendent, opening the ancestor, pasting in the code, closing… Oh, and don’t forget that the Classic PB requires an object to compile cleanly before it allows a save. And some tasks, like altering an object’s ancestry, can’t be done in the painter at all! These require the developer to resort to the “Edit Source” option, working directly with the PB source code! Not a task for the faint of heart (or the newbie)…
Most PowerBuilder developers are probably familiar with PowerDesigner and it’s industry-leading Data Modeling capabilities, and perhaps they even use it to generate a Data Dictionary for their relational database. But PowerDesigner has a depth and breadth far beyond basic data modeling that many are just not aware of… One of the primary features of the Developer edition of PD is the Object Oriented Modeling (OOM) tool. The OOM has the ability to reverse-engineer code modules into a UML compliant Class Diagram. It actually reads and parses the code, and generates all the UML artifacts into a PD model! Once the code is reversed into a Class Diagram, the developer can work with the model in a graphical drag-and-drop UI, and then forward-engineer the changes back into the code. PD can read and write a number of languages, including Java, Visual Basic, C, C++, C#, and (wait for it…), PowerBuilder! PowerDesigner can directly update the PowerScript inside PB Classic PBLs!
Let’s get started with a simple demo using the Examples app that ships with PB 12 Classic, and PD 15.2. One note: the PD plug-in is a free component, but you do have to have PowerDesigner (Developer or Studio editions) installed to get it.
The first step (after both PB and PD are installed) is to enable the plug-in inside the PowerBuilder IDE. Open PB, and select Tools > Plug-in Manager… This opens the dialog shown in Figure 1. Simply check PowerDesigner on and click OK.
Three new panels will appear in the IDE – the Class Browser and two “output” panels. Since these take up valuable screen real estate, I typically close the output panels and just leave the Object Browser visible. The output panels will automatically reappear when needed, so just close those for now.
The next step is to reverse-engineer the PowerBuilder code into an Object Oriented Model. The plug-in will add three new menu options to the Target context menu. Right-click on the Target and select Reverse Engineer.
This brings up a dialog that shows a treeview containing all the PBLs in the current target, and all the objects in each PBL. This lets you select the PBLs or individual objects that need to be reverse-engineered. Typically, one would reverse the entire target, but hundreds of classes can result in quite a messy diagram. If you know you’ll be working with a distinct subset of the target, deselect anything that isn’t required. Click OK to initiate the reverse-engineering process, and then go get a cup of coffee (or maybe even grab lunch?). The process can take several minutes, depending upon the number of objects being reversed, and the number of object references that are detected.
When the process finishes, there are two important steps that must be completed to “finalize” the linkage between the model and the PB target.
Save the OO Model to a .OOM file. Right-click the second node in the PD Object Browser (the one just below the topmost “Workspace” node) and select Save As… to save the .OOM file into a folder somewhere.
Now go back to the PB System Tree and right-click on the Target node (again, not the Workspace node). Select Plug-in Attributes… to open the dialog window shown below. Set the oompath attribute to the full path/filename of the .OOM file that was just created. Click OK to close this dialog.
You should immediately notice a couple of changes to the IDE:
The PD Object Browser pane will be populated with a set of folders, one for each PBL in the target.
Expanding a PBL node will display a set of subfolders:
Classes: This folder contains the shortcut to the object in the PBL, including PB system classes like checkbox, radiobutton, datawindow, etc. This is just like the PB System Tree.
Associations: This list shows the relationships between any object in the PBL and any other object it references, like controls or user objects on a window.
Generalizations: This folder contains an entry for every inheritance relationship, from a child to its parent. For example, since w_about_system is a direct descendent of w_center, then in OO terms, w_about_system is a generalization of w_center.
Dependencies: For PB applications, Dependencies are very much like Associations, but you’ll also see “influent objects” listed here. For example, w_add_sales_order has 6 commandbuttons and 2 datawindows, so those 8 entries are Associations. The PB system classes “commandbutton” and “datawindow” are both “influent object dependencies”.
You’ll also see an entry called “ClassDiagram_1″. This is the graphical representation of all the entries in the four folders listed above, for that specific PBL.
You’ll also see a new menu item in the PB system tree context menu – Find in Class Diagram. Choosing this item will open the OOM class diagram and select the class. Double-clicking a class in the PD diagram opens the object in the corresponding PowerBuilder painter. You now have the full depth and breadth of PowerDesigner’s Object Oriented Modeling tool at your disposal. Attempting to define all that functionality is clearly beyond the scope of this blog post (and probably would fill a book).
You have a full cross-reference of object classes and system classes, ancestry and descendent lineages, as well as the ability to view methods and event scripts! However, it’s important to remember that PowerDesigner is a software MODELING tool, NOT a software DEVELOPMENT tool like PowerBuilder. PD doesn’t allow you to compile code, check syntax, or deploy targets. And even though the integration between the model and the code is bi-directional, it’s not automatic. Changes made in one side are not automatically visible in the other.
To move a change from the model into the code, select Language > Generate PowerBuilder… from the PD menu.
To move a change from the code into the model, select Reverse Engineer… from the PB target context menu.
Let’s walk through a simple example. We’re going to change the ancestor of a window class and generate that change into PB. To do this in PB would require use of the Edit Source option, and a knowledge of the internal syntax of the PowerScript code that is usually hidden from the developer’s view. In PD, it’s a simple drag and drop!
In the PD browser, find the w_main class. It lives in the pbexamfe PBL. Right-click it and select Find in Diagram…
Press F7 to zoom out a bit, because you’ll need to see both w_main and w_center on the screen at the same time. w_center is in the same PBL, so it exists in the same diagram.
Select the Generalization tool from the PD toolbar (it’s the one that looks sort of like an organization chart). Then click on w_main and drag the arrow over to drop it on w_center. That class is now the ancestor of w_main!
* An alternative method is to use the Properties dialog, as shown below. When you first open the properties for w_main, you’ll see that the “Extends” property reads <None>. Click the button immediately to the right of that field (Select Classifier), then select w_center in the list of window classes. This has the same effect as dragging and dropping a Generalization link.
To generate the code back into the PBLs, select Language > Generate PowerBuilder… The dialog shown below is presented, which allows you to select or de-select objects. You can also have PD save a backup copy of any changed PBLs at this time. Since I knew that I’d only changed one object, I deselected everything except w_main. Click OK to start the generation process. Note: even if you only change a single object, PB will still initiate an incremental build on the target.
If you now open w_main in the PB editor, you’ll see that it shows as being descended from w_center. A simple example, I know, but think of how easy this would make the process of adding a “corporate” layer between the existing framework layers in a PFC-based application! You’d create new empty classes with the PFD_ prefix, create a generalization from those to their corresponding PFC_ layer components, then change the PFE_ components’ ancestry by dropping their existing generalizations from the PFC_ to the PFD_ layer.
To refactor a method or event script from one class to another, it’s as simple as dragging and dropping the operation definition from one classes’ Operations folder to another. That single step brings over the method signature, the parameters, as well as all the PowerScript! Once all the refactoring work is done and tested on the “Classic” side, the migration to PB12.Net and the Visual Studio Isolated Shell will proceed much more smoothly.
In summary, PowerDesigner and the PD Plug-in for PowerBuilder can be a fantastic addition to your PB tools arsenal, especially if you’re considering a migration to PB12.Net.
One of the overriding goals of this release was to give PB developers an easy migration path that would allow them to bring their tried-and-true Win32 PowerBuilder applications into the new and exciting world of WPF. While it’s unrealistic to assume that all PB apps, big and small, will migrate cleanly into PB12.NET with zero refactoring, there is one specific class of applications that will absolutely require some coding work to make the transition. These would be any applications that invoke SOAP-based web services, using the SoapConnection and SoapException extension classes. These have been deprecated in PB12.NET (don’t worry – they still exist in PB12 Classic!). They have been replaced with the new WCF proxies. However, the migration process does not automatically convert the existing web service proxy objects/projects into WCF proxy projects. But don’t worry, I’m here to help…
This post is going to walk through the process of migrating the existing Web Services sample app, which was featured in a previous blog post, into a WPF/WCF application. You’ll see that it’s relatively easy, and results in much cleaner and more streamlined coding.
Step 1: Migrate the existing target into PB12.NET
This step is very simple. Open PB12.NET, and create a new Solution (what we used to call a Workspace). This creates an empty .PBWX file in a folder of your choosing. Then click Add Target… This prompts you for the location of the an existing .PBTX (WPF) or .PBT (Classic) target file. If you installed the Sample Applications during the PB12 install, the target you want should be found in the folder C:\Documents and Settings\All Users\Documents\Sybase\PowerBuilder 12.0\Code Examples\Web Services.
Select the WS.PBT file. This target has NOT been migrated to PB12.NET, so the Migration Wizard will kick in.
Note that this is a non-destructive migration, unlike previous versions of PB. You’re asked for two locations – and the terminology used can be a little confusing. The first location, labelled “Target”, is actually the location of the existing Source – i.e., the code you’re migrating FROM. The second, labelled “New location and target file” is both the folder location into which PB will place the migrated code AND the new target name. The PBLs referenced in the “target” are not changed at all – the migrated code is placed into the “new” directory – PBL for PBL.
Walk through the remaining panels of the wizard, accepting the defaults, and you’ll get to the main PB12.NET IDE.
Step 2: Generate the WCF Proxies for the services
The old SOAP proxies are useless in PB12.NET. In fact, once we finish replacing the existing proxies with WCF proxies, you can even remove the Proxies.PBL from the target library list. None of the proxy or structure classes in that PBL will be referenced.
Select File >> New >> PB Library to create a new PBL that will store the new WCF Proxy classes. I’m calling mine WCFProxies.PBL.
Select File >> New >> WCF Client Proxy to start the Proxy wizard. You’ll have to do this for each of the old SOAP web service proxies, but let’s start with the Movie Times service.
I’ll name this project p_movietimes_wcfclient, and place it in the WCFProxies PBL.
Leave the NameSpace entry blank. Name the proxy assembly movietimes.dll, and place the proxy class into WCFProxies.PBL.
The next panel shows the list of services (there’s only one), and the data structures (there are several). Click Next, then Finish. This opens the Project Painter and loads p_movietimes_wcfclient
Now click the Generate Proxy button to create the proxy object class.
Do this for each of the web services that currently have proxies. I’ve gone ahead and completed this process and stored the zipped solution files here. Note: some of the web services are no longer active, so the code in the cb_invoke button of their corresponding visual user objects has been commented out.
What comes out: you’ll get WCF proxy objects in the WCFProxies.PBL. These are real PowerScript objects, with methods and events that you can open and inspect. You’ll also (optionally) get new additions into the References folder, for any structure classes that are called out in the WSDL. So, in your PowerScript, you’ll declare the proxy with the class type that appears in the PBL, and any structure references that appear will come from the References folder.
Step 3: Remove the deprecated SOAP client classes
The SoapConnection, SoapException, and SoapPBCookie classes are obsolete in PB12.NET. All of the processing that these classes used to perform has been incorporated directly into the WCF proxy class (which we generated in Step 2). All we need to do is delete these from the WS PBL, and then remove any references to them from the PowerScript code.
Remove the instance variable declaration for the SoapConnection class.
Remove all the code from the Constructor and Destructor events.
Remove both of the InstantiateService() methods. Select the method, then choose Edit >> Delete Function…
Step 4: Call the new WCF proxy classes
The uo_ancestor class used to manage all the SOAP client and proxy instantiation. Since none of that is necessary anymore, all we need to do is open every descendent of uo_ancestor, and fix the code in their cb_invoke::clicked event to instantiate and invoke the new WCF proxies. Again, let’s start with uo_movietimes.
Replace the local declarations and remove the call to InstantiateService()
s03_movieinformation on line 1 becomes movietimes_MovieInformationSoapClient (Intellisense really helps here).
s03_theatre on line 2 becomes movietimes.Theatre
Comment out the entire IF statement that calls InstantiateService() on line 13. Replace it with: px_Service = CREATE movietimes_MovieInformationSoapClient
Comment out the final END IF on the last line.
Fix a type casting exception that will be thrown at runtime (the C# compiler is much more strict than the PowerScript compiler!)
The local ANY variable response needs to be changed to an array declaration, since the proxy’s GetTheatresAndMovies() method returns an array of movietimes.Theatre structures. PowerScript allows an Any to hold literally any reference, but C# has strict rules about array to non-array assignments.
That’s really all the code that needs to change! I did go ahead and comment out the code that invokes the dead web services, since I couldn’t generate new proxies for them. And I changed the MovieTimes user object layout so that the Canvas element was nested inside a Grid with two RowDefinition. This makes the Resize logic completely unnecessary, as WPF handles resizing anything inside a Grid cell. But that’s the topic of the next blog post – no reading ahead…
The final step would be to perform a full Build, and a Deploy, then test out the Movie Times web service call.
Download the sample PB12.NET solution by clicking here.
The long-anticipated update to PowerBuilder is the 16th major release in 19 years, and that doesn’t count the dozens of maintenance releases and HUNDREDS of EBF patches. But this one is special… The last really substantial overhaul of the IDE came with the release of version 8.0, which introduced the System Tree, Workspaces and Targets, and tighter SCC integration.
The first thing you’ll notice when you install PB12 is that it actually contains TWO separate IDEs. PB12 “Classic” is essentially the same interface that we’ve known since version 8.0, and continues the tradition of allowing you to build Win32 client applications, .NET Winform and Webform apps, .NET Web Services and Assemblies, and JEE components for EAServer, JBoss, WebLogic, or Websphere. All of these are distinct “target” types and can be edited from a single Workspace in PB12.
PB12.NET is a second IDE that will contain two target types: WPF (Windows Presentation Foundation) applications and WCF (Windows Communication Foundation) client proxies. WPF is the new graphical rendering engine for Windows, and it replaces the aging Win32 GDI architecture. The language for designing the user interface layer of a WPF applications is called XAML (EXtensible Application Markup Language), which is an XML-based syntax. The good news is, PB12.NET actually generates the XAML for you interactively from the grapical design pane! (But it’s still a good idea to get familiar with the XAML syntax…)
PB12.NET leverages the Microsoft Visual Studio Isolated Shell, so it’s going to look a little different to veteran PB developers. It will look and feel like Visual Studio, but with the familiar PowerBuilder coding experience that we’ve all grown familiar with. You still write event and function scripts in PowerScript, and develop Datawindows, and your existing apps should migrate to WPF targets with little or no refactoring required.
It’s important to note that this is NOT a Visual Studio plugin – and there’s a big distinction. If PB12.NET had been constructed as a VS2008 plugin, you would have to purchase and install Visual Studio 2008 to run PowerBuilder! This is why we went in the direction of the Isolated Shell. Everything you need comes in the PB12 install – you do NOT need to install Visual Studio or Expression Blend to construct fully functional WPF applications.
Here’s a link to the Trial Download of PB12. Note that the GA version had an issue with our licensing software, which resulted in valid license files being rejected on machines that had participated in the Beta program at any time. This issue has been fixed, and the EBF release for that specific defect can be found at this link.
This really isn’t a Sybase-related post. This is about my first real exposure to the power of Social Media and Social Networking.
I mean, I’ve seen the Youtube clip of the flashmob that The Black-Eyed Peas organized for Oprah’s kickoff show. That was all organized through Twitter, and the event quickly evolved from a handful of artists to include well over 21,000 people. But last night really was the first time I was personally affected by this phenomenon, and I thought it important enough to blog about. It’s a real lesson in the way companies today are actively monitoring sources like Twitter and Yelp to continuously improve their customers’ experiences, and to protect and enhance their brand.
Now, it’s a Marriott, so the room was perfectly nice, and clearly up to the usual Marriott standards of excellence. And I’m absolutely NOT a complainer – you know the type… But I’d been looking forward to this trip, and being assigned this room was kind of a downer. So I logged onto Twitter and posted the following at about 11pm:
“At the Orlando Marriott World Center for RIM WES 2010. But I have the crappiest room in the hotel. Posting photos of the view in the AM.”
I then went to bed. I awoke to find the following letter slipped under my door sometime during the night:
Dear Mr. Horan,
Thank you for your continued loyalty as a Marriott Rewards member! Please accept my sincerest apologies for your current room situation on the 4th floor of the East Tower. Unfortunately, due to the large volume of check-ins, I did not have a room for you to move into tonight. I have setup a King Poolview…for you to move into on Monday, if you desire.
Zachary Long, Assistant Front Office Manager
Orlando World Center Marriott
Sure enough, I’ve been upgraded to a sweet room overlooking the pool on the 8th floor. And here’s the upgraded view:
Now, I have no proof that this upgrade was a direct result of my tweet. But I don’t think it matters – Marriott did the right thing by me, no matter what the catalyst was.
I think the lesson is clear. Companies that are serious about protecting and enhancing their Brand are listening to Social Media outlets to hear what people really think about them…
I hear this question ALL the time… And frankly, I think the question itself is a little misinformed! (Of course, I don’t say that to the person’s face…)
The first step is to find out what they’re really asking…
“Do you mean, you want your PB application to run inside a browser, so you no longer have to worry about client-side deployment?” (And yes, there are still people that think “.NET” means “runs in a browser”…)
If the answer is YES, we talk about the native WebForms capability of PB11.5. You simply create a Webform target and project object, click DEPLOY, and your existing PB app runs inside an Internet Explorer browser with IIS as the web server. Alternatively, there’s the Appeon for PowerBuilder utility, which supports additional browser clients and application servers.
If the answer is NO, then the next question is, “Are you saying, you want your PB application to run as a Microsoft Windows Forms app, so you can access the .NET framework classes directly, or use the Smart Client deployment features?”
If the answer is YES, I show them how PB 11.5 apps can be deployed as Winform applications, and how they can reference .NET assembly classes directly from their PowerScript code with conditional compilation blocks. And with the Smart Client deployment features, even the client-side deployment headaches are taken care of!
If the answer to that question is NO, then I follow up with, “OH! Do you mean you have PowerBuilder non-visual components that you would like to deploy as .NET assemblies or web services, so that they can be accessed from C# or VB.NET apps?” If the answer here is YES, then I proceed to demonstrate how easy it is to deploy .NET assemblies or web services right from PB 11.5, with absolutely NO code changes required. PB 11.5 even supports strong-named assemblies and Code Access Security standards to help ensure code integrity and security.
If I’m STILL getting a blank stare, then I have to ask, “I think I get it… You want to use Visual Studio as your IDE, and write code in C# or VB - but you’d rather not lose the functionality of the Datawindow…”
This is when I drop Datawindow.NET on them, but I have to explain that we’re now crossing the border from “migration land” into “rewrite land”. The datawindows that they’ve developed in their existing applications can be reused, and they’re a critical component of the presentation layer. It’s the business logic layer will need to be completely refactored into the new target language, and there’s no way around calling that a “rewrite”.
If they’re still not satisfied with my answer at this point, then it’s pretty clear what they’re after… A magic tool that will translate their PB applications into XAML, so they can take advantage of the new Windows Presentation Foundation (WPF) graphical rendering engine, and have their apps run as 100% managed code! This is certainly an important requirement, as WPF is the future of Windows user interface development and many IT departments are requiring full .NET compliance.
Well, there’s good news and bad news. The good news is, Sybase is developing a tool that can do this! The bad news is, it won’t be released for another few months, but the Beta program is still open and you can try it out today!
The bottom line is, you don’t need to “migrate” away from PowerBuilder to embrace .NET in your organization. PowerBuilder *IS* .NET!
Back in January, I showed how easy it was to consume SOAP-based web services with PowerBuilder 11.5. PowerBuilder supports the .NET web services engine, which allows PB apps to invoke very complex web services with very little of the detailed coding required for a similar C# or VB.Net application. The Web Services Proxy wizard parses the WSDL for the service, and automatically creates a proxy object and all the necessary structures and parameter definitions that represent the service’s operations and request/response structures. Once those are created, the code required to invoke a service and parse the resulting data is extremely simple.
This posting will look at the other side of the equation – using PowerBuilder to create and publish a .NET web service to an Internet Information Services (IIS) instance. We’ll also see just how easy it is to test and debug that service with the PB integrated debugger.
Taking it up a notch
Most of the web service examples I’ve seen have been really limited in functionality, and I’m sure you’ve seen them as well… For example, pass in a ticker symbol and get the current stock quote. Pass in a zip code and get the current time and temperature. YAWN!!! While those are good at teaching the basics of the SOAP plumbing, most “real world” web services are rarely that simple, and usually require a much more complex XML schema in the request or response message.
You basically have two options when dealing with very complex XML in a SOAP message:
Marshall the resultset into an XML document within the service, and pass it back as a single long string.
This forces the recipient to parse the response using MSXML, or the System.XML classes to extract the data they need. What you end up with is a web service with absolutely no “metadata” in the WSDL that can help the consumer understand the content.
Create a set of strongly-typed structures that accurately represent the data being returned.
This allows the .NET web services engine to create meaningful WSDL for the SOAP-based service. With this approach, the WSDL contains actual metadata about the request and/or response structure, and the consumer can better understand the nature of the data being returned.
This example will use the second approach, and return a complex, nested hierarchical resultset from a PowerBuilder .NET web service. We’ll use the “EAS Demo DB 115″ database that ships with PowerBuilder 11.5.
Step 1: Create the .NET Web Service target
You can use an existing workspace, or create a new one. Once you have that, select File > New and select the Target tabpage. We’re creating a .NET Web Service target from scratch, so select that path through the Wizard.
Create from scratch
Project name, Library location, and Target name: I selected OrdersByCustID_ws for mine.
Library search path: accept the default.
PowerBuilder object name: This is the NVO that will be created, and I called mine n_ordersByCustID.
Web Service virtual directory name: This is the folder location within your local IIS instance where the compiled service objects will be placed. I selected OrdersByCustID. The Web Service URL Preview is an .ASMX page that allows you to test your compiled service. We’ll use that later.
Resource file and directory list: This is where you’d list image files, 3rd-party .NET DLLs, etc., that your Web Service target will use. We won’t have any, so just click NEXT.
Win32 Dynamic Library File list: Again, we won’t be using any, so just click NEXT.
Directly deploy to IIS: The default server name is “localhost”.
Click FINISH, and you’ve got your target, project object, and nonvisualobject class created in your PBL.
Step 2: Design the Web Service
The first thing you ask is, “What question am I being asked, and what will the answer look like?” For this example, I designed a query that answers a very basic sales question: Show me the complete order history for a specific customer between any two dates.” The inputs to this question are the customer ID, and the desired From and To dates. The output will be a nested hierarchy that looks sort of like this pseudo-XML structure:
For a single customer, retrieve some basic customer information (ID, name, city, state, zip). For each sales_order whose Order_Date is between the From and To date, retrieve the Order_ID, and the Order_Line_Items. Here’s a picture of the datawindow painter SQL source for that query:
Now, I could take this “hierarchical”-style query, construct grouping levels in the datawindow and use the native XML properties of the datawindow to persist an XML representation of the resultset. But that would give me Option 1 above, a single string containing the XML data without metadata in the WSDL, and I don’t want that! What I’m going to do is create three Structure classes that represent the three “levels” of this query, Customer, Order and Order_Line_Item.
The Customer structure will have an attribute which is an array of Order structures, and the Order structure will have an attribute which is an array of Order_Line_Items. The public method in the nonvisualobject will take three input parameters (custID, fromDate, and toDate), and its return value will be the outer Customer structure.
Here’s a picture of the three Structure classes I’ve created. You can clearly see how I’ve nested an array of s_order_line_item within the s_sales_order structure, and an array of s_sales_order within s_orders_by_custid.
Step 3: Create the public method within the NVO
This will be the Operation that gets exposed in your WSDL, so choose a name for the method and its arguments that will give the consumer some indication of their meanings. I chose:
getOrdersByCustID( long al_custID, date ad_from, date ad_to) returns s_orders_by_custid
The code in that method is really pretty simple. I won’t recreate it here, but you can download the entire example here and review the code yourself. It basically just instantiates a datastore, retrieves the data, and moves the data row by row, column by column into a local variable that matches the output structure. The NVO also needs to connect and disconnect from the database, and I placed the code for this in the Constructor and Destructor events, respectively.
Remember that ANY public method or instance variable will be available to be exposed through the web service. If you create additional methods within the NVO (and my example has both an of_connect() and an of_disconnect() method), then these should be scoped as PRIVATE. The same goes for Instance variables. Make them Private, and if you want to expose them, create public Get/Set methods for them.
Step 4: Complete the Project Object and Deploy the Web Service
The most important section of the Web Service Project object is on the Objects tab. This is where you select the NVO and the methods in that NVO that will be exposed as part of the Web Service. In the future, you may have web service targets with multiple PBLs, containing multiple NVOs, but this example has one of each. In the Custom Class Objects treeview, select the n_ordersbycustid object. Then select the checkbox next to the getOrdersByCustID method in the list of available methods. Then save the changes to the Project object, and click the Deploy button. If all goes well, the output window should give you a success message when the service is deployed to IIS.
The Objects tab also allows you to view the WSDL that will be generated for your service (NOTE: some browsers handle this better than others – IE8 seems to work fine, but Google Chrome does not.) You can also click the Run Web Service button to launch a simple .ASMX page that allows you to test the service. Let’s do that now!
Step 5: Test the Web Service
Click the Run Web Service button. This launches the .ASMX page that shows the NVO name, and a link to any exposed methods within the class. It looks like this:
Click the link to getordersbycustid, and you’ll be prompted to enter values for the input parameters. You also see the WSDL, presented in the different available W3C versions. Now, the data in the EAS Demo DB is getting quite old, so you’ll find that order dates are back in the 2003-2006 timeframe. Use the following values:
Customer ID = 101;
From Date = ’2003-01-01′;
To Date = ’2004-12-31′
Then click Invoke to run the service. If everything works correctly, a new Internet Explorer window will open and present the XML result from the service. If that doesn’t happen, you can actually run the PowerBuilder debugger and step through the lines of code in the method as it’s running!
Open the debugger and place a breakpoint at some line of code inside the getOrdersByCustID method, say the point where it executes the Retrieve() function. Then click the “Start Debugger” button. Now go back to the ASMX page and re-click the Invoke button. The PB Debugger will stop on your breakpoint, and you can begin step-by-step debugging, examine local, instance, or global variables, set watch variables, etc…
Here’s a quickie for all you PowerBuilder developers that are currently evaluating the beta of PB.Net (aka PB12). Even though it’s not required to know XAML (because the WYSIWYG layout designer in PB.Net automatically generates it for you), it’s probably a good idea to get familiar with it. There will be times, as shown in my 3-part series on WPF and XAML development with PB.Net, where you may want to extend the graphical look-and-feel of your application, and you’ll have to do this directly in the XAML code itself.
Well, our good friends at Microsoft have created a little utility that lets you experiment with and validate your XAML code before you bring it over into your PB application. It’s called XAMLPAD.EXE, and it’s included for free in the .Net 3.5 SDK. If you have that version of the SDK installed, you can find XAMLPAD.EXE in the folder “C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\”.
Here’s a screen shot:
So you can use XAMLPad to do things like develop custom templates and styles outside of PB, before you copy/paste the code into the XAML pane of the PB.Net IDE.
In this third (and final?) chapter of the series, we’ll add the final four commandbuttons, flesh out the PowerScipt, then add some custom XAML tags to the presentation layer which will really add some sizzle to the finished Calculator application. The point of this exercise is to show how XAML and WPF allows you to separate the user interface design/implementation from the “business logic” code of a PB.Net application.
To refresh everyone’s memory, this series is implementing the sample Calculator application from MSDN, which can be downloaded here. If you’ve gotten this far, we have the basic interface designed, using a GridPanel and some rectangular WPF commandbuttons. Not quite the same look and feel as the MSDN sample, which uses rounded commandbuttons, with cool animations for the MouseOver! and Clicked! events.
Here’s a screen shot of the MSDN sample app in action (which you wouldn’t see unless you have Visual Studio installed):
OK, so that app has some cool extra functionality, like a MultiLineEdit that simulates a running “paper trail” of the calculation history, some extra function keys, and Memory, so a direct “line for line” comparison to our PB.Net version won’t be a decent metric of application size. It’s not even an exercise on how to program a calculator or a Finite State Machine in PowerBuilder. This is really an attempt to show that PB.Net will be able to do everything that the Visual Studio developers can do with XAML and WPF, and more…
Back to the PB application: I’m not going to bloat this posting with the gory details of every event and method call. If you want to peruse the code, you can download the .Zip file containing the finished application here. But I will cover the XAML concept of Styles and Templates, which allow the PB developer to customize and extend the default GUI behavior. How about some rounded CommandButtons with a gradient background, and some animation to top it all off??
Step 1: Add the final four CommandButtons (BKSP, CE, C, and Enter)
The buttons we’ve placed into the application so far have been standard visual userObjects, descended from the CommandButton class. This allowed us to write some PowerScript into the ancestor classes, which are inherited by each instance that was dropped onto the gridPanel. These final four commandbuttons will just be regular WPF commandbuttons, each with one line of PowerScript in their clicked! events.
Select CommandButton from the Toolbox palette, and drop one each in the second row of grid cells (technically, Grid.Row=”1″). Stretch them to fit, and set their Margin and TextSize properties to match the other buttons in the grid. Their text property, reading from left to right, should be “BKSP”, “CE”, “C”, and “Enter”, and their names should be set to something indicative of their purpose – I named mine “cb_bksp”, “cb_clearentry”, “cb_clearall”, and “cb_enter”.
Now go into the Script window for each of them, and add the following lines of PowerScript.
//if we have only one operand, just exit
if parent.ib_eraseDisplay then Return
We defined the window functions that are called by these event scripts in Part II of this series. One nice new feature of PB.Net is that your PowerScript doesn’t have to compile cleanly before it can be saved, so if you haven’t already defined those window functions, you can go back and add them later! (You just can’t do a Full Build until they exist…)
Step 2: Define the custom templates and static resources in the XAML
Custom templates are used to define a standard look and feel for an object with a <Style> tag. Once a standard Style is defined, it can just be referenced from your controls, and they’ll “inherit” that behavior. It’s very similar to the way HTML works with Cascading Style Sheet definitions. These go into a section of the XAML called Resources, which can be “bound” to any container or control, depending on the desired scope of that reference. Since everything in our sample exists within the outer GridPanel, my XAML will create them as Grid.Resources. This example will be demonstrating three changes to the GUI of the calculator.
Using ellipses as buttons, instead of plain old rectangular commandbuttons;
Adding a nice shaded gradient to the background of those ellipses;
Adding an animation effect to the “MouseOver” and “Clicked” events using a StoryBoard tag;
Position the cursor in the XAML pane, right after the opening <Grid> tag on line 2, and start typing the <Grid.Resources> tag. Everything we’re adding goes inside this tag. With the shell’s Intellisense feature, all you need is the following key sequence:
<GR (tab) .RES (tab) > then enter.
Now you have a full <Grid.Resources></Grid.Resources> tag with room to enter more stuff inside.
The first thing we’ll add in there is the Storyboard tag, which defines the animation effect, and binds it to the ellipse control (which we’ll add in a moment.) That code looks like the following, and you can just copy and paste right inside the <Grid.Resources> tag.
In a nutshell, this creates a Storyboard named “playStoryboard”, which animates an Ellipse named “TB”. It will shrink both the height and width by 10 pixels for .25 seconds, then return them to their original values.
Next, we’ll create the Ellipse style with its gradient background, and the event triggers that link to the Storyboard animation. This looks like a scary bit of XAML, but it’s not all that difficult to understand. This goes right after the closing </Storyboard> tag. Place the cursor there and hit Enter to get a blank line, then paste the following code:
All this code does is define a Style named “Btn”. The style defines an Ellipse named “TB” with RadialGradient properties, within a single-celled GridPanel (because all WPF controls must exist within a container). It then defines the event triggers for the MouseOver and Clicked events, and binds the clicked event to the Storyboard we defined earlier. The TargetType attribute of the Style says that this Style can be linked to by any control that is a “pbwpf:CommandButton”. Whew!
Step 3: Bind the CommandButton controls to the new Style resource.
The next step is to tell the CommandButtons to use the new Style. That is done by simply adding the Style attribute to the CommandButton definitions in the XAML. Go into each <pbwpf:CommandButton> tag in the XAML pane, and add the following attribute:
You can copy that to the clipboard and just paste it anywhere inside each pbwpf:CommandButton definition. Watch the Design Layout automatically change as each tag is altered! Also, note that we haven’t written any PowerScript code in the Clicked or MouseMove events! This should help illustrate the inherent separation of concerns between the XAML-based presentation layer and the PowerScript-based business logic layer.
Since we didn’t implement all of the functionality of the original MSDN example, a true line-by-line comparison isn’t really meaningful. But I’m going to tell you anyway. My example in PowerBuilder.Net comes in at a svelte 94 lines of code, compared to 498 lines of C# in the MSDN sample. I’m confident that, even if I took the time to implement the remaining functionality, I wouldn’t add 400 more lines of code…
Of course, this is Beta software, and I am seeing some Beta strangeness… For example, the buttons inside the grid should resize themselves as the grid cells expand, and they’re not. Also, the animation in the mouse click is not returning the buttons to their original dimensions… At this point, I’m not sure if it’s my XAML or an issue inside PB.Net, but I’ll certainly follow up as newer builds become available. Feel free to post any thoughts or comments…
Oct 7, 2009
Follow-up: I’ve solved the animation problem where the commandbuttons were returning to a circle instead of an ellipse. If you move the Width and Height properties from the <Grid> tag to the <Ellipse> tag, you’ll see the correct behavior. Here’s an excerpt of the revised XAML, so you can see what I’ve changed.