Friday, November 21, 2014

consumer Business service (BSSV) in JDE.

Create consumer BSSV ........

·         Generate a web service proxy
·         Create a business service to wrap a web service proxy
·         Create and retrieve a soft coding record
·         Generate an XML template from a value object class
·         Call a business service from a business function
                                                                                                                                                                         

Activity Overview

EnterpriseOne consumes external web services through a web service proxy which is called from a business service which is called from a business function. In this exercise each of these elements will be created to call a weather forecast web service. The weather forecast web service receives a US zip code as input and returns a one week forecast for that location.
In this exercise the student will
  • Create a business service from OMW
  • Generate a web service proxy within JDeveloper
  • Create an internal business service class that calls the web service proxy
  • Use EnterpriseOne soft coding applications to create a soft coding record
  • Add code to retrieve the soft coding record from within the business service
  • Generate an XML template from a value object class
  • Use the XML template as an example to write business function code which will call the business service
                   
Note.  This exercise closely mimics the Weather Forecast Processor Reference Implementation, JRH90I10. Use the code in those objects as an example if necessary for understanding.


Activity Detailed Steps

Configure JDeveloper for Web Service Consumption
To allow JDeveloper to retrieve external WSDL files configure preferences with HTTP Proxy information.
1.      Launch JDeveloper
2.      From the Tools menu choose Preferences
3.      Highlight Web Browser and Proxy
4.      Configure JDeveloper to Use an HTTP Proxy Server with these settings
a.       Host Name - www-proxy.us.oracle.com
b.      Port Number – 80
c.       Exceptions – Add these exceptions to the machine name already listed: |*.oracle.com|*.peoplesoft.com|*.jdedwards.com|*.mlab.jdedwards.com

5.      Press OK
6.      Close JDeveloper
Create a business service from OMW
To add a new Business Service in Object Management Workbench:
7.      Launch JD Edwards Solution Explorer
a.       user name: JDE
b.      password: JDE
8.      Type OMW in the fast path field and press enter
9.      Press Add
10.  Select the Business Function radio button and press OK
11.  Complete the following information about the published business service object
a.       Object Name – J5500050
b.      Description – Weather Forecast Processor
c.       Product Code/Product System Code – 55
d.      Source Language – BSSV
e.       Package Prefix – oracle.e1.bssv
12.  Press OK
13.  Launch JDeveloper for the new business service
Generate a web service proxy within JDeveloper
Use native JDeveloper tooling to create a web service proxy for http://www.webservicex.net/WeatherForecast.asmx.
1.      With the project highlighted in the Application Navigator pane choose New from the context menu.
2.      In the New Gallery change the filter to All Technologies. Under Business Tier choose Web Service. In the right hand pane choose Web Service Proxy. Press OK.

3.      A multi-step wizard is shown. Click next past the welcome page. For step one provide http://www.webservicex.net/WeatherForecast.asmx?wsdl as the WSDL Document URL. Accept the defaults for the remainder of the wizard steps.

4.      All classes necessary for invoking the web service will be generated and can be viewed with the structure pane. When the wizard completes a WeatherForecastSoapClient class will be opened in the editor. This is the class that will be used to invoke the web service from the business service.
Create a business service class (and value object) that calls the web service proxy
Use the EnterpriseOne extension to create an internal business service which will call the web service proxy.
1.      With the project highlighted in the Application Navigator pane choose New from the context menu.
2.      Select Classes under the EnterpriseOne heading then choose Business Service Class in the right hand pane. Click OK.
3.      Enter the following information about the business service class to be created:
a.       Name – WeatherForecastProcessor
b.      Method Name – getForecastByZip
c.       Input Class (type this - class does not yet exist) InternalGetWeatherForecast
4.      In the method body declare an instance of the WeatherForecastSoapClient class. Add the required try/catch block. Use the WeatherForecaseSoapClient instance variable to add a call to GetForecastByZip.
    try{
      WeatherForecastSoapClient myPort = new WeatherForecastSoapClient();
     
      myPort.getWeatherByZipCode()
    }
    catch (Exception e){
    }
