Kbase P83659: How to consume Web Services from 4GL ?
Autor |
  Progress Software Corporation - Progress |
Acesso |
  Público |
Publicação |
  05/08/2009 |
|
Status: Verified
GOAL:
How to consume Web Services from 4GL ?
GOAL:
How to access a Web Service from within a 4GL procedure ?
GOAL:
Is it possible to use Web Services with Progress 4GL ?
FACT(s) (Environment):
OpenEdge 10.x
All Supported Operating Systems
FIX:
OpenEdge 10 has native 4GL support for Web Services. In order to access a Web Service via 4GL, you will need to follow these general steps:
Use the WSDL Analyzer to provide HTML documentation on the interface that the WSDL describes. This documentation includes 4GL sample code to use.
Based on the documentation of the WSDL Analyzer develop your 4GL application which accesses the Web Service.
Note: For in-depth information on how to access a Web Service through 4GL, please refer to the "OpenEdge Development: Web Services" documentation.
For a quick overview on how to connect to a Web Service, please take a look at the following sample 4GL code which retrieves the latest (20 minutes delay) stock quote of a stock ticker. This Web Service is available at http://www.swanandmokashi.com/HomePage/WebServices/StockQuotes.asmx and uses Document/Literal as the WSDL's Style/Use. The 4GL code to access this Web Service is also available in the %DLC%\src\samples\webservices\4GL Client\StockQuotes directory for the OpenEdge 10.0B03 or later installation.
Note: This Web Service has been removed from the Internet. Please use these steps as merely guidelines to access a Web Service. Publicly available Web Services can be found at www.xmethods.net .
Run the WSDL Analyzer within a Proenv window: bprowsdldoc http://www.swanandmokashi.com/HomePage/WebServices/StockQuotes.asmx DOC
Take a look at the index.html document inside the DOC directory which has been created by the WSDL Analyzer.
By clicking on the link in the Port types section you will get to the documentation which explains how to access this Web Service via 4GL.
By using this documentation you will be able to create a 4GL application, e.g.:
Note: The Wrapped Document/Literal calling convention is now supported since OpenEdge 10.1A, so the sample code might look different.
DEFINE VARIABLE hWS AS HANDLE.
DEFINE VARIABLE hPortType AS HANDLE.
DEFINE VARIABLE lcSymbol AS LONGCHAR VIEW-AS EDITOR LARGE SIZE 60 BY 20.
DEFINE VARIABLE lcQuote AS LONGCHAR VIEW-AS EDITOR LARGE SIZE 60 BY 20.
DEFINE VARIABLE hDoc AS HANDLE.
DEFINE VARIABLE hRoot AS HANDLE.
DEFINE VARIABLE hChild AS HANDLE.
DEFINE VARIABLE hNextChild AS HANDLE.
DEFINE VARIABLE hNextNode AS HANDLE.
DEFINE VARIABLE hTextNode AS HANDLE.
DEFINE VARIABLE i AS INTEGER.
DEFINE VARIABLE j AS INTEGER.
DEFINE VARIABLE err AS LOGICAL.
DEFINE VARIABLE cResult AS CHARACTER INITIAL "".
DEFINE VARIABLE fillin-1 AS CHARACTER FORMAT "x(40)".
DEFINE VARIABLE text-1 AS CHARACTER FORMAT "x(50)" VIEW-AS TEXT.
DEFINE VARIABLE fillin-2 AS CHARACTER VIEW-AS EDITOR LARGE SIZE 63 BY 15 SCROLLBAR-VERTICAL LABEL "".
DEFINE VARIABLE text-2 AS CHARACTER FORMAT "x(80)" VIEW-AS TEXT.
DEFINE BUTTON bgetQuote LABEL "getQuote" SIZE 14 BY 1 DEFAULT.
DEFINE FRAME fTemp
text-1 AT ROW 1.5 COLUMN 2
fillin-1 AT ROW 1.25 COLUMN 25
text-2 AT ROW 3 COLUMN 2
bgetQuote AT ROW 4.5 COLUMN 30
fillin-2 AT ROW 6 COLUMN 5
WITH NO-LABELS SIZE 82 BY 22 THREE-D.
IF SESSION:DISPLAY-TYPE = "GUI":U THEN
DO:
CURRENT-WINDOW:WIDTH-CHARS = FRAME ftemp:WIDTH-CHARS.
CURRENT-WINDOW:HEIGHT-CHARS = FRAME ftemp:HEIGHT-CHARS.
END.
ON CHOOSE of bgetQuote
DO:
fillin-2:SCREEN-VALUE = "".
lcQuote = "".
cResult = "".
/*
&nb.sp; Constructs the XML document that is input to the Web service operation.
The document is created in this form:
<s0:GetStockQuotes xmlns:s0="http://swanandmokashi.com/">
<s0:QuoteTicker>stock-symbol(s)</s0:QuoteTicker>
</s0:GetStockQuotes>
*/
CREATE X-DOCUMENT hDoc.
CREATE X-NODEREF hRoot.
CREATE X-NODEREF hChild.
CREATE X-NODEREF hNextChild.
CREATE X-NODEREF hNextNode.
CREATE X-NODEREF hTextNode.
hDoc:CREATE-NODE-NAMESPACE(hRoot, "http://swanandmokashi.com", "s0:GetQuotes", "element").
hRoot:SET-ATTRIBUTE("xmlns:s0", "http://swanandmokashi.com").
hDoc:APPEND-CHILD(hRoot).
hDoc:CREATE-NODE-NAMESPACE(hChild, "http://swanandmokashi.com", "s0:QuoteTicker", "element").
hRoot:APPEND-CHILD(hChild).
hDoc:CREATE-NODE(hTextNode, ?, "text").
hTextNode:NODE-VALUE = FILLIN-1:SCREEN-VALUE.
hChild:APPEND-CHILD(hTextNode).
hDoc:SAVE("longchar", lcSymbol).
/* Invokes the GetStockQuotes operation in the Web service by making a procedure call */
RUN GetStockQuotes IN hPortType (INPUT lcSymbol, OUTPUT lcQuote) NO-ERROR.
/* Checks for errors and manages the information for any that occur */
RUN ErrorInfo (OUTPUT err).
IF NOT err THEN
/*
The Web service returns the output XML document with the following structure
(shown for a single quote example, PRGS):
<GetStockQuotesResponse xmlns="http://swanandmokashi.com/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xml="http://www.w3.org/XML/1998
amespace"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<GetStockQuotesResult>
<Quote>
<CompanyName>PROGRESS SOFT</CompanyName>
<StockTicker>PRGS</StockTicker>
<StockQuote>21.41</StockQuote>
<LastUpdated>4:00pm</LastUpdated>
<Change>+0.86</Change>
<OpenPrice>N/A</OpenPrice>
. <DayHighPrice>21.60</DayHighPrice>
<DayLowPrice>20.41</DayLowPrice>
<Volume>160721</Volume>
<MarketCap>740.4M</MarketCap>
<YearRange>11.50 - 24.06</YearRange>
</Quote>
</GetStockQuotesResult>
</GetStockQuotesResponse>
*/
DO:
hDoc:LOAD("longchar", lcQuote, FALSE).
/*
The client then parses this document to get the relevant information and put it
into a CHARACTER variable for display
*/
hDoc:GET-DOCUMENT-ELEMENT(hRoot). /* hRoot = GetQuotesResponse */
IF hRoot:SUBTYPE = "ELEMENT" AND hRoot:NAME = "GetQuotesResponse" THEN
DO:
hRoot:GET-CHILD(hChild, 1). /* hChild = GetQuotesResult*/
IF hChild:SUBTYPE = "ELEMENT" AND hChild:NAME = "GetQuotesResult" THEN
DO i = 1 TO hChild:NUM-CHILDREN:
hChild:GET-CHILD(hNextChild, i). /* hNextChild = Quote */
IF hNextChild:SUBTYPE = "ELEMENT" AND hNextChild:NAME = "Quote" THEN
DO j = 1 TO hNextChild:NUM-CHILDREN:
hNextChild:GET-CHILD(hNextNode, j).
IF hNextNode:NAME = "CompanyName" THEN
DO:
hNextNode:GET-CHILD(hTextNode, 1).
IF hTextNode:SUBTYPE = "TEXT" THEN
cResult = cResult + hNextNode:NAME + " = " + hTextNode:NODE-VALUE + "~n".
END.
IF hNextNode:NAME = "StockTicker" THEN
DO:
. hNextNode:GET-CHILD(hTextNode, 1).
IF hTextNode:SUBTYPE = "TEXT" THEN
cResult = cResult + hNextNode:NAME + " = " + hTextNode:NODE-VALUE + "~n".
END.
IF hNextNode:NAME = "StockQuote" THEN
DO:
hNextNode:GET-CHILD(hTextNode, 1).
IF hTextNode:SUBTYPE = "TEXT" THEN
cResult = cResult + hNextNode:NAME + " = " + hTextNode:NODE-VALUE + "~n".
END.
IF hNextNode:NAME = "LastUpdated" THEN
DO:
hNextNode:GET-CHILD(hTextNode, 1).
IF hTextNode:SUBTYPE = "TEXT" THEN
cResult = cResult + hNextNode:NAME + " = " + hTextNode:NODE-VALUE + "~n".
END.
IF hNextNode:NAME = "Change" THEN
DO:
hNextNode:GET-CHILD(hTextNode, 1).
IF hTextNode:SUBTYPE = "TEXT" THEN
cResult = cResult + hNextNode:NAME + " = " + hTextNode:NODE-VALUE + "~n".
END.
IF hNextNode:NAME = "OpenPrice" THEN
&n.bsp; DO:
hNextNode:GET-CHILD(hTextNode, 1).
IF hTextNode:SUBTYPE = "TEXT" THEN
cResult = cResult + hNextNode:NAME + " = " + hTextNode:NODE-VALUE + "~n".
END.
IF hNextNode:NAME = "DayHighPrice" THEN
DO:
hNextNode:GET-CHILD(hTextNode, 1).
IF hTextNode:SUBTYPE = "TEXT" THEN
cResult = cResult + hNextNode:NAME + " = " + hTextNode:NODE-VALUE + "~n".
END.
IF hNextNode:NAME = "DayLowPrice" THEN
DO:
hNextNode:GET-CHILD(hTextNode, 1).
IF hTextNode:SUBTYPE = "TEXT" THEN
cResult = cResult + hNextNode:NAME + " = " + hTextNode:NODE-VALUE + "~n".
END.
IF hNextNode:NAME = "Volume" THEN
DO:
hNextNode:GET-CHILD(hTextNode, 1).
IF hTextNode:SUBTYPE = "TEXT" THEN
cResult = cResult + hNextNode:NAME + " = " + hTextNode:NODE-VALUE + "~n".
END.
IF hNextNode:NAME = "MarketCap" THEN
 .; DO:
