<Lesson X>Exercise Title

Lesson 5

Create Service ConsumerBusiness Service

Objectives

By the end of this lesson, you will be able to:

  • 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

______

BStudentNotes

EStudentNotes

EInstructorNotes

EInstructorNotes

BActivityNotes

Activity Overview

EnterpriseOne consumes external web servicesthrough 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 codeas input and returns a one week forecast for that location.

In this exercise the student will

  • Create abusiness 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
  5. Host Name - www-proxy.us.oracle.com
  6. Port Number – 80
  7. Exceptions – Add these exceptions to the machine name already listed: |*.oracle.com|*.peoplesoft.com|*.jdedwards.com|*.mlab.jdedwards.com

  1. Press OK
  2. Close JDeveloper
Create a business service from OMW

To add a new Business Service in Object Management Workbench:

  1. Launch JD Edwards Solution Explorer
  2. user name: JDE
  3. password: JDE
  4. Type OMW in the fast path field and press enter
  5. Press Add
  6. Select the Business Function radio button and press OK
  7. Complete the following information about the published business service object
  8. Object Name – J5500050
  9. Description – Weather Forecast Processor
  10. Product Code/Product System Code – 55
  11. Source Language – BSSV
  12. Package Prefix – oracle.e1.bssv
  13. Press OK
  14. 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

  1. With the project highlighted in the Application Navigator pane choose New from the context menu.
  1. 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.

  1. A multi-step wizard is shown. Click next past the welcome page. For step one provide as the WSDL Document URL. Accept the defaults for the remainder of the wizard steps.

  1. 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.
  1. Select Classes under the EnterpriseOne heading then choose Business Service Class in the right hand pane. Click OK.
  2. Enter the following information about the business service class to be created:
  3. Name – WeatherForecastProcessor
  4. Method Name – getForecastByZip
  5. Input Class (type this - class does not yet exist) InternalGetWeatherForecast
  6. 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){

}

  1. Ctrl-click on getWeatherByZipCode to see that its signature is:

WeatherForecasts getWeatherByZipCode(String zipCode) throws java.rmi.RemoteException

  1. 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.
  2. Choose General on the left hand pane and Java class on the right. Press OK.
  3. Give the new class the following values and press OK
  4. Name – InternalGetWeatherForecast
  5. Package – oracle.e1.bssv.J5500050.valueobject
  6. Extends – oracle.e1.bssvfoundation.base.ValueObject
  7. 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

  1. 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.
  2. Choose General on the left hand pane and Java class on the right. Press OK.
  3. Give the new class the following values and press OK
  4. Name – DayForecast
  5. Package – oracle.e1.bssv.J5500050.valueobject
  6. Extends – oracle.e1.bssvfoundation.base.ValueObject
  7. 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;

  1. Generate accessors for the WeatherData member variables.
  2. Add an array of type DayForecast to the InternalGetWeatherForecast class.

private DayForecast[] details = null;

  1. Generate accessors for the InternalGetWeatherForecast member variables.
  2. 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);

}

}

  1. 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);

  1. 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

  1. 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.

  1. On step 2 of 6 uncheck Sign Outbound Messages

  1. On step 3 of 6 uncheck Verify Inbound Signed Request Body
  1. On step 4 of 6 uncheck Encrypt Outbound Messages
  2. On step 5 of 6 uncheck Decrypt Inbound Message Content
  1. 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.
  1. 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.
  2. 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=" 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.

  1. 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 MenusEnterpriseOne Life Cycle ToolsSystem Administration ToolsSoft Coding Administration and launch the Soft Coding Template application.
  2. Press Add
  3. Add a Soft Coding template with the following values
  4. Template Name – CUST_J5500050
  5. SoftCoding Key – J5500050
  6. Value – copy the <port-info> element from the generated xml document
  1. 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.
  2. Launch the Soft Coding Records application
  3. Press Add
  4. Enter values for these fields
  5. User/Role – JDE
  6. Environment – PD812
  7. Template Name – CUST_J5500050
  8. 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.
  1. Press OK
  2. 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.
  3. Press Ctrl-Enter and highlight E1SC. Press Enter.
  4. Type “J5500050” for the soft coding key.
  5. Tab twice and replace BS_NAME with WeatherForecastProcessor
  6. Tab and replace BS_METHOD_NAME with getForecastByZip
  7. Tab twice to exit the code template edit mode.
  8. 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;

  1. 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
  1. 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
  1. 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>

  1. 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).
  2. Run or debug the main method until the web service is called successfully.
  3. 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.

  1. 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
  1. 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.
  2. Host Name: www-proxy.us.oracle.com
  3. Port Number: 80
  1. Press OK
  2. 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
  1. Press “Design” for the business function then Start Business Function Design Aid
  2. Take the Form exit to Editthe business function
  3. Within Visual Studio find the declaration for GetWeatherForecast.
  4. 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;

}

  1. 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;

}

  1. 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;

}

  1. 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;

}

  1. 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

{

}

  1. 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);