5.      Ctrl-click on getWeatherByZipCode to see that its signature is:
WeatherForecasts getWeatherByZipCode(String zipCode) throws java.rmi.RemoteException
6.      This signature will be used to help construct the value object for the business service. First create the value object class. With the project highlighted, choose New from the context menu.
7.      Choose General on the left hand pane and Java class on the right. Press OK.
8.      Give the new class the following values and press OK
a.       Name – InternalGetWeatherForecast
b.      Package – oracle.e1.bssv.J5500050.valueobject
c.       Extends – oracle.e1.bssvfoundation.base.ValueObject
9.      The new class is generated and opened. Add a string member variable for zip code. Inspect the WeatherForecasts class to decide what other member variables should be added to the value object class.
private String zipCode = null;   // input
private String placeName = null; // output
private String stateCode = null; // output
private String status = null;    // output
10.  One of the values returned in the WeatherForecasts object is an array. To handle this return value, another value object class must be created and added as a member variable. With the project highlighted, choose New from the context menu.
11.  Choose General on the left hand pane and Java class on the right. Press OK.
12.  Give the new class the following values and press OK
a.       Name – DayForecast
b.      Package – oracle.e1.bssv.J5500050.valueobject
c.       Extends – oracle.e1.bssvfoundation.base.ValueObject
13.  Using the WeatherData class as a guide, add the following member variables to the DayForecast object.
private String day = null;
private String maxTemperatureF = null;
private String minTemperatureF = null;
private String maxTemperatureC = null;
private String minTemperatureC = null;
14.  Generate accessors for the WeatherData member variables.
15.  Add an array of type DayForecast to the InternalGetWeatherForecast class.
private DayForecast[] details = null;
16.  Generate accessors for the InternalGetWeatherForecast member variables.
17.  Add logic to WeatherForecastProcessor.getForecastByZip to pass in the zip code and to copy the values returned to the InternalGetWeatherForecast member variables.
WeatherForecasts wsReturn = myPort.getWeatherByZipCode(internalVO.getZipCode());
     
if (wsReturn != null)
{
  internalVO.setPlaceName(wsReturn.getPlaceName());
  internalVO.setStateCode(wsReturn.getStateCode());
  internalVO.setStatus(wsReturn.getStatus());
  WeatherData[] weatherData = wsReturn.getDetails();
  if (weatherData != null && weatherData.length > 0)
  {
    DayForecast[] dayForecast = new DayForecast[weatherData.length];
    for (int i = 0; i < weatherData.length; i++)
    {
      dayForecast[i] = new DayForecast();
      dayForecast[i].setDay(weatherData[i].getDay());
      dayForecast[i].setMaxTemperatureC(weatherData[i].getMaxTemperatureC());
      dayForecast[i].setMaxTemperatureF(weatherData[i].getMaxTemperatureF());
      dayForecast[i].setMinTemperatureC(weatherData[i].getMinTemperatureC());
      dayForecast[i].setMinTemperatureF(weatherData[i].getMinTemperatureF());
    }
    internalVO.setDetails(dayForecast);
  }
}
18.  Add logic to the catch block to report any exceptions that may occur. The string used to construct the E1Message, “003FIS”, is an error data dictionary item. The strMessage variable is substitution text added to the error.
String strMessage = new String("WeatherForecastProcessor.getForecastByZip|" +
                         e.toString());

E1Message eMessage = new E1Message(context, "003FIS", strMessage);
//Add messages to final message list to be returned.
messages.addMessage(eMessage);
19.  Compile (context menu > Make) the project and resolve any compile errors.
Create and retrieve a soft coding record
Soft coding records provide a means to dynamically provide endpoint and security information to the third party web service. To create and use soft coding records:
1.      Expand the ‘proxy’ package in the project and highlight the WeatherForecastProxy in the Applications Navigator, choose Secure Proxy from the context menu
2.      The wizard that is shown can be used to create soft coding records for any web service proxy. Every supported standard is represented on the screens. For the weather service, only the endpoint needs to be specified. On step 1 of 6, choose No Authentication.

3.      On step 2 of 6 uncheck Sign Outbound Messages

4.      On step 3 of 6 uncheck Verify Inbound Signed Request Body

5.      On step 4 of 6 uncheck Encrypt Outbound Messages
6.      
7.      On step 5 of 6 uncheck Decrypt Inbound Message Content

8.      Click Finish. Open the resulting xml file by highlighting WeatherForecastProxy in the Application Navigator and double clicking on WeatherForecastSoap_Stub.xml in the structure pane.

