Adobe Flash Builder for the iSeries Programmer, Part 3
November 4, 2011 Shannon O'Donnell
NOTE: The code accompanying this article is available for download here. In my previous articles in this series, I showed you how to use Flash Builder to create a customer information GUI. You learned how to add visual components such as input text boxes, drop down lists, and even a tab navigator control. In this article, I will show you how to make your GUI into a server client that talks to Websphere Application Server Express running on your iSeries, and I will show you how to exchange data with the iSeries DB2/400 database. I will also demonstrate how to use the data-binding wizards that are built into Flash Builder, as well as how to use a manually defined Web service function within ActionScript to communicate with the Websphere Application Server. You will also learn how to code a couple of simple RPGLE programs that will be the brains behind your Web service and will communicate with your DB2/400 data, and I will also show how to convert that data very quickly into Web services. As Web services, they will be responsible for taking your DB2/400 data and converting it into XML, which gets sent to your Flash Builder client. And, in what is becoming a bit of a broken record in all of my articles in this series, once again we have a lot of information to cover in a short amount of space, so let’s get started. Creating the Backend RPG and Files To get started, we need to create all of our backend code and database files to hold our sample code. You can, of course, use your own code and physical files, but it’s easier to follow along if you use the same ones I do. You can download all of the RPG and physical files you need for this article here. Once you get the downloadable zip file, go ahead and unzip the save file within it to somewhere on your PC that you can find again, and then open up a DOS command prompt. For most installations of Windows you can do this by simply typing “CMD” in the Windows Start/Run box. We will be FTPing the save file to your iSeries. You will need to change to the directory where you unzipped the save file to. You can also qualify all of the FTP commands with the absolute path to the save file, but I find it’s easier if you simply change to the directory where all of the data lives that you’re going to be uploading. If you’re not sure how to change to a different directory from Windows command prompt, take a look at the example in Figure 1. This assumes you unzipped the save file to a directory on your PC named “Customer” in the C: root.
In this example, when I open the command prompt window, I am in the C:Usersshannon directory on my PC by default. I use the “cd..” (Change Directory) command to drill down to the root directory, and then “cd Customer” to get to the Customer directory, the location I unzipped my save file to. FTP and Restore the Save File Now you will need to FTP the save file to your iSeries. If you do not already have a save file created on your iSeries that you can FTP this code to, go ahead and create one now using the following command from an iSeries command line: CRTSAVF FILE(QGPL/CUSTSAVF) Next, start an FTP session in your command prompt and upload the save file as shown in Figure 2.
To restore the library in the save file, use the following iSeries command: RSTLIB LIB(CUSTOMER) SAVLIB(CUSTOMER) DEV(*SAVF) SAVF(QGPL/CUSTSAVF) The data files we need for this article, CUSTP1 and CUSTP2, are already created for you and contain a small amount of data. Feel free to add some additional data to these files if you like. You will need to create the RPGLE programs, however, I want you to create Program Call Markup Language (PCML) source from each into a new IFS directory. The PCML file will be used by the Websphere Web Services Server so that it will know the structure of your RPG program(s) and build the service from them. From an iSeries command line, use the following command to create a new IFS directory named CustomerPCML: md '/CustomerPCML' Now create two RPGLE programs. WSGETCUSTN will return a multiple occurrence data structure of customer numbers that your GUI will use to present the list of available customers to the user. The second program, WSGETCUSTI, will accept a customer number as an input parameter and return a list of customer information about the selected customer. Make sure you have CUSTOMER in your library list, and then from a command line enter the following commands to create the two RPGLE programs required for this article: CRTBNDRPG PGM(CUSTOMER/WSGETCUSTI) SRCFILE(CUSTOMER/QRPGLESRC) PGMINFO(*PCML) INFOSTMF('/CustomerPCML/wsgetcusti.pcml') CRTBNDRPG PGM(CUSTOMER/WSGETCUSTN) SRCFILE(CUSTOMER/QRPGLESRC) PGMINFO(*PCML) INFOSTMF('/CustomerPCML/wsgetcustn.pcml') This will create two RPGLE programs, WSGETCUSTN and WSGETCUSTI, and compile them into the CUSTOMER library. In addition, it will create a PCML source file for each and place that into the IFS directory named /CustomerPCML. You can take a look at the PCML files created if you like by drilling down to the /CustomerPCML file using the wrklnk ‘/CustomerPCML’ command from your iSeries command line. There are also a couple of test programs included in the downloadable code, TSGETCUSTN and TSGETCUSTI, which you can use to call the Web service programs directly for debug purposes. Because you will eventually end up creating Web service programs that contain a lot of input/output parameters and a lot of those will be multiple occurrence data structures, it can be a real chore to test them. If you create a test stub for calling them from a command line on the iSeries, using the same I/O parameters as the called program, it’s a lot easier to test. Creating the Web Services and the Web Service Server Now it is time to create the Web service on your iSeries. You will need to ensure that you are at V5R4 or above of OS/400 and that you have the Websphere Application Server Express V6.0 or above already installed and configured. It is outside the scope of this article to step you through the process of doing that, but I will tell you that it’s not difficult to install or configure WAS Express. IBM‘s own documentation that comes with the Web Enablement Licensed Program package is very detailed and you should be up and running with it in just a couple of hours or less. The complete instructions for installing and configuring WAS Express 6.0 can be found here. Assuming that you now have Websphere installed and running properly, it’s time to configure a Web services server. First, make sure that you have the *ADMIN server running for HTTP server. If you’re not sure, use the WRKACTJOB command and then look for the ADMIN instances running under the QHTTPSVR subsystem. If it is not running, you can start it using the command: STRTCPSVR *HTTP HTTPSVR(*ADMIN) Give it a few minutes to start and then open a Web browser and go to the HTTP admin page by typing in the URL: http://youriSeriesIPAddress:2001. You should be prompted to log on to your iSeries and then you will be taken to the admin page. Go ahead and go to the link for IBM Web Administration. If you are on a different version of OS/400 than I am for this article (V5R4), then your version of the following Web pages may vary slightly, although the steps you go through should be pretty close. Click on the link to create a Web services server. You can either attach an existing OS/400 user profile to this server, or you can use the default user profile that came with Websphere. I created my own, but you should do whatever is most appropriate for your own shop.
Next, tell the server to create a Web service from your RPGLE program, as shown in Figure 4.
Then, attach the PCML you generated when you compiled the RPG program by specifying its location as shown in Figure 5.
Name the service (it uses the program name by default, which is what we will do for this article), and give it an appropriate description.
You will now be prompted to specify the input/output parameters of your service. Notice these are the same parameters that are on your *ENTRY PLIST of your RPGLE program. For the first service we create, returning the list of customer numbers, there will be no input parameters, so go ahead and change everything to output only.
Finally, note the port that the server is running on and all of the other server properties for future reference.
Now take a look at your newly created Web service’s Web Service Description Language (WSDL) file. To do that, open the Web service instance you just created and click on the “Manage Deployed Services” link. Locate your new service named WSGETCUSTN, click the radio box next to it to select it, and click the “view definition” link. This will open the service’s WSDL file for you. You will need to note that WSDL address because you will be using it in your Flash Builder project. Creating the Customer Detail Information Service Follow the same steps you just used to add the WSGETCUSTN service for customer numbers to the WSGETCUSTI RPGLE program to create a Web service of that name. The only difference here is that you need to make sure you set the iCustno parameter to an INPUT parameter and then set all of the remaining parameters to OUTPUT parameters. Name your service WSGETCUSTI.
Changing the Customer GUI to a Server Project If you have not already gone through the first tutorial in this series, you should do that first. You will need to build on what we created there for this article. So, assuming you have created the customer information GUI already, go ahead and open it now in Flash Builder. In my previous article, I showed you how to create an ArrayList that stored the data you loaded into the drop down control to allow the user to select a customer number. That data was hard-coded into the program and is not really a very practical way to store data. In addition, when the user clicked on a customer name in the drop down list, although they were then taken to the customer detail state, we did not load any of the input fields because we did not yet have any data to display. In this article, we are going to remedy all of those shortcomings. The first thing we want to do is retrieve the list of customer numbers from the iSeries and load them into the drop down list control. To do that, we will use the Flash Builder Data Binding wizard to bind the drop down control to the WSGETCUSTN Web service we created earlier. Binding to an iSeries Web Service In Flash Builder, locate the Data menu item at the top of the IDE and select the “Connect to Web Service” sub-menu item. You will be presented with a panel like the one shown in Figure 10. In the WSDL URI: parameter, you will need to enter the URL to the WSGETCUSTN Web service you created earlier. An easy way to get that URL is to go back to the HTTP admin Web page in your browser, click on the “Manage Deployed Services” link, and then click the “View Definition” link next to the WSGETCUSTN service. The WSDL file will be displayed in a new Web page, and you can then simply copy the URL from there to the wizard WSDL URL field. Keep all the defaults that the wizard fills in on that panel and click the Next button.
Once the wizard has finished introspecting the WSDL file, you will get a panel showing you the methods (Operations) that are available within the Web service. When you created the Web service on the iSeries, it automatically created two methods for you. One outputs the data in a standard XML format, and the other outputs the data in a format that is more HTML friendly in that it uses substitution characters for all of the < and > characters. This allows you to view the XML in a browser, for example, as if it were an actual XML file. However, that format is not very useful to our client. So just make sure that you ONLY select the method name that does not end with _XML. See Figure 11 for the method (Operation) that you should select. Click the Finish button, and the wizard will create the connection to the service for you.
Now that you have created a data binding to your WSGETCUSTN Web service in Flash Builder, we can now connect it to your drop down list control. To do that, click on the Design tab in Flash Builder and then click on the drop down list to select it. Look in the Properties panel on the right. Go ahead and blank out the data provider that is already in there from the last article; we will not be using an ArrayList here. For that matter, if you want, go back to the Source view and delete the entire ArrayList block of code, because we no longer need it. Back on the Properties panel for the drop down list control in Design view, click on the drop down arrow next to the data provider and select “Bind to Data”. When you do, you will see a panel like the one shown in Figure 12. Take all of the defaults here and click the OK button. You have now bound your Web service data to the drop down list control. We are not yet done with this part because what you also did here was to tell Flash Builder to automatically call the Web service and populate the drop down list control as soon as the program loads in the user’s browser. This might be what you want to do, but probably not. There are all kinds of problems with using that approach, including the fact that if the user has a slower connection to your iSeries, then it’s possible that the call to the service could time out before any connection has been established and that will throw an error to the user. A better way to do it is to call the Web service later. In our case, we are going to let the user decide when to load it. We need to do a few things to fix this. Go into Source view and locate the drop down list control source. You should see a “creationComplete” property. Delete that creationComplete including everything from creationComplete to the ending double quote symbol on that property. The next thing we want to do is add a new button to the screen. Go into Design view and drag a button onto the screen. Place it next to the drop down control, and then in the Properties for the button, set the label to “Refresh Customer Numbers” and set the Click property to getCustomerNumbers(). We will create the getCustomerNumbers() function next. Back in Source view, add the following lines of code: protected function getCustomerNumbers():void { CursorManager.setBusyCursor(); wsgetcustnResult.token = wSGETCUSTN.wsgetcustn(); } This function will be called when the user clicks the “Refresh Customer Numbers” button. It will set the cursor to the Busy Cursor symbol, which looks like a little clock with a spinning second hand, and then it will do a call to the wSGETCUSTN Web service you bound to earlier and return the result into the wsgetcustnResult variable that the data binding wizard automatically created for you. One last thing we want to do is to add a bit of code to the call to the Web service that the data binding wizard created that will tell it to execute another function when control returns back to the client after contacting the Web service. Locate this line of code: <wsgetcustn:WSGETCUSTN id="wSGETCUSTN" fault="Alert.show(event.fault.faultString + 'n' + event.fault.faultDetail)" showBusyCursor="true"/> And add this bit of code into it just after the id=”wSGETCUSTN” property: result="ResultOfWSGETCUSTNWebServiceCall(event)" Now when the call to the Web service completes, it will call the function named ResultOfWSGETCUSTNWebServiceCall. Strictly speaking, for this utility we do not need to have it call another function when it returns to the client. However, it is important that you know how to do this because very quickly you will want to add more functionality than what the data binding wizard can provide for you. This is a fairly painless way to do learn how to do that. Let’s go ahead and create the ResultOfWSGETCUSTNWebServiceCall function now. Add the following lines of code to your script: public function ResultOfWSGETCUSTNWebServiceCall(event:ResultEvent): void { ddlCustomer.prompt = "Please Choose a Customer Number"; ddlCustomer.enabled = true; CursorManager.removeBusyCursor(); } This function sets the first element of the drop down list control to the value “Please Choose a Customer Number”, enables that control, and removes the Busy Cursor icon, which tells the user that they can now do something. Now that you have a list of customer numbers from the iSeries database loaded into your drop down list control, you want to let the user select one of them and then go get the detail for that customer and display it to them. Calling a Web Service Manually Although the data binding wizard is an extremely useful tool for calling a Web service, there are times when you will want or need to call the service manually. We are going to do that to get the customer detail information. To call the WSGETCUSTI Web service manually locate the <fx:Declarations> section of your ActionScript and add the following bit of code: <s:WebService id="GETCUSTI" wsdl="http://myiSeries:10032/web/services/WSGETCUSTI?wsdl" showBusyCursor="true" requestTimeout="0" > <s:operation name="wsgetcusti" result="ResultOfGETCUSTIWebServiceCall(event)" resultFormat="e4x" fault="faultHandlerWSSend(event)" > <s:request> <s:param0> <_ICUSTNO>{CustomerNumberSelected} </_ICUSTNO> </s:param0> </s:request> </s:operation> </s:WebService> This will use the “WebService” function within Flash Builder to talk to a Web service manually. When you use one of these, you are responsible for telling it what functions to call when control returns to your program successfully, and when control returns to your program in a failure mode. In addition, you have to manually parse the returned data so that you can load it into your presentation fields on your various states. If you examine this code, you will see that it contains a parameter by the name of _ICUSTNO. If you go back and look at the WSDL for your WSGETCUSTI Web service, you will see that this is the name that was created for the *ENTRY parameter to your program. A little trick that I use to determine the exact spellings of the various field names created and the other names used by the service call is to use a free utility called SoapUI to test my Web service. You can find SoapUI online by Googling for it and it’s a very fast, easy install. Once you have it, create a new WSDL project within it, paste in the URL you used for the Web service call (see the code we just added for an example), and then test it. Then you can see the exact structure of the call to the service, and basically you can copy and paste that into your Flash Builder project when doing your own manual calls. SoapUI is an invaluable tool, so make sure you grab a copy of it. So let’s now create the ResultOfGETCUSTIWebServiceCall function to parse the returned data that we will then load into the input fields on the TabNavigator control on the CustomerDetail state. Add the following lines of code before any function definitions, but after the <![CDATA[ line. Note that some of these declarations and imports may already exist in your project from the original project we created in the first article in this series, so you will not need to add those again if it does: import mx.controls.Alert; import mx.events.FlexEvent; import mx.managers.CursorManager; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; [Bindable] private var CustomerNumberSelected:String; [Bindable] private var tempString:String; [Bindable] private var StartstringFound:int; [Bindable] private var EndstringFound:int; [Bindable] private var WebServiceCallResult:String; public function ResultOfGETCUSTIWebServiceCall(event:ResultEvent):void { currentState = "Customer_Detail"; lbCustomer.text = CustomerNumberSelected; tempString = GETCUSTI.wsgetcusti.lastResult; StartstringFound = tempString.search("<ns:_OFIRSTNAME>"); EndstringFound = tempString.search("</ns:_OFIRSTNAME>"); WebServiceCallResult = tempString.substr((StartstringFound + 16), (EndstringFound - (StartstringFound + 16))); firstName.text = WebServiceCallResult; StartstringFound = tempString.search("<ns:_OLASTNAME>"); EndstringFound = tempString.search("</ns:_OLASTNAME>"); WebServiceCallResult = tempString.substr((StartstringFound + 15), (EndstringFound - (StartstringFound + 15))); lastName.text = WebServiceCallResult; StartstringFound = tempString.search("<ns:_OADDR1>"); EndstringFound = tempString.search("</ns:_OADDR1>"); WebServiceCallResult = tempString.substr((StartstringFound + 12), (EndstringFound - (StartstringFound + 12))); address1.text = WebServiceCallResult; StartstringFound = tempString.search("<ns:_OADDR2>"); EndstringFound = tempString.search("</ns:_OADDR2>"); WebServiceCallResult = tempString.substr((StartstringFound + 12), (EndstringFound - (StartstringFound + 12))); address2.text = WebServiceCallResult; StartstringFound = tempString.search("<ns:_OCITY>"); EndstringFound = tempString.search("</ns:_OCITY>"); WebServiceCallResult = tempString.substr((StartstringFound + 11), (EndstringFound - (StartstringFound + 11))); city.text = WebServiceCallResult; StartstringFound = tempString.search("<ns:_OSTATE>"); EndstringFound = tempString.search("</ns:_OSTATE>"); WebServiceCallResult = tempString.substr((StartstringFound + 12), (EndstringFound - (StartstringFound + 12))); state.text = WebServiceCallResult; StartstringFound = tempString.search("<ns:_OZIP>"); EndstringFound = tempString.search("</ns:_OZIP>"); WebServiceCallResult = tempString.substr((StartstringFound + 10), (EndstringFound - (StartstringFound + 10))); zip.text = WebServiceCallResult; StartstringFound = tempString.search("<ns:_OITMNO>"); EndstringFound = tempString.search("</ns:_OITMNO>"); WebServiceCallResult = tempString.substr((StartstringFound + 12), (EndstringFound - (StartstringFound + 12))); itemNo.text = WebServiceCallResult; StartstringFound = tempString.search("<ns:_OITMDESC>"); EndstringFound = tempString.search("</ns:_OITMDESC>"); WebServiceCallResult = tempString.substr((StartstringFound + 14), (EndstringFound - (StartstringFound + 14))); desc.text = WebServiceCallResult; StartstringFound = tempString.search("<ns:_OITMQTY>"); EndstringFound = tempString.search("</ns:_OITMQTY>"); WebServiceCallResult = tempString.substr((StartstringFound + 13), (EndstringFound - (StartstringFound + 13))); quantity.text = WebServiceCallResult; } This function sets the currentState to Customer_Detail and then takes the XML data returned by the WSGETCUSTI Web service and stores it in the variable named tempString. We then use some simple parsing to extract the actual data values from each XML tag and load it into the various input controls on the Customer_Detail state. We use the length of the XML tag name and substring the data out of it based on that. If you used SoapUI to test your service, then you will also have seen what the returned XML data looks like and how it named your XML tags. I cannot overemphasize how useful the SoapUI utility is to the Flash Builder programmer who works with Web services! Finally, add this line of code after the previous function to add in the Fault function, which is called if the call to the Web service fails: private function faultHandlerWSSend(event:FaultEvent):void { Alert.show("Failed to connect to web service!" + event.fault.message); } This will display an Alert box to the user if the call failed. You can, of course, replace this bit of code with a more useful customized message or action. Try It! That’s pretty much it. You now have a Flash Builder client that retrieves a list of customer numbers from an iSeries physical file, allows the user to select one of those numbers, and then retrieves the customer detail information from another iSeries physical file, all using very easily created Web services and your original RPGLE code. Very cool, right? To use this in production, export the project as a release build (see the previous articles in this series listed in the Related Stories section below to learn how to do that), copy the release files to your iSeries, and turn your users loose on it. You have now seen just how easy it is to turn an existing RPGLE program into a Web service, how to test it (using SoapUI or using a test stub RPGLE program created for that purpose). and how to build a Flash-based Web program to interact with iSeries data. You have all the tools now to build your own production-quality Flash-based programs. In my next article, I will show you how to create a version of this same utility that runs on mobile devices. Shannon O’Donnell is a freelance author who has worked with the IBM iSeries for over 20 years. He can be reached via email here. RELATED STORIES Adobe Flash Builder for the iSeries Programmer, Part 2 Adobe Flash Builder for the iSeries Programmer, Part 1
|