4 Creating a Business Service

This chapter contains the following topics:

4.1 Understanding Business Services

Business services are JD Edwards EnterpriseOne Object Management Workbench (OMW) objects that are called by a published business service to accomplish a specific task. Business service classes are written in Java programming language and provide methods that access the business logic in JD Edwards EnterpriseOne for many supported business transactions, such as journal entries, exchange rates, accounts payable vouchers, inventory lookups, pricing, sales orders, and so on. A business service method can call a business function or a database operation. A utility business service performs a repeatable task and can be called by multiple business service classes.

This chapter focuses on business services that call a business function. Because many of the rules and best practices are the same for business services that call business functions and business services that call database operations, discussions in this chapter are applicable to both types of business services. However, some differences and exceptions exist, and Chapter 5, Creating a Business Service That Calls a Database Operation focuses on differences for each type of database operation.

You use wizards, which are provided by JDeveloper and the business services framework, and the Java programming language to create business service classes. If you are creating a new business service, you first create an OMW object. When you launch JDeveloper from OMW, the project should be created automatically. If the project is not created, you use the Project wizard that is provided by JDeveloper to create a project for your business service. You use the Business Service Class wizard to create a business service class that has one or more methods. A method can call a business function, a database operation, or another business service (for example, a utility business service method) to accomplish a specific task. The business services framework provides two wizards: the Create Business Function Call wizard to help you create methods that call business functions and the Create Database Call wizard to help you create methods that call database operations.

In addition to wizards, the business services framework provides a set of foundation packages that help you create a business service method. Each foundation package contains a set of interfaces and related classes. All business service classes extend from the BusinessService foundation class. The wizards that are provided by the business service framework enable you to create code that is specific for calling a business function or a database operation. Code samples, using a specific example of adding an address book record that uses AddressBook master business function, are provided throughout this chapter to demonstrate general concepts. Rules and best practices are discussed if they are applicable to the topic.

This business service class diagram shows the main business service class (AddressBookProcessor) and the internal value object class (InternalAddAddressBook) and its components:

Figure 4-1 Business service class diagram

Description of Figure 4-1 follows
Description of "Figure 4-1 Business service class diagram "

These features are illustrated in the diagram:

  • AddressBookProcessor extends BusinessService class.

  • InternalAddAddressBook and its components extend ValueObject class.

4.2 Developing a Business Service

A business service represents one or more Java classes that expose public methods. A business service class can expose multiple methods, such as addAddressBook, addAddressBookWithPhones, changeAddressBook, and so on. The methods access logic in JD Edwards EnterpriseOne and support a specific step in a business process, for example, adding an address book record. When you create the business service, you should consider including methods that have similar functionality and manageability in the same business service. If multiple processes are similar and can reuse code, then these methods should exist in the same business service.

4.2.1 IContext and IConnection Objects

A business service public method must contain two objects, IContext and IConnection, as part of its signature. The IContext object provides the default connection for the business function call and holds an identifier that ties together all processing for the business service. The IConnection object enables the business service method to be run under an explicit transaction; and if the connection is null, the default transaction is used. The context and connection objects are passed to the public methods of the business service class, which in turn passes these objects to any of the methods that call a business function. To indicate the boundaries of the internal method, business service public methods must call the inherited methods, startInternalMethod(context, "methodName", valueObject) before any other logic and finishInternalMethod(context, "methodName", valueObject) when all other processing is finished.

This code sample shows how to use IContext and IConnection:

public static E1MessageList addAddressBook(IContext context, IConnection 
connection, InternalAddAddressBook internalVO){
        //call start internal method, passing the context (which was 
        //passed from published business service)
       startInternalMethod(context, "addAddressBook",internalVO);
       ...
       // calls method which then executes BSFN AddressBookMBF
       E1MessageList messages = callAddressBookMasterMBF
(context,connection, internalVO, programId);
       ...
       // call finish internal method passing context
       finishInternalMethod(context, "addAddressBook");    
       //return status code from BSFN call 
       ...
       return messages;
   }

4.3 Managing Business Service Components

This section discusses naming conventions and concepts for creating business service classes, methods, internal value objects, and fields. Code samples are provided as examples for you to follow. Rules and best practices are also discussed.

4.3.1 Business Service Class Names

The naming convention for a business service class is to use the functional description with Processor added at the end of the name, for example, AddressBookProcessor and AddressBookQueryProcessor.

This code sample shows the naming convention for a business service class:

public abstract class AddressBookProcessor extends BusinessService {
   ....
   }

4.3.2 Business Service Method Names

A method is an operation that performs a business process. The naming convention for a business service public method is to name the public method the same name as the method in the published business service, for example, addAddressBook.

This code sample shows the naming convention for a public method:

public static E1MessageList addAddressBook(IContext context, 
IConnection connection, InternalAddAddressBook internalVO){
   ...
   }

4.3.3 Business Service Internal Value Object Names

Internal value object classes are the input and output parameters of the business service methods. These value objects are not published interfaces. You use these internal value objects to map values to and from a business function. Internal value objects can be composed of fields, compounds, and components.

The naming convention for an internal value object class is to use the published value object name with Internal added to the beginning of the name. Some examples of names for internal value objects are InternalAddAddressBook, InternalProcessPurchaseOrder, and InternalEntity.

This code sample shows the naming convention for an internal value object class:

    public class InternalAddAddressBook extends ValueObject {
   ....
   }

Database operations use a different convention for naming internal value objects.

See Understanding Database Operations.

4.3.3.1 Field Names

The naming convention for field names in the internal value object is to use a name that matches the data structure member names of the business function that is being called, for example, mnAddressNumber and szMailingName. The number of fields exposed through the internal value object may be larger than the published value object, and you should include all of the possible business data fields and flag fields in the internal value object because this object can be used by internal applications.