9.      Within the xml, look for the port-info tags. This xml element is the portion that is used in a soft coding record. In this example where only endpoint is provided, the significant information is in the wsdl-port tag. In a typical customer set-up, the wsdl-port tag will need to contain different information in different environments.
10.  For this particular web service only the endpoint really needs to be specified. Even though all the ‘security’ options were disabled in the wizard the web service will give a failure if the runtime tag is present in the XML. To avoid this error the runtime tag must be removed. The resulting XML will contain:
<port-info>
<wsdl-port namespaceURI="http://www.webservicex.net" localpart="WeatherForecastSoap"/>
<operations>
<operation name='GetWeatherByZipCode'>
</operation>
<operation name='GetWeatherByPlaceName'>
</operation>
</operations>
</port-info>
This can be copied to the clipboard for use in the Soft Coding Application.
11.  Go back to JD Edwards Solution Explorer and under Tools choose EnterpriseOne Menu to launch the web version of the EnterpriseOne menu. Navigate to EnterpriseOne Menus > EnterpriseOne Life Cycle Tools > System Administration Tools > Soft Coding Administration and launch the Soft Coding Template application.
12.  Press Add
13.  Add a Soft Coding template with the following values
a.       Template Name – CUST_J5500050
b.      SoftCoding Key – J5500050
c.       Value – copy the <port-info> element from the generated xml document

14.  Press OK. The significance of the soft coding template is the soft coding key, which will be directly referenced in the code, and the structure of the xml. The template will be used both by the developer in subsequent steps for the development environment, and by system administrators for production environments.
15.  Launch the Soft Coding Records application
16.  Press Add
17.  Enter values for these fields
a.       User/Role – JDE
b.      Environment – PD812
c.       Template Name – CUST_J5500050
18.  Press Populate Soft Coding Value to pull in the soft coding key and xml from the template. At this point, if any information needed to vary based on user or environment the xml would be changed as necessary. For this example, the information from the template can be used as is.

19.  Press OK
20.  Return to JDeveloper. Open the WeatherForecastProcessor class and within the getForecastByZip method locate the line declaring ‘myPort’. Place the cursor on the line after the declaration.
21.  Press Ctrl-Enter and highlight E1SC. Press Enter.
22.  Type “J5500050” for the soft coding key.
23.  Tab twice and replace BS_NAME with WeatherForecastProcessor
24.  Tab and replace BS_METHOD_NAME with getForecastByZip
25.  Tab twice to exit the code template edit mode.
26.  Either restructure the nested try/catch blocks so that there is only one try/catch or place a return statement within the soft coding catch block so that the service doesn’t continue to run if a soft coding record is not found.
return messages;
27.  Compile (context menu > Make) the project and resolve any compile errors.
Generate an XML template from a Value Object class
The business service which calls the web service is now complete. In order to test the business service’s invocation of the web service, an XML document will be created.
1.      Highlight InternalGetWeatherForcast.java in the Application Navigator choose Generate XML Document Template from the context menu

2.      An XML file will be created and opened that can be used as an example of how to pass values from a business function to the business service. Choose Reformat from the editor window’s context menu

3.      Save a copy of this file (save as) with a different name to the same location on the hard drive. Modify that copy of the file so it only contains the tags related to the input (zip code). Put a value in for zip code. Save the file.
<?xml version="1.0" encoding="UTF-8"?>
<internal-get-weather-forecast>
  <zip-code>80134</zip-code>
</internal-get-weather-forecast>
4.      By typing or using a code template (ctrl-enter) add a main method to the WeatherForecastProcessor. Add a call to TestBusinessService.callBSSVWithXMLFile. Use the available Javadoc to assist in the correct values to pass (autoCommit can be true).
5.      Run or debug the main method until the web service is called successfully.
6.      Refresh the valueobject package of the project by highlighting it and pressing “refresh”.

Results of the test will be written to the same folder as the input file. The output comes in two files with the same prefix as the input file. The _messages file contains the results of the call to the business service. If the call could not be competed, or threw an exception of any kind it is captured in this text file. The _out.xml file contains the results of a successful call to the business service. Be aware that calling a business service successfully is not the same as a business service successfully completing its task. The concept is the same as a business function returning success which does not mean that the business function completed without errors.
7.      Open the _out and _messages files which are now visible in the project to inspect the results of the web service invocation. This is a significant development milestone which proves that the business service is complete and correctly able to call the web service.
Start the Development Business Services Server
To build and deploy the Development Business Services Server:
1.      From the context menu of the workspace choose Deploy Development BSSV Server