hNextNode:GET-CHILD(hTextNode, 1).
IF hTextNode:SUBTYPE = "TEXT" THEN
cResult = cResult + hNextNode:NAME + " = " + hTextNode:NODE-VALUE + "~n".
END.
IF hNextNode:NAME = "YearRange" THEN
DO:
hNextNode:GET-CHILD(hTextNode, 1).
IF hTextNode:SUBTYPE = "TEXT" THEN
cResult = cResult + hNextNode:NAME + " = " + hTextNode:NODE-VALUE + "~n".
END.
END. /* hNextChild = Quote */
END. /* hChild = GetQuotesResult*/
END. /* hRoot = GetQuotesResponse */
END. /* IF NOT err */
fillin-2:SCREEN-VALUE = cResult.
DELETE OBJECT hDoc.
DELETE OBJECT hRoot.
DELETE OBJECT hChild.
DELETE OBJECT hNextChild.
DELETE OBJECT hNextNode.
DELETE OBJECT hTextNode.
END. /* bgetQuote*/
FILLIN-1:SCREEN-VALUE = "PRGS".
text-1:SCREEN-VALUE = "Enter Stock Symbols".
text-2:SCREEN-VALUE = "To get multiple quotes enter Ticker symbols seperated by , (eg. PRGS,GE)".
ENABLE ALL WITH FRAME fTemp.
/* Creates a Web service object in the 4GL */
CREATE SERVER hWS.
/* Connects to the Web service (which has only one valid service and port */
hWS:CONNECT("-WSDL http://www.swanandmokashi.com/HomePage/WebServices/StockQuotes.asmx?wsdl").
IF NOT hWS:CONNECTED() THEN
MESSAGE "SERVER NOT CONNECTED" VIEW-AS ALERT-BOX.
/*
Sets the handle to the port type in the WSDL where the Web service operation is defined
and waits for the user to invoke the request
*/
RUN StockQuotesSoap SET hPortType ON SERVER hWS.
IF NOT VALID-HANDLE(hPortType) THEN
MESSAGE "hPortType invalid: " VALID-HANDLE(hPortType) VIEW-AS ALERT-BOX.
WAIT-FOR CLOSE OF CURRENT-WINDOW.
ONG>/*
Clean up by:
a. Deleting the procedure object.
b. Disconnecting from the Web service.
c. Deleting the Web service object.
*/
DELETE PROCEDURE hPortType.
hWS:DISCONNECT().
DELETE OBJECT hWS.
PROCEDURE ErrorInfo:
DEFINE OUTPUT PARAMETER errorfound AS LOGICAL INITIAL FALSE.
DEFINE VARIABLE k AS INTEGER.
DEFINE VARIABLE hSOAPFault AS HANDLE.
DEFINE VARIABLE hSOAPFaultDetail AS HANDLE.
DEFINE VARIABLE HeaderXML AS LONGCHAR VIEW-AS EDITOR SIZE 52 BY 5 LARGE.
IF ERROR-STATUS:NUM-MESSAGES > 0 THEN
DO:
errorfound = TRUE.
DO k = 1 TO ERROR-STATUS:NUM-MESSAGES:
MESSAGE ERROR-STATUS:GET-MESSAGE(k) VIEW-AS ALERT-BOX.
END.
IF VALID-HANDLE(ERROR-STATUS:ERROR-OBJECT-DETAIL) THEN
DO:
hSOAPFault = ERROR-STATUS:ERROR-OBJECT-DETAIL.
MESSAGE
"Fault Code: " hSOAPFault:SOAP-FAULT-CODE SKIP
"Fault String: " hSOAPFault:SOAP-FAULT-STRING SKIP
"Fault Actor: " hSOAPFault:SOAP-FAULT-ACTOR SKIP
"Error Type: " hSOAPFault:TYPE VIEW-AS ALERT-BOX.
IF VALID-HANDLE(hSOAPFault:SOAP-FAULT-DETAIL) THEN
DO:
hSOAPFaultDetail = hSOAPFault:SOAP-FAULT-DETAIL.
MESSAGE "Error Type: " hSOAPFaultDetail:TYPE VIEW-AS ALERT-BOX.
HeaderXML = hSOAPFaultDetail:GET-SERIALIZED().
DISPLAY HeaderXML LABEL "Serialized SOAP fault detail" WITH FRAME a.
END.
END.
END.
END..