4.3.3.2 Compound and Component Names for a Business Service

By design, the internal value object has a flat hierarchical structure, meaning that the structure contains few, if any, compounds and components. Compounds and components that exist within an internal value object should be named similarly; for example, the compound name should be prefaced with the word Internal (such as, InternalPhones).

The following code sample shows an internal value object class that has one compound (internalPhones) and many field names (szAlphaName, szSearchType, and so on) at the top level that correspond to business function data structure member names.

public class InternalAddAddressBook extends ValueObject {
   private String szLongAddressNumber;
   private MathNumeric mnAddressBookNumber;
   private String szTaxId;
   private String szMailingName;
   private String szAddressLine1;
   private String szAddressLine2;
   private String szAddressLine3;
   private String szAddressLine4;
   private String szPostalCode;
   private String szCity;
   private String szCounty;
   private String szState;
   private String szCountry;
   private String szAlphaName;
   private String szSearchType;    
   private String szVersion;
   private String szBusinessUnit;
   private Date jdDateEffective;
   private ArrayList internalPhones;  
   ...
 }

4.3.4 Creating a Business Service Class

The business service foundation provides the Business Service Object wizard, which you use to create new business service classes. This wizard follows the methodology discussed in this document. The Business Service Object wizard prompts you for the class name, an internal input value object class, and a method name, and then it generates code for a business service class. The wizard also generates comments and TODO: statements where necessary to help you complete the generated code.

See "Creating Business Service Classes" in the JD Edwards EnterpriseOne Tools Business Services Development Guide.

4.3.4.1 Rules

When you create a business service class, follow these rules:

  • Business service classes are abstract classes and must extend the foundation class BusinessService. BusinessService is the parent class that provides foundation support for transactions and logging.

  • Business service classes have only static methods, so to reinforce static behavior and prevent the class from being instantiated, declare an abstract class.

This code sample illustrates extending the BusinessService class and declaring the class as abstract:

public abstract class AddressBookProcessor extends BusinessService {
       
   ...
   }

You design and develop a business service as a static class that processes multiple requests simultaneously. A static class means that only one instance of the class exists in Java virtual memory (JVM), regardless of the number of simultaneous requests being processed. These requests are also called threads.

Static classes reduce object creation. If a business service was not static, one business service would exist for each request. As each request finishes, the class would be released and eventually the system reclaims the memory that the class used. Creating and releasing objects repeatedly causes performance degradation, because more memory is used and more CPU cycles are required.

To ensure that the business service provides a thread-safe environment, you cannot use instance variables in the business service class. An instance variable is a value that is useful to only one request, for example, a counter. A thread-safe environment means that the multiple requests (threads) that are being processed simultaneously do not interfere with each other. The absence of instance variables helps ensure thread safety at compile time. You can include static variables in the business service class. A static variable is a value that is useful to all requests, for example, a cached value that is used to specify a language. A static variable is shared data, independent of a request.

4.3.5 Declaring a Business Service Public Method

A public method is an operation that can be used by other classes and methods. The signature takes IContext, IConnection, and an internal value object and returns E1MessageList.

You can add additional public methods to a business service class by accessing the JDeveloper Code Templates and selecting E1SM – EnterpriseOne Business Service Method Call. This template generates code for a public method. You press Tab to move through the highlighted fields and complete the code. This template enforces methodology and gives you a head start for developing a new public method.

This code sample shows how to declare a public message:

public static E1MessageList  addAddressBook(IContext context, 
IConnection connection, InternalAddAddressBook internalVO){
       startInternalMethod(context, "addAddressBook", internalVO);
       // call BSFN AddressBookMBF
       E1MessageList messages = callAddressBookMasterMBF(context, 
connection, internalVO, programID); 
       finishInternalMethod(context, "addAddressBook");        
       return messages;
   }

4.3.5.1 Rules for Declaring a Business Service Public Method

When you declare a public method for a business service class, follow these rules:

  • Business service classes must expose public static methods to a published business service class. A business service class cannot contain instance variables or nonstatic methods.

  • Business service methods that are to be used by a published business service must return an E1MessageList object to that published business service. The caller of the business service determines how to handle the errors and whether to create and throw an exception. The signature of the business service method must contain IContext and IConnection objects and a value object class that represents an internal value object that passes values to the business function calls.

4.3.5.2 Best Practices for Private and Protected Methods

When you declare methods other than the public method (for example, a utility method), consider these best practices:

  • Declare nonpublic methods as protected or private; all methods must be static.

  • Keep scope as private as possible.

4.3.6 Creating Internal Value Objects

Internal value object classes and their components extend the foundation ValueObject class.

The business service foundation provides value object class wizards that help you create internal value object classes that follow methodology rules. The value object wizards also assist you by pulling useful information from the JD Edwards EnterpriseOne data dictionary into the Javadoc for value objects. You must create accessor methods (get and set methods) because the value object wizards do not generate these methods. Also, you must provide the description name of the field for the Javadoc.

The value object wizards enable you to create value object classes from the data structures that are defined within a business function or from database tables or business views. Remember that the wizard uses the field name that comes from the data structure, table, or business view to generate member variables for the internal value object class. These generated variables look very much like JD Edwards EnterpriseOne data items.

This code sample is from a business function:

    /**
    * Address Line 1
    * EnterpriseOne Alias: ADD1
    * EnterpriseOne field length:  40 
    */
   private String szAddressLine1 = null;

This code sample is from a table:

    /**
    * CreditMessage
    * A value in the user defined code table 
    * that indicates the credit status of a customer or supplier
    * EnterpriseOne Alias: CM
    * EnterpriseOne field length:  2 
    * EnterpriseOne User Defined Code: 00/CM
    */
   private String F0101_CM = null;

See Understanding Database Operations.