2.      Populate required information about HTTP Proxy server. A proxy server is a security measure. All ‘outgoing’ traffic is routed through the proxy server. This is required so that the business services server can call the web service. The same information was configured for JDeveloper in the first section of this document.
a.       Host Name: www-proxy.us.oracle.com
b.      Port Number: 80

3.      Press OK
4.      Progress of the build and deploy scripts can be viewed in the Apache Ant tab.
Update a business function to call the business service
To update B5500050 to call getForecastByZip in J5500050:
1.      From OMW search for B5500050 and add it to the default project
2.      Press “Design” for the business function then Start Business Function Design Aid
3.      Take the Form exit to Edit the business function
4.      Within Visual Studio find the declaration for GetWeatherForecast.
5.      Add variables and code to initialize the XML parser. Add associated import statement.
#include <xerceswrapper.h>

XRCS_Status             XRCSStatus           = XRCS_SUCCESS;
XRCS_hParser            hParser              = NULL;

XRCSStatus = XRCS_initEngine();
if(XRCSStatus != XRCS_SUCCESS)
{
  jdeVWriteLogEntry(_J("B5500050"), __FILE__, __LINE__, 0,
              _J("XRCS_initEngine failed"));
  return ER_ERROR;
}

/*  Get the Xerces Parser  */
XRCSStatus = XRCS_getParser(&hParser);
if(XRCSStatus != XRCS_SUCCESS)
{
  jdeVWriteLogEntry(_J("B5500050"), __FILE__, __LINE__, 0,
                    _J("XRCS_getParser failed"));
  XRCS_terminateEngine();
  return ER_ERROR;
}
6.      Add variables and code to declare and parse an XML string. The xmlString variable is initialized based on the XML template generated from within JDeveloper (also used for the test run). For more complex XML documents this can be constructed dynamically if appropriate.
XRCS_hDocument          hDoc       = NULL;
JCHAR                   *xmlString = _J("<?xml version=\"1.0\"?><internal-get-weather-forecast><zip-code></zip-code></internal-get-weather-forecast>");

/*  Parse the XML String  */
XRCSStatus = XRCS_parseXMLString(hParser, xmlString, &hDoc);
if(XRCSStatus != XRCS_SUCCESS)
{
  jdeVWriteLogEntry(_J("B5500050"), __FILE__, __LINE__, 0,
                    _J("XRCS_parseXMLString failed"));
  XRCS_freeParser(hParser);
  XRCS_terminateEngine();
  return ER_ERROR;
}
7.      Add code and variables to locate and set the value of the zip code element.
XRCS_hElement                 hRootElm         = NULL;
XRCS_hElement                 *hElm            = NULL;
unsigned int                   nElmCount        = 0;

/* Get Root Element */
XRCSStatus = XRCS_getDocumentElement(hDoc,&hRootElm);
if(XRCSStatus != XRCS_SUCCESS)
{
  jdeVWriteLogEntry(_J("B5500050"), __FILE__, __LINE__, 0,
                    _J("XRCS_getDocumentElement failed"));
  XRCS_freeParser(hParser);
  XRCS_freeDocument(hDoc);
  XRCS_terminateEngine();
  return ER_ERROR;
}

/* Get the zip code and set it's value to xml document element*/
XRCSStatus = XRCS_getElementsByTagName(hRootElm, _J("zip-code"),
                                       &hElm,&nElmCount);
if(XRCSStatus != XRCS_SUCCESS)
{
  jdeVWriteLogEntry(_J("B5500050"), __FILE__, __LINE__, 0,
                    _J("XRCS_getElementsByTagName failed"));
  XRCS_freeParser(hParser);
  XRCS_freeDocument(hDoc);
  XRCS_freeElement(hRootElm);
  XRCS_terminateEngine();
  return ER_ERROR;
}

XRCSStatus = XRCS_setElementText(*hElm, lpDS->szZip);
if(XRCSStatus != XRCS_SUCCESS)
{
  jdeVWriteLogEntry(_J("B5500050"), __FILE__, __LINE__, 0,         
                    _J("XRCS_setElementText failed"));
  XRCS_freeParser(hParser);
  XRCS_freeDocument(hDoc);
  XRCS_freeElement(hRootElm);
  XRCS_freeElementArray(hElm,nElmCount);
  XRCS_terminateEngine();
  return ER_ERROR;
}
8.      Add code and variables to serialize the in-memory XML document to its string format. Depending on the size of the document both of these representations of the XML document can be quite memory intensive. Consider ways to minimize the size of the XML document passed to the business service.
JCHAR                         *newXMLString          = NULL;

