Beruflich Dokumente
Kultur Dokumente
Objectives
By the end of this lesson, you will be able to:
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.
Create an internal business service class that calls the web service proxy
Add code to retrieve the soft coding record from within the business service
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.
To allow JDeveloper to retrieve external WSDL files configure preferences with HTTP
Proxy information.
1. Launch JDeveloper
b. Port Number – 80
5. Press OK
6. Close JDeveloper
b. password: JDE
9. Press Add
11. Complete the following information about the published business service object
12. Press OK
1. With the project highlighted in the Application Navigator pane choose New from the
context menu.
14. 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.
15. 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.
16. 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.
17. Select Classes under the EnterpriseOne heading then choose Business Service Class
in the right hand pane. Click OK.
18. Enter the following information about the business service class to be created:
a. Name – WeatherForecastProcessor
19. 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){
}
21. 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.
22. Choose General on the left hand pane and Java class on the right. Press OK.
23. 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
24. 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.
25. 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.
26. Choose General on the left hand pane and Java class on the right. Press OK.
27. 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
28. Using the WeatherData class as a guide, add the following member variables to the
DayForecast object.
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);
}
}
33. 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);
34. Compile (context menu > Make) the project and resolve any compile errors.
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.
6.
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:
<portinfo>
<wsdlport namespaceURI="http://www.webservicex.net"
localpart="WeatherForecastSoap"/>
<operations>
<operation name='GetWeatherByZipCode'>
</operation>
<operation name='GetWeatherByPlaceName'>
</operation>
</operations>
</portinfo>
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.
c. Value – copy the <port-info> element from the generated xml document
35. 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.
d. User/Role – JDE
e. Environment – PD812
39. 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.
40. Press OK
41. 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.
47. 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;
48. Compile (context menu > Make) the project and resolve any compile errors.
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.
49. 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
50. 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="UTF8"?>
<internalgetweatherforecast>
<zipcode>80134</zipcode>
</internalgetweatherforecast>
51. 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).
52. Run or debug the main method until the web service is called successfully.
53. 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.
54. 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.
1. From the context menu of the workspace choose Deploy Development BSSV Server
55. 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.
b. Port Number: 80
56. Press OK
57. Progress of the build and deploy scripts can be viewed in the Apache Ant tab.
1. From OMW search for B5500050 and add it to the default project
58. Press “Design” for the business function then Start Business Function Design Aid
61. Add variables and code to initialize the XML parser. Add associated import
statement.
#include <xerceswrapper.h>
XRCSStatus = XRCS_initEngine();
if(XRCSStatus != XRCS_SUCCESS)
{
jdeVWriteLogEntry(_J("B5500050"), __FILE__, __LINE__, 0,
_J("XRCS_initEngine failed"));
return ER_ERROR;
}
62. 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.
if(XRCSStatus != XRCS_SUCCESS)
{
jdeVWriteLogEntry(_J("B5500050"), __FILE__, __LINE__, 0,
_J("XRCS_parseXMLString failed"));
XRCS_freeParser(hParser);
XRCS_terminateEngine();
return ER_ERROR;
}
63. Add code and variables to locate and set the value of the zip code element.
/* 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;
}
64. 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.
65. 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;
if ( idReturnValue == CallBSSVNoError ||
idReturnValue == CallBSSVNoErrorWithMessages)
{
}
else
{
}
66. 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>
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;
67. 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.
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;
}
68. Add a for loop to iterate through the first level children. Add associated variable.
unsigned int i = 0;
69. In the “for i” block add code to get the child element’s name and value. Add
associated variables.
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;
}
70. 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;
}
71. 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.
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;
}
72. Add another for loop (nested within the “for i" loop) to iterate over the detail’s child
elements and process them. Add associated variables.
73. 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;
}
}
}
74. 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;
75. 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;
76. 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);
77. 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.
78. 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.