See "Creating Business Function Calls" in the JD Edwards EnterpriseOne Tools Business Services Development Guide.

See "Creating Database Operation Calls" in the JD Edwards EnterpriseOne Tools Business Services Development Guide.

4.3.6.1 Rules for Internal Value Object

This list identifies the rules for internal value objects:

  • The structure of an internal value object has a flatter hierarchy than the published value object, because the internal value object has few if any compounds or components.

  • The collections within the internal value object can be created using either ArrayList or Array. An ArrayList is easier to work with because it can be dynamically sized. Arrays are necessary when the internal value object will be serialized. A business service that exposes JD Edwards EnterpriseOne functionality to a third party can use an ArrayList. A business service called from a business function (for example, using web service callout when JD EnterpriseOne is a web service consumer) must use an Array because the ArrayList data type cannot be serialized.

    For example, you can use the following code sample to declare the compound for phones:

    Private ArrayList internalPhones = null;
    

    ArrayList is populated during business service processing, and in the preceding code sample, the collection contains InternalPhone objects.

    Or you can use this code sample to declare the compound for phones:

    Private InternalPhone[] internalPhones = null;
    
  • The data types for internal value object classes match the types used in the JD Edwards EnterpriseOne data structures, as identified in the following table:

    Internal Value Object Data Type Usage
    oracle.e1.bssvfoundation.util.MathNumeric Use for all fields that are declared as numeric in JD Edwards EnterpriseOne.
    java.lang.String Use for string and char fields.
    java.util.Date Use for all JDEDate fields in JD Edwards EnterpriseOne.
    java.util.GregorianCalendar Use for all UTIME fields in JD Edwards EnterpriseOne.

4.3.6.2 Best Practices for Internal Value Object

When deciding which fields to include in the internal value object class, consider that all data fields that the application accepts and the function uses are valid fields.

If an internal business function call passes processing fields, you must determine whether these fields should be exposed in the internal value object class. An example of this type of processing field would be a field that is used to manipulate a cache. If a business service is called from another business service and a processing field is exposed and passed in from the calling business service, will the behavior be as expected? If not, that processing field should not be exposed in the internal value object class. Fields that should not be exposed in the internal value object class can be handled by creating another value object called InternalProcessing. The InternalProcessing value object can contain all unexposed processing fields as member variables. The InternalProcessing value object should not be part of the InternalValueObject class and should not be exposed from the business service method signature. The InternalProcessing value object can be passed in the business function method calls but is not passed in or out of the business service method.

This code sample shows an InternalProcessing value object:

/**
* InternalProcessing contains processing fields used for 
* ProcessPurchaseOrderAcknowledge
* but these will not be exposed fields.
*/
public class InternalProcessing extends ValueObject {
   /**
    * Action Flag
    * EnterpriseOne Key Field: false
    * EnterpriseOne Alias: ACFL
    * EnterpriseOne field length:  1
    * EnterpriseOne User Defined Code: 08/AC
    */
   private String cProcessHeaderDetailFlag = null;
  /**
    * Job Number
    * EnterpriseOne Key Field: false
    * EnterpriseOne Alias: JOBS
    * EnterpriseOne field length:  8
    * EnterpriseOne decimal places: 0
    * EnterpriseOne Next Number: 00/4
    */
   private MathNumeric mnF4311JobNumber = null;
   /**
    * Transaction ID
    * EnterpriseOne Key Field: false
    * EnterpriseOne Alias: TCID
    * EnterpriseOne field length:  15
    * EnterpriseOne decimal places: 0
    */
   private MathNumeric mnTransactionID = null;
   /**
    * Process ID
    * EnterpriseOne Key Field: false
    * EnterpriseOne Alias: PEID
    * EnterpriseOne field length:  15
    * EnterpriseOne decimal places: 0
    */
   private MathNumeric mnProcessID = null;
   /**
    * Job Number
    * EnterpriseOne Key Field: false
    * EnterpriseOne Alias: JOBS
    * EnterpriseOne field length:  8
    * EnterpriseOne decimal places: 0
    * EnterpriseOne Next Number: 00/4
    */
   private MathNumeric mnCacheJobNumber = null;
}

This code sample shows how to pass the InternalProcessing value object to business function methods:

     public static E1MessageList processPurchaseOrderAcknowledge