/* Serialize the XML DOC to XML String */
XRCSStatus = XRCS_serializeDocumentToXMLStringNoEncoding(hDoc,
                &newXMLString);
if(XRCSStatus != XRCS_SUCCESS)
{
  jdeVWriteLogEntry(_J("B5500050"), __FILE__, __LINE__, 0,
       _J("XRCS_serializeDocumentToXMLStringNoEncoding failed"));
  XRCS_freeParser(hParser);
  XRCS_freeDocument(hDoc);
  XRCS_freeElement(hRootElm);
  XRCS_freeElementArray(hElm,nElmCount);
  XRCS_terminateEngine();
  return ER_ERROR;
}
9.      Add code and variables to make the call to the business service. Add an if statement to isolate success return codes from error return codes.
ID                       idReturnValue        = ER_SUCCESS;
JCHAR                    *bssvPayloadReturn   = NULL;

/* call the Business Service */
idReturnValue = jdeCallBusinessService(lpBhvrCom,
      lpVoid,
      _J("oracle.e1.bssv.J5500050.WeatherForecastProcessor"),           _J("getForecastByZip"),
      TRUE,
      newXMLString,
      &bssvPayloadReturn);

if ( idReturnValue == CallBSSVNoError ||
     idReturnValue == CallBSSVNoErrorWithMessages)
{
}
else
{
}
10.  Add code to the ‘else’ block to set an error to the application if the call to the business service did not succeed. Add associated variable and import statement.
#include <b953002.h>

JCHAR             *errorText               = NULL;
DSDE954000         dsDE954000               = {0};

memset((void *)(&dsDE954000), (int)(_J('\0')),
       sizeof(dsDE954000));

errorText = jdeGetBusinessServiceErrorText(idReturnValue);
jdeVWriteLogEntry(_J("B5500050"), __FILE__, __LINE__,
                  0,errorText);
jdeStrncpy(dsDE954000.szWSCallExceptionInfo, errorText ,
           DIM(dsDE954000.szWSCallExceptionInfo));
jdeSetGBRErrorSubText(lpBhvrCom, lpVoid, (ID) 0, _J("007FIS"),
                      &dsDE954000);

XRCS_freeParser(hParser);
XRCS_freeDocument(hDoc);
XRCS_freeElement(hRootElm);
XRCS_freeElementArray(hElm,nElmCount);
XRCS_terminateEngine();
jdeFree(newXMLString);
jdeFreeBSSVPayloadReturn (&bssvPayloadReturn);
return ER_ERROR;
11.  Within the ‘if’ block add code to parse the returned XML string, retrieve the root element and the first level children of that element. Add associated variables.
XRCS_hDocument          hBSSVDoc             = NULL;
XRCS_hElement           *hChildElms          = NULL;
unsigned int            nChildCount          = 0;

XRCSStatus = XRCS_parseXMLStringRemoveEncoding(hParser,
                  bssvPayloadReturn, &hBSSVDoc);

if(XRCSStatus != XRCS_SUCCESS)
{
  jdeVWriteLogEntry(_J("B5500050"), __FILE__, __LINE__, 0,
             _J("XRCS_parseXMLStringRemoveEncoding failed"));
  XRCS_freeParser(hParser);
  XRCS_freeDocument(hDoc);
  XRCS_freeElement(hRootElm);
  XRCS_freeElementArray(hElm,nElmCount);
  XRCS_terminateEngine();
  jdeFree(newXMLString);
  jdeFreeBSSVPayloadReturn (&bssvPayloadReturn);
  return ER_ERROR;
}

XRCSStatus = XRCS_getDocumentElement(hBSSVDoc,&hRootElm);

if(XRCSStatus != XRCS_SUCCESS)
{
  jdeVWriteLogEntry(_J("B5500050"), __FILE__, __LINE__, 0,
                    _J("XRCS_getDocumentElement failed"));
  XRCS_freeParser(hParser);
  XRCS_freeDocument(hDoc);
  XRCS_freeElementArray(hElm,nElmCount);
  XRCS_freeDocument(hBSSVDoc);
  XRCS_terminateEngine();
  jdeFree(newXMLString);
  jdeFreeBSSVPayloadReturn (&bssvPayloadReturn);
  return ER_ERROR;
}

XRCSStatus = XRCS_getElementChildren(hRootElm,
                                     &hChildElms,
                                     &nChildCount);
if(XRCSStatus != XRCS_SUCCESS)
{
  jdeVWriteLogEntry(_J("B5500050"), __FILE__, __LINE__, 0,
                    _J("XRCS_getElementChildren failed"));
  XRCS_freeParser(hParser);
  XRCS_freeDocument(hDoc);
  XRCS_freeElementArray(hElm,nElmCount);
  XRCS_freeDocument(hBSSVDoc);
  XRCS_freeElement(hRootElm);
  XRCS_terminateEngine();
  jdeFree(newXMLString);
  jdeFreeBSSVPayloadReturn (&bssvPayloadReturn);
  return ER_ERROR;
}
12.  Add a for loop to iterate through the first level children. Add associated variable.
unsigned int i = 0;

for (i=0; i<nChildCount; i++)
{
}
13.  In the “for i” block add code to get the child element’s name and value. Add associated variables.
JCHAR       *elmName         = NULL;
JCHAR       *elmValue        = NULL;

XRCSStatus = XRCS_getElementName(hChildElms[i],&elmName);
if(XRCSStatus != XRCS_SUCCESS)
{
  jdeVWriteLogEntry(_J("B5500050"), __FILE__, __LINE__, 0,
                    _J("XRCS_getElementName failed"));
  XRCS_freeParser(hParser);
  XRCS_freeDocument(hDoc);
  XRCS_freeElementArray(hElm,nElmCount);
  XRCS_freeDocument(hBSSVDoc);
  XRCS_freeElement(hRootElm);
  XRCS_freeElementArray(hChildElms,nChildCount);
  XRCS_terminateEngine();
  jdeFree(newXMLString);
  jdeFreeBSSVPayloadReturn (&bssvPayloadReturn);
  return ER_ERROR;
}

XRCSStatus = XRCS_getElementText(hChildElms[i],&elmValue);
if(XRCSStatus != XRCS_SUCCESS)
{
  jdeVWriteLogEntry(_J("B5500050"), __FILE__, __LINE__, 0,
                    _J("XRCS_getElementText failed"));
  XRCS_freeParser(hParser);
  XRCS_freeDocument(hDoc);
  XRCS_freeElementArray(hElm,nElmCount);
  XRCS_freeDocument(hBSSVDoc);
  XRCS_freeElement(hRootElm);
  XRCS_freeElementArray(hChildElms,nChildCount);
  XRCS_terminateEngine();
  XRCS_freeString(elmName);
  jdeFree(newXMLString);
  jdeFreeBSSVPayloadReturn (&bssvPayloadReturn);
  return ER_ERROR;
}
14.  Still within the “for i" block add code to handle each specific child node. Add associated variables.
if ( (JCHAR*)NULL != elmValue)
{
  if (jdeStricmp(elmName,_J("place-name"))==0)
  {
    jdeStrncpyTerminate(lpDS->szCity, elmValue,
                        DIM(lpDS->szCity));
  }
  else if (jdeStricmp(elmName,_J("state-code"))==0)
  {
    jdeStrncpyTerminate(lpDS->szState, elmValue,
                        DIM(lpDS->szState));
  }
  else if (jdeStricmp(elmName,_J("details"))==0)
  {
  }
  XRCS_freeString(elmName);
  XRCS_freeString(elmValue);
  elmName = (JCHAR*)NULL;
  elmValue = (JCHAR*)NULL;
}
15.  Add code to handle the details element (within the “details” else if code block). Since this is a compound element it will also have to retrieve child nodes much the same way the root element does. This block of code gets the count of the detail’s child elements. Add associated variables.
XRCS_hElement           *hDetailElms        = NULL;
unsigned int            nDetailCount        = 0;
JCHAR                   *detailName         = NULL;
unsigned int            day                 = 0;

day = day + 1;
XRCSStatus = XRCS_getElementChildren(hChildElms[i],
                         &hDetailElms,&nDetailCount);