(IContext context,IConnection connection, InternalProcessPurchase
OrderAcknowledge internalVO){
       //Call start internal method, passing the context (which was 
       //passed from published business service).
       startInternalMethod(context, "processPurchaseOrderAcknowledge",
 internalVO);
       //Create new message list for business service processing.
       E1MessageList messages = new E1MessageList();
       InternalProcessing internalProcessingVO = new 
InternalProcessing();
       //TODO: call method (created by the wizard), which then 
       //executes Business Function or Database operation.
       messages = callPurchaseOrderAcknowledgeNotify(context, 
connection, internalVO,internalProcessingVO);
       //TODO:  add messages returned from E1 processing to business 
       //service message list.
       //Call finish internal method passing context.
       finishInternalMethod(context, "processPurchaseOrderAcknowledge
");
       //Call finish internal method passing context.
       return messages;
   }
   /**
    * Calls the PurchaseOrderAcknowledgeNotify(B4302190) business 
    * function which has the D4302190A data structure.
    * @param context conditionally provides the connection for the 
    * business function call and logging information
    * @param connection can either be an explicit connection or null.
    * If null the default connection is used.
    * @param TODO document input parameters
    * @return A list of messages if there were application errors, 
    * warnings,or informational messages. Returns null if there were 
    * no messages.
    */
   private static E1MessageList callPurchaseOrderAcknowledgeNotify
(IContext context, IConnection connection, InternalProcessPurchase
OrderAcknowledge internal VO, InternalProcessing internalProcessingVO) {
       BSFNParameters bsfnParams = new BSFNParameters();
       // map input parameters from input value object
       bsfnParams.setValue("cProcessHeaderDetailFlag", 
internalProcessingVO.
get CProcessHeaderDetailFlag());
       bsfnParams.setValue("mnF4311JobNumber", internalProcessingVO.
getMnF4311JobNumber());
       bsfnParams.setValue("mnTransactionID", internalProcessingVO.
getMnTransactionID());
       bsfnParams.setValue("mnProcessID", internalProcessingVO.get
MnProcessID());
       bsfnParams.setValue("mnCacheJobNumber", internalProcessingVO.
getMnCacheJobNumber());
       bsfnParams.setValue("cHeaderOrderStatusCode", internalVO.get
CHeaderOrderStatusCode());
       bsfnParams.setValue("mnOrderNumber", internalVO.getMnOrder
Number());
       bsfnParams.setValue("szOrderType", internalVO.
getSzOrderType());
       bsfnParams.setValue("szOrderCompany", internalVO.getSzOrder
Company());
       ....
       //get bsfnService from context
       IBSFNService bsfnService = context.getBSFNService();
       //execute business function
       bsfnService.execute(context, connection, "PurchaseOrder
AcknowledgeNotify",bsfnParams);
       //map output parameters to output value object
       internalProcessingVO.setCProcessHeaderDetailFlag(bsfnParams.
getValue("cProcessHeaderDetailFlag").toString());
       internalProcessingVO.setMnF4311JobNumber((MathNumeric)bsfn
Params.getValue("mnF4311JobNumber"));
       internalProcessingVO.setMnTransactionID((MathNumeric)bsfn
Params.getValue("mnTransactionID"));
       internalProcessingVO.setMnProcessID((MathNumeric)bsfnParams.
getValue("mn⇒ProcessID"));
       internalProcessingVO.setMnCacheJobNumber((MathNumeric)bsfn
Params.getValue("mnCacheJobNumber"));
       internalVO.setCHeaderOrderStatusCode(bsfnParams.
getValue("cHeaderOrderStatusCode").toString());
       internalVO.setMnOrderNumber((MathNumeric)bsfnParams.getValue
("mnOrderNumber"));
        ...
       //return any errors, warnings, or informational messages to the
       //caller
       return bsfnParams.getE1MessageList();
   }

4.4 Calling Business Functions

A business function is an encapsulated set of business rules and logic that can be reused by multiple applications. Business functions provide a common way to access the JD Edwards EnterpriseOne database. A business function performs a specific task.

You use the business service foundation Business Function Call Wizard to create a business function call.

See "Understanding Business Function Calls" in the JD Edwards EnterpriseOne Tools Business Services Development Guide.

This code sample is generated by the Business Function Wizard:

       //calls method which then executes BSFN AddressBookMBF
       //RI: This private function is created by the wizard, The 
       //business function will be executed inside this internal function
       messages = callAddressBookMasterMBF(context, internalVO,programId);

The wizard creates a generic method. You modify the signature of the method and complete the code for the objects that will be accessed for mapping to and from the business function call. The wizard creates InputVOType as a placeholder in the signature for the internal value object class name that you provide.

This code sample shows a business function call that was created by the wizard:

/**
  * Calls the AddressBookMasterMBF(N0100041) business function which has 
  * the D0100041 data structure.
  * @param context provides the connection for the business function call 
  * and logging information
  * @param TODO document input parameters
  * @return A list of messages if there were application errors, warnings,
  * or informational messages. Returns null if there were no messages.
  */
     private static E1MessageList callAddressBookMasterMBF(IContext 
context, IConnection connection, InputVOType internalVO) {
     BSFNParameters bsfnParams = new BSFNParameters();
     // map input parameters from input value object
     bsfnParams.setValue("cActionCode", internalVO.getCActionCode());
     bsfnParams.setValue("cUpdateMasterFile", internalVO.getCUpdateMaster
File());
     bsfnParams.setValue("cProcessEdits", internalVO.getCProcessEdits());
     bsfnParams.setValue("cSuppressErrorMessages", internalVO.getCSuppress
ErrorMessages());
     bsfnParams.setValue("szErrorMessageID", internalVO.getSzErrorMessage
ID());
     bsfnParams.setValue("mnSameAsExcept", internalVO.getMnSameAsExcept());
     bsfnParams.setValue("mnAddressBookNumber", internalVO.getMnAddressBook
Number());
     ...
       try {
          //get bsfnService from context
          IBSFNService bsfnService = context.getBSFNService();
          //execute business function
          bsfnService.execute(context, connection, "AddressBookMasterMBF", 
bsfnParams);
      } catch (BSFNServiceInvalidArgException invalidArgEx) {
         //Create error message for Invalid Argument exception and return 
         //it in ErrorList
          E1MessageList returnMessages = new E1MessageList();
          returnMessages.addMessage(new E1Message(context, "018FIS", 
invalidArg 
Ex.getMessage()));
          return returnMessages;
      } catch (BSFNServiceSystemException systemEx) {
          //Create error message for System exception and return it in 
          //ErrorList
          E1MessageList returnMessages = new E1MessageList();
          returnMessages.addMessage(new E1Message(context, "019FIS", 
systemEx.getMessage()));
          return returnMessages;
      }
     //map output parameters to output value object
     internalVO.setMnAddressBookNumber(bsfnParams.getValue("mnAddressBook
Number");
     internalVO.setSzLongAddressNumber(bsfnParams.getValue("szLongAddress
Number");
     internalVO.setSzTaxId(bsfnParams.getValue("szTaxId"));
     internalVO.setSzAlphaName(bsfnParams.getValue("szAlphaName"));
     internalVO.setSzSecondaryAlphaName(bsfnParams.getValue("szSecondary
AlphaName"));
     internalVO.setSzMailingName(bsfnParams.getValue("szMailingName"));
     internalVO.setSzSecondaryMailingName(bsfnParams.getValue("szSecondary
MailingName"));
     internalVO.setSzDescriptionCompressed(bsfnParams.getValue
("szDescriptionCompressed"));
     internalVO.setSzBusinessUnit(bsfnParams.getValue("szBusinessUnit"));
     internalVO.setSzAddressLine1(bsfnParams.getValue("szAddressLine1"));
     //return any errors, warnings, or informational messages to the caller
     return bsfnParams.getE1MessageList();
   }

After the wizard creates the code for the generic method, you modify the code as needed. You might need to:

  • Add parameters to be passed.

    At a minimum, the internal value object includes an IContext object and an IConnection object, generated by the wizard, and an internal value object, which you define. You may need to pass an additional parameter such as an internalProcessing value object for processing fields that should not be exposed.

  • Fix mappings if required.

    The generated code assumes that all fields can be mapped directly to and from the internal value object. If an additional structure exists in the internal value object or some fields should be mapped from class constant fields, you must fix the mapping statements where this assumption is not true. JDeveloper identifies incorrect statements.

  • Fix the data type of the object retrieved from bsfnParams.

    The generated code adds a cast argument when mapping to internalVO by getting values from the bsfnParams object. The bsfnParams object is a collection of objects and when an object is retrieved, the type needs to be cast to the correct data type so that it can be added to the internalVO reference, as illustrated in this code sample:

    private static E1MessageList callAddressBookMasterMBF(IContext context, 
                                             IConnection connection,
                                             InternalAddAddressBook internalVO, 
                                             String programId) {
           // create new bsfnParams object
           BSFNParameters bsfnParams = new BSFNParameters();
          //set values for bsfn params based on internal vo attribute values
           bsfnParams.setValue("cActionCode", ACTION_CODE_ADD);
           bsfnParams.setValue("cUpdateMasterFile", UPDATE_MASTER_TRUE);
           bsfnParams.setValue("cProcessEdits", PROCESS_EDITS_TRUE);
           bsfnParams.setValue("cSuppressErrorMessages", SUPPRESS_ERROR_FALSE);
           bsfnParams.setValue("szVersion", internalVO.getSzVersion());
           bsfnParams.setValue("mnAddressBookNumber", 
                               internalVO.getMnAddressBookNumber());
           bsfnParams.setValue("szLongAddressNumber", 
                               internalVO.getSzLongAddressNumber());
           bsfnParams.setValue("szTaxId", internalVO.getSzTaxId());
           bsfnParams.setValue("szSearchType", internalVO.getSzSearchType());
           ...
           bsfnParams.setValue("szState", internalVO.getSzState());
           bsfnParams.setValue("szCountry", 
                               internalVO.getSzCountry());
            //set program id to value retrieved in business service properties
            bsfnParams.setValue("szProgramId", programID );
           try {
                //get bsfnService from context
                IBSFNService bsfnService = context.getBSFNService();
                //execute business function
    
                bsfnService.execute(context, connection, "AddressBookMaster
    MBF", bsfnParams);
            } catch (BSFNServiceInvalidArgException invalidArgEx) {
               //Create error message for Invalid Argument exception and 
               //return it in ErrorList
                E1MessageList returnMessages = new E1MessageList();
                returnMessages.addMessage(new E1Message(context, "018FIS", 
    invalidArgEx.getMessage()));
                return returnMessages;
            } catch (BSFNServiceSystemException systemEx) {
                //Create error message for System exception and return it in 
                //ErrorList
                E1MessageList returnMessages = new E1MessageList();
                returnMessages.addMessage(new E1Message(context, "019FIS", 
    systemEx.getMessage()));
                return returnMessages;
            }
           //set internal VO attributes based on values passed back from bsfn
           //Must cast object to appropriate data type coming from bsfnParams 
    collection.
           internalVO.setMnAddressBookNumber((MathNumeric)bsfnParams.
    getValue("mnAddressBookNumber"));
           internalVO.setSzLongAddressNumber((String)bsfnParams.
    getValue("szLongAddressNumber"));
           internalVO.setSzCountry((String)bsfnParams.getValue("szCountry"));
           internalVO.setSzBusinessUnit((String)bsfnParams.
    getValue("szBusinessUnit"));
           internalVO.setJdDateEffective((Date)bsfnParams.
    getValue("jdDateEffective"));
           E1MessageList messages = bsfnParams.getE1MessageList();
           //set prefix to the message list being returned to provide more 
    information on errors
           bsfnParams.getE1MessageList().setMessagePrefix("AB MBF N0100041");
           //return any errors, warnings, or informational messages to the 
           //caller
           return bsfnParams.getE1MessageList();
       }
    

When you run a business function, two exceptions, BSFNServiceInvalidArgException and BSFNServiceSystemException, are thrown. The generated code runs the business function within a try/catch block, and in the event that an invalid argument is passed to the business function, the error will be caught and added to the message list and returned to the caller. The same behavior occurs if a database exception occurs within the business function. This code sample shows a try/catch block:

try {
    //get bsfnService from context
    IBSFNService bsfnService = context.getBSFNService();
    //execute business function
    bsfnService.execute(context, connection, "AddressBookMasterMBF", 
bsfnParams);
} catch (BSFNServiceInvalidArgException invalidArgEx) {
   //Create error message for Invalid Argument exception and return it in 
ErrorList
    E1MessageList returnMessages = new E1MessageList();
    returnMessages.addMessage(new E1Message(context, "018FIS", 
invalidArgEx.getMessage()));
    return returnMessages;
} catch (BSFNServiceSystemException systemEx) {
    //Create error message for System exception and return it in ErrorList
    E1MessageList returnMessages = new E1MessageList();
    returnMessages.addMessage(new E1Message(context, "019FIS", 
systemEx.getMessage()));
    return returnMessages;
}

4.5 Calling Database Operations

You can create business services that call database operations. You use the business service foundation Database Call wizard to create these business service methods. Database operations include query, insert, update, and delete.

This code sample shows code that is generated by the Database Call Wizard:

          //calls method which then executes jdbj callto the table 
          //selected.
       messages = selectF0101(context, internalVO, maxRows);

The wizard creates a generic method. You modify the signature of the method and complete the code for the objects that will be accessed for mapping to and from the database operation call. The wizard creates InputVOType as a placeholder in the signature for the internal value object class name that you provide.

The wizard generates unique code for each type of database operation.

4.6 Calling Other Business Services

A method in one business service can call a method in another business service. For example, SupplierProcessor.addSupplier could call AddressBookProcessor.addAddressBook or AddressBookProcessor.addAddressBook could call PhonesProcessor.addPhones.

In this code sample, the PhonesProcessor.addPhones method takes an internalProcessPhones value object; this object is created and populated before calling the method:

//RI:  Business service call to business service
        //call PhonesProcessor         
        //only call phones processor if phones exist.
        if (internalVO.getInternalPhones() != null) {
        //create new internalVO for phones processor
           InternalProcessPhone phones = new InternalProcessPhone();
           //map data from internalVO to phones processor internalVO
           phones.setMnAddressBookNumber(internalVO.getMnAddressBook
Number());
           phones.setPhones(internalVO.getInternalPhones());
           phones.setSzProgramId(programId);
           //call phones processor to add phones
           E1MessageList phonesMessages = 
           RI_PhonesProcessor.addPhones(context, connection, phones);
           //If errors occur, change the error type to WARNING because 
           //we don't want to stop processing of Address Book record due 
           //to error while adding phones, interpret as warning instead.
           if (phonesMessages.hasErrors()) {
              phonesMessages.changeMessageType(E1Message.ERROR_MSG_TYPE, 
                                               E1Message.WARNING_MSG_TYPE);
              //set list of phones to list w/ only added phones.
              internalVO.setInternalPhones(phones.getPhones());
           }
           //add messages returned from phones processor
            messages.addMessages(phonesMessages);
         }

A business service method can call a business service utility method. For example, PurchaseOrderProcessor. processPurchaseOrder can call ItemProcessor.processItem and EntityProcessor.processEntity.

This code sample shows a business service call to a business service utility:

//RI:  Business service call to business service
    //call business service utility
    //This business service returns a status code, this example will not 
    //use the status code to drive functionality, but
    //could be evaluated to change processing.
    InternalEntityUtility utilityEntity = new InternalEntityUtility();
    utilityEntity.setMnAddressBookNumber(internalVO.getMnAddressBook
Number());
    utilityEntity.setSzLongAddressNumber(internalVO.getSzLongAddress
Number());
    utilityEntity.setSzTaxId(internalVO.getSzTaxId());
    
    E1MessageList entityMessages = EntityProcessor.processEntity(context, 
connection, utilityEntity);
    internalVO.setMnAddressBookNumber(utilityEntity.getMnAddressBook
Number());
    internalVO.setSzLongAddressNumber(utilityEntity.getSzLongAddress
Number());
    internalVO.setSzTaxId(utilityEntity.getSzTaxId());
    //Don't stop processing in case of errors from utility, change type to 
    // warning and add them to error collection.
    if(entityMessages.hasErrors())
      entityMessages.changeMessageType(E1Message.ERROR_MSG_TYPE,E1Message.
WARNING_MSG_TYPE);
    //take messages generated from EntityProcessor and add them to the 
    //high level value object.
     if (retMessages == null)
     {
       retMessages = entityMessages;
     }
     else
     {
       retMessages.addMessages(entityMessages);
     }

4.7 Managing Business Service Properties

Business service properties provide a way for you to change a value in a business service method without changing the method code. A business service property consists of a key and a value. The key is the name of the business service property and cannot be changed. You use OMW to create business service properties.

4.7.1 Standard Naming Conventions for the Property Key

You can organize business service properties at the system level or at the business service level. Business service properties defined at the system level are used by more than one business service. Business service properties defined at the business service level are used by only one business service.

4.7.1.1 System-Level Business Service Properties

The naming convention for system-level business service properties, used by multiple business services, is to use SYS followed by a meaningful name that you provide. The naming convention looks like this:

SYS_Free_Form

where Free_Form is a name that you enter.

This is an example of a name for a system-level business service property that enables a user to define the program ID that is to be used by any of the master business functions (MBFs) for processing:

SYS_PROGRAM_ID

4.7.1.2 Business Service Level Business Service Properties

The naming convention for business service-level business service properties, used by only one business service, is to use the BusinessServiceName followed by a meaningful name that you provide. The naming convention looks like this:

BusinessServiceName_Free_Form

This table provides examples of names for business service-level business service properties:

Business Service Property Name Usage
J0100001_AB_MBF_VERSION This business service property allows the user to define which processing version to use when running the Address Book MBF when processing from the AddressBook business service.
J0100021_AB_MBF_VERSION This business service property allows the user to define which processing version to use when running the Address Book MBF when processing from the Customer business service.
J0100021_CUS_MBF_VERSION This business service property allows the user to define which processing version to use when running the Customer MBF when processing from the Customer business service.
J4200040_BYPASS_BSFN_WARNINGS This business service property sets a Bypass Warning Flag for sales order processing. If 1, the bypass warning flag is true - treat as warnings, do not stop processing. If 0, the bypass warning flag is false - treat warnings as errors, stop processing.
J4200040_PREFIX_1 This business service property adds prefix text to an error message that is returned from a business function to give more specific context to the error message. For example, if an error is returned for a detail line, the value for the prefix message could be "Line no. sent in:". This text is then concatenated with the line number data and added as a prefix to the error message.

See Handling Errors in the Business Service.


4.7.2 Business Service Property Methods

The ServicePropertyAccess class provides two utility methods for accessing property values. These methods are:

  • Get property value and return null/blank if no value exists in the database, illustrated in this code sample:

    getSvcPropertyValue(IContext context, java.lang.String key) 
      
    Example:  String processingVersion = ServicePropertyAccess.
    getSvcPropertyValue(context,SVC_PROPERTY_AB_MBF_PROC_VERSION );
    
  • Get service property value, but if the value is null, use the provided default value, illustrated in this code sample:

    String getSvcPropertyValue(IContext context, java.lang.String key, 
    java.lang.String defaultVal)
    Example:  String programID = ServicePropertyAccess.getSvcPropertyValue
                         ((Context)context, SVC_PROPERTY_PROGRAM_ID,"BSSV");
    

Both of these methods throw a ServicePropertyException message when the property key is null or does not exist in the database. A business service must call these methods in a try/catch block and catch the ServicePropertyException. You can handle business service property errors by creating a new E1Message object that collects the business service property exception message as well as other errors retrieved from business function calls. The business service returns the E1Message object to its caller, and the exception and error messages can be included in the BusinessServiceException, which is thrown by the published business service. When you create the business service, you determine whether to continue processing if an exception is caught. If you allow processing to continue, a failure (an invalid value was passed because of the ServicePropertyException) could occur in the call to the business function. Including text for the exception offers more information as to why the error occurred.

You can use the code template E1SD – EnterpriseOne Add Call to Service Property with Default Value to generate code that calls the business service property method where a default value is passed. The template generates the code and highlights the fields that you need to change.

You can use this code sample as a model for handling business service properties:

   public static final String SVC_PROPERTY_AB_MBF_PROC_VERSION = 
"J010010_AB_MBF_PROC_VERSION";
   public static final String SVC_PROPTERY_PROGRAM_ID = 
"SYS_PROGRAM_ID";
   ... 
               //Call access  Business Service Property to retrieve 
               //Program ID and processing Version
               //create string so it can be passed to bsfn call
               String programId = null;
               //Call to return Business Service Properties - if fails  
               //to retrieve value, use default and continue.
               try {
                   programId = 
                           BusinessServicePropertyAccess.
getSvcPropertyValue(context, SVC_PROPERTY_PROGRAM_ID, "BSSV");
               } catch (BusinessServicePropertyException se) {
                   context.getBSSVLogger().app(context,"@@@Attempt to 
retrieve Business Service Property failed", "Verify that key exists 
in database as entered.", SVC_PROPERTY_PROGRAM_ID, se);
                   //Create new E1 Message using DD item for business 
                   //service property exception.
                   E1Message scMessage = new E1Message(context, 
"001FIS", SVC_PROPERTY_PROGRAM_ID);
                   //Add messages to final message list to be returned.
                   messages.addMessage(scMessage);
               }

4.8 Handling Errors in the Business Service

The business service object exposes public methods that call business functions or database operations to perform a specific business process. During business processing, the business service captures errors and warnings in an array list and returns this information to the published business service in an E1MessageList object.

4.8.1 Rules

All business services must return an E1MessageList object to the published business service. The E1MessageList object must contain all errors, warnings, and information messages that were collected throughout the business service processing.

4.8.2 Best Practices

When writing code for handling errors, remember these best practices:

  • The business service foundation provides methods that you can use to add prefix messages to errors. You should add useful information such as key information or detail line information when returning error messages. If you add a prefix to an E1MessageList object that contains no errors, no prefix will be appended and no error will be thrown.

    This example shows how to add a prefix, which names the business function where the messages occurred, to the message list:

    bsfnParams.getE1MessageList().setMessagePrefix("AddressBookMasterMBF
    (N0100041): ");
    

    If the prefixed text can be translated to another language, use a business service property with this naming convention for the text:

    BSSVname_PREFIX_sequence

    Use this code to attach the business service property as a prefix in an error message:

    private static final String SVC_PROPERTY_PHONE_ERR_PREFIX = 
    "JR010030_PREFIX_1";
    ...
    phonesMessages.setMessagePrefix(SVC_PROPERTY_PHONE_ERR_PREFIX +(i+1));
    
  • If an error condition that is not handled by the business function call occurs, you can use a business service foundation method to create a new error and add the error to the message list. This can be used when a checked exception is thrown by business service foundation and you want to collect the exception as a message in the E1MessageList. Examples of situations requiring a new E1Message are calling the BSSVDataFormatter utility and retrieving business service properties. Because the alias for the JD Edwards EnterpriseOne error to be returned must be passed to the method, an error data dictionary item must exist in JD Edwards EnterpriseOne.

    This code shows creating a new E1Message:

    new E1Message(context, "001FIS", PROGRAM_ID);
    

4.8.3 Collecting Errors

When multiple business functions are called, a potential exists for several errors and warnings to be returned by the business functions. You should gather all errors and warnings in the E1MessageList object for all of the business functions that are called so that all errors and warnings are sent to the caller.

When a business service calls a business function, the business function collects all style errors, defined as error messages in the JD Edwards EnterpriseOne data dictionary, in an ArrayList. The business function always returns an E1MessageList object to its caller. The E1MessageList object contains an ArrayList of the messages returned from a business function call. If no messages are returned, the ArrayList is empty. To determine the state of the E1MessageList object or to determine whether any errors have occurred, you can use one of these methods to call the E1MessageList:

  • hasErrors()

  • hasWarning()

  • hasInfoMessages()

The business service foundation provides several methods that let you add, remove, change, and append to the ArrayList messages.

This code sample shows how to use hasErrors() to call the E1MessageList:

                if (messages.hasErrors()) {
               //Get the string representation of all the messages.
               String error = messages.getMessagesAsString();
               //Throw new BusinessServiceException
               throw new BusinessServiceException(error, context);
           }  

This code sample shows adding a prefix to an E1MessageList to show where errors occurred in a business function:

private static E1MessageList callAddressBookMasterMBF(IContext context,
                                       IConnection connection,
                                       InternalValueObject internalVO,
                                       String programId){
   
   //create new bsfnParams object
   BSFNParameters bsfnParams = new BSFNParameters();
   //set values for bsfn params based on internal vo attribute values
   bsfnParams.setValue("mnAddressBookNumber", 
                       internalVO.getMnAddressBookNumber());
   bsfnParams.setValue("szLongAddressNumber", 
                       internalVO.getSzLongAddressNumber());
   bsfnParams.setValue("szTaxId", internalVO.getSzTaxId());
   ...
   //execute the AddressBookMasterMBF business function
   bsfnService.execute(context,connection, "AddressBookMasterMBF",bsfnParams);
   //set internal VO attributes based on values passed back from bsfn
internalVO.setMnAddressBookNumber((MathNumeric)bsfnParams.getValue
("mnAddressBookNumber"));
internalVO.setSzLongAddressNumber((String)bsfnParams.getValue
("szLongAddressNumber"));
   internalVO.setSzTaxId((String)bsfnParams.getValue("szTaxId").
toString());
   internalVO.setSzAlphaName((String)bsfnParams.getValue
("szAlphaName"));
   ...
bsfnParams.getE1MessageList().setMessagePrefix("AddressBookMasterMBF
(N0100041): ");
    //return any errors, warnings, or informational messages to the 
    //caller
   return bsfnParams.getE1MessageList();

This code sample shows calling the PhonesMBF within a loop and handling the errors that are being collected:

public static E1MessageList addPhones(IContext context, IConnection 
connection,
 InternalProcessPhone internalVO){
       E1MessageList retMessages = new E1MessageList();
       
       E1MessageList phonesMessages;
       //Add All phones passed in
       for (int i = 0; i < internalVO.getPhones().length; i++) {
           phonesMessages = callPhonesMBFtoAdd(context, connection, 
internalVO, i);
           //set message prefix to add line number
           phonesMessages.setMessagePrefix("Phone line no. sent in"+
(i+1));
           //collect messages for all phones.
           retMessages.addMessages(phonesMessages);
       }
      //send messages back to caller
      return retMessages;

This sample code shows returning the messages to the caller and adding them to the existing message object:

  public static E1MessageList addAddressBook(IContext context, 
IConnection connection,
  InternalAddAddressBook internalVO){
       E1MessageList retMessages = null;
       ...
            //if no errors in address book, continue and add phones.
       if (retMessages != null && !retMessages.hasErrors()) {
           E1MessageList phonesMessages;
      //RI:  Business service call to business service
           //call PhonesProcessor
           ...
           phonesMessages = PhonesProcessor.addPhones(context,
connection,phones);
           //If errors occur, change the error type to WARNING
           if (phonesMessages != null && phonesMessages.hasErrors()){
               phonesMessages.changeMessageType(E1Message.ERROR_MSG_
TYPE, 
E1Message.WARNING_MSG_TYPE);
           }
           if (retMessages == null)
           {
             retMessages = phonesMessages;
           }
           else
           {
             retMessages.addMessages(phonesMessages);
           }
       } 
       ....
       return retMessages;

4.9 Modifying a Business Service

You can modify a business service providing that the change does not alter the signature or behavior of the published business service. You can change a business service in many ways, and how you change the business service depends on the business service design and the type of change that is required. Any change to a business service should be determined as part of the design process. You should ask yourself these questions to determine whether the modifications affect the published business service:

  • Am I adding or removing required fields in the value object?

  • Will these changes affect the way the existing published business service behaves?

If the answer is yes, you must create a new business service. You can copy and modify the existing business service to create a new business service.

4.10 Documenting a Business Service

When you create code, use standard Javadoc practices to document both the business service and the published business service classes. Javadoc comments should be added for member variables for all value objects. Most of this is generated by the value object wizards. However, you are responsible for making sure that the description for exposed fields is added and is in context with the business process that is being supported.

This code is an example of Javadoc for a member variable:

/**
 * Address Line 1
 * Line 1 of the Address.
 * EnterpriseOne Key field: false
 * EnterpriseOne Alias: add1
 * EnterpriseOne field length:  40 
 */
 private String addressLine1 = null;

This documentation is a result of the preceding Javadoc:

Figure 4-2 Javadoc documentation

Description of Figure 4-2 follows
Description of "Figure 4-2 Javadoc documentation"

You should include Javadoc comments for all public methods. The behavior of the public methods should also be documented.

This code sample shows how to document a method using Javadoc:

  /**
    * Method addAddressBook is used for adding Address Book information 
    * into EnterpriseOne, this includes basic address information plus 
    * phones.  If a phone cannot be added, the Address Book record will 
    * still be added, but warning messages will be returned for the 
    * corresponding phones that caused errors.
    * @param context conditionally provides the connection for the database 
    * operation and logging information
    * @param connection can either be an explicit connection or null. If 
    * null the default connection is used. 
    * @param internalVO represents data that is passed to EnterpriseOne for 
    * processing an AddressBook record.
    * @return an E1Message containing the text of any errors or warnings 
    * that may have occurred
    */
   public static E1MessageList addAddressBook(IContext context,
                                        IConnection connection, 
                                        InternalAddAddressBook internalVO){

This documentation is a result of the preceding Javadoc code:

Figure 4-3 Generated documentation resulting from Javadoc code

Description of Figure 4-3 follows
Description of "Figure 4-3 Generated documentation resulting from Javadoc code"