if(XRCSStatus != XRCS_SUCCESS)
{
  jdeVWriteLogEntry(_J("B5500050"), __FILE__, __LINE__, 0,
                    _J("XRCS_getElementChildren failed"));
  XRCS_freeParser(hParser);
  XRCS_freeDocument(hDoc);
  XRCS_freeElementArray(hElm,nElmCount);
  XRCS_freeDocument(hBSSVDoc);
  XRCS_freeElement(hRootElm);
  XRCS_terminateEngine();
  jdeFree(newXMLString);
  jdeFreeBSSVPayloadReturn (&bssvPayloadReturn);
  return ER_ERROR;
}
16.  Add another for loop (nested within the “for i" loop) to iterate over the detail’s child elements and process them. Add associated variables.
JCHAR             *detailValue         = NULL;
unsigned int      j                    = 0;

for (j=0; j<nDetailCount; j++)
{
  XRCSStatus = XRCS_getElementName(hDetailElms[j],&detailName);
  if(XRCSStatus != XRCS_SUCCESS)
  {
    jdeVWriteLogEntry(_J("B5500050"), __FILE__, __LINE__, 0,
                      _J("XRCS_getElementName failed"));
    XRCS_freeParser(hParser);
    XRCS_freeDocument(hDoc);
    XRCS_freeElementArray(hElm,nElmCount);
    XRCS_freeDocument(hBSSVDoc);
    XRCS_freeElement(hRootElm);
    XRCS_freeElementArray(hChildElms,nChildCount);
    XRCS_freeElementArray(hDetailElms,nDetailCount);
    XRCS_terminateEngine();
    jdeFree(newXMLString);
    jdeFreeBSSVPayloadReturn (&bssvPayloadReturn);
    return ER_ERROR;
  }
  XRCSStatus = XRCS_getElementText(hDetailElms[j],&detailValue);
  if(XRCSStatus != XRCS_SUCCESS)
  {
    jdeVWriteLogEntry(_J("B5500050"), __FILE__, __LINE__, 0,
                      _J("XRCS_getElementText failed"));
    XRCS_freeParser(hParser);
    XRCS_freeDocument(hDoc);
    XRCS_freeElementArray(hElm,nElmCount);
    XRCS_freeDocument(hBSSVDoc);
    XRCS_freeElement(hRootElm);
    XRCS_freeElementArray(hChildElms,nChildCount);
    XRCS_freeElementArray(hDetailElms,nDetailCount);
    XRCS_terminateEngine();
    XRCS_freeString(elmName);
    jdeFree(newXMLString);
    jdeFreeBSSVPayloadReturn (&bssvPayloadReturn);
    return ER_ERROR;
  }
}
17.  Within the “for j” loop add code to determine which detail child element is found an assign it to the appropriate data structure element. This web service has a fixed number of detail elements that are returned, so the business function data structure was created to accommodate all the values. Some web services will have a variable number of returns and the returned data will have to be stored in a cache prior to returning it to the application.
if ( (JCHAR*)NULL != detailValue)
{
  if (jdeStricmp(detailName,_J("max-temperature-c"))==0)
  {
    switch (day)
    {
      case 0:
        jdeStrncpyTerminate(lpDS->szDay1MaxC, detailValue,
                            DIM(lpDS->szDay1MaxC));
        break;
      case 1:
        jdeStrncpyTerminate(lpDS->szDay2MaxC, detailValue,
                            DIM(lpDS->szDay2MaxC));
        break;
      case 2:
        jdeStrncpyTerminate(lpDS->szDay3MaxC, detailValue,
                            DIM(lpDS->szDay3MaxC));
        break;
      case 3:
        jdeStrncpyTerminate(lpDS->szDay4MaxC, detailValue,
                            DIM(lpDS->szDay4MaxC));
        break;
      case 4:
        jdeStrncpyTerminate(lpDS->szDay5MaxC, detailValue,
                            DIM(lpDS->szDay5MaxC));
        break;
      case 5:
        jdeStrncpyTerminate(lpDS->szDay6MaxC, detailValue,
                            DIM(lpDS->szDay6MaxC));
        break;
    }
  }
  else if (jdeStricmp(detailName,_J("min-temperature-c"))==0)
  {
    switch (day)
    {
      case 0:
        jdeStrncpyTerminate(lpDS->szDay1MinC, detailValue,
                            DIM(lpDS->szDay1MinC));
        break;
      case 1:
        jdeStrncpyTerminate(lpDS->szDay2MinC, detailValue,
                            DIM(lpDS->szDay2MinC));
        break;
      case 2:
        jdeStrncpyTerminate(lpDS->szDay3MinC, detailValue,
                            DIM(lpDS->szDay3MinC));
        break;
      case 3:
        jdeStrncpyTerminate(lpDS->szDay4MinC, detailValue,
                            DIM(lpDS->szDay4MinC));
        break;
      case 4:
        jdeStrncpyTerminate(lpDS->szDay5MinC, detailValue,
                            DIM(lpDS->szDay5MinC));
        break;
      case 5:
        jdeStrncpyTerminate(lpDS->szDay6MinC, detailValue,
                            DIM(lpDS->szDay6MinC));
        break;
    }
  }
  else if (jdeStricmp(detailName,_J("max-temperature-f"))==0)
  {
    switch (day)
    {
      case 0:
        jdeStrncpyTerminate(lpDS->szDay1MaxF, detailValue,
                            DIM(lpDS->szDay1MaxF));
        break;
      case 1:
        jdeStrncpyTerminate(lpDS->szDay2MaxF, detailValue,
                            DIM(lpDS->szDay2MaxF));
        break;
      case 2:
        jdeStrncpyTerminate(lpDS->szDay3MaxF, detailValue,
                            DIM(lpDS->szDay3MaxF));
        break;
      case 3:
        jdeStrncpyTerminate(lpDS->szDay4MaxF, detailValue,
                            DIM(lpDS->szDay4MaxF));
        break;
      case 4:
        jdeStrncpyTerminate(lpDS->szDay5MaxF, detailValue,
                            DIM(lpDS->szDay5MaxF));
        break;
      case 5:
        jdeStrncpyTerminate(lpDS->szDay6MaxF, detailValue,
                            DIM(lpDS->szDay6MaxF));
        break;
    }
  }
  else if (jdeStricmp(detailName,_J("min-temperature-f"))==0)
  {
    switch (day)
    {
      case 0:
        jdeStrncpyTerminate(lpDS->szDay1MinF, detailValue,
                            DIM(lpDS->szDay1MinF));
        break;
      case 1:
        jdeStrncpyTerminate(lpDS->szDay2MinF, detailValue,
                            DIM(lpDS->szDay2MinF));
        break;
      case 2:
        jdeStrncpyTerminate(lpDS->szDay3MinF, detailValue,
                            DIM(lpDS->szDay3MinF));
        break;
      case 3:
        jdeStrncpyTerminate(lpDS->szDay4MinF, detailValue,
                            DIM(lpDS->szDay4MinF));
        break;
      case 4:
        jdeStrncpyTerminate(lpDS->szDay5MinF, detailValue,
                            DIM(lpDS->szDay5MinF));
        break;
      case 5:
        jdeStrncpyTerminate(lpDS->szDay6MinF, detailValue,
                            DIM(lpDS->szDay6MinF));
        break;
    }
  }
}
18.  After processing of each detail child element the allocated memory needs released. Within the “if ( (JCHAR*)NULL != detailValue)“ code block add code to release the memory.
XRCS_freeString(detailName);
XRCS_freeString(detailValue);
detailName = (JCHAR*)NULL;
detailValue = (JCHAR*)NULL;
19.  After processing of each root child element the allocated memory needs released. Within the “if ((JCHAR*)NULL != elmValue)“ code block add code to release the memory.
XRCS_freeString(elmName);
XRCS_freeString(elmValue);
elmName = (JCHAR*)NULL;
elmValue = (JCHAR*)NULL;
20.  Throughout the code the failure cases have cleaned up prior to returning ER_ERROR. Just before the method returns ER_SUCCESS add code to clean up for the successful case.
XRCS_freeParser(hParser);
XRCS_freeDocument(hDoc);
XRCS_freeElementArray(hElm,nElmCount);
XRCS_freeDocument(hBSSVDoc);
XRCS_freeElement(hRootElm);
XRCS_freeElementArray(hChildElms,nChildCount);
XRCS_terminateEngine();
jdeFree(newXMLString);
jdeFreeBSSVPayloadReturn (&bssvPayloadReturn);
21.  This completes the business function coding. Return to OMW and run Bus Build to build the business function resolve any compile errors that may occur.
22.  From Solution Explorer run the EnterpiseOne Menu option under Tools. Use fast path to run P5550. This is a test application developed for this exercise. It calls the business function that was just created when the find button is pressed. Use the application to retrieve the weather forecast for US zip codes.
Note.  The machines used in this exercise are “combination” machines. That is, the development client, enterprise server, database server, and security server are all hosted on the same machine. Due to a limitation of this configuration the business function that was just built is not actually the business function executed. The application is running the business function on the enterprise server despite the OCM records which would indicate otherwise. The steps above are correct and will produce a business function that correctly calls the business service, but this test is actually executing the business function on the enterprise server which was built and deployed while preparing this exercise. Standalone installs are also “combination” machines and will behave the same way. J