Documente Academic
Documente Profesional
Documente Cultură
Wouldn't you like to streamline and simplify the development process for your business
applications?
Then you should get to know more about BOPF, our infrastructure for developing business objects
that is available for the SAP Business Suite. With the Business Object Processing Framework, you
will save time during the development cycle because you don't have to implement all the technical
details yourself - details such as authorization control, low-level transaction handling, buffer
management, provisioning of consumer API, or business logic orchestration. Using the model-driven
approach in BOPF, you can instead focus your attention more on the actual business requirements
themselves.
The Business Object Processing Framework is an ABAP OO-based framework that provides a set of
generic services and functionalities to speed up, standardize, and modularize your development.
BOPF manages the entire life cycle of your business objects and covers all aspects of your business
application development. Instead of expending effort for developing an application infrastructure, the
developer can focus on the individual business logic. Using BOPF, you get the whole application
infrastructure and integration of various components for free. This allows you to rapidly build
applications on a stable and customer-proved infrastructure.
BOPF is not really a new framework. In fact, it is well established and broadly used in multiple SAP
ByDesign and SAP Business Suite applications and products for example, Transportation
Management (TM), Environment, Health and Safety (EH&S), SAP Supplier Lifecycle Management,
SAP Management of Change, SAP Quality Issue Management, to name but a few. Apart from its use
in SAP internal development, BOPF is also used in customer development projects.
Due to increased interest, BOPF is also used in SAP customer development projects. It is released
with SAP Business Suite EHP5 SP11, SAP Business Suite EHP6 SP05, and SAP Business
Suite EHP7. Moreover, starting with the SAP NetWeaver 7.50 release, BOPF is available in the SAP
NetWeaver standalone system - as part of the SAP BASIS layer.
With BOPF, you have a framework at your disposal with which you can seamlessly integrate different
components of business applications. You can use them out-of-the-box. The advantages of using
BOPF are thus obvious:
When using BOPF you dont have to care about the development of adapters or integration layers to
consume the following components:
Figure: BOPF acts as a bridging unit between various components
Dynpro BOPF provides a standard interface for consumption by the classic Dynpro UI.
Web Dynpro / Floor The generation and configuration of complex user interfaces has never been as easy
Plan Manager as it is today with the FPM. FPM is implemented as a Web Dynpro component and
(FPM) can be easily integrated with BOPF. BOPF provides configurable and codeless
integration of FPM and enables you to seamlessly consume the services of BOPF
Business Objects in a modification-free environment.
More: Floorplan Manager for Web Dynpro ABAP and Web Dynpro ABAP on SCN
SAPUI5 SAPUI5 is designed for building lightweight UIs for casual use.
Gateway (OData) SAP NetWeaver Gateway is a technology that provides a convenient way to connect
devices, environments, and platforms to SAP software based on market standards.
The BOPF integration of the Gateway is based on REST and OData standards.
Business Object The Business Object Layer (BOL) provides a generic API for accessing business
Layer & GenIL data.
The Generic Interaction Layer (GenIL) enables uniform access to business data
using a stateless request/response format. BOPF provides adapters for BOL and
GenIL integration.
Process Integration
Post Processing With BOPF BOs, you can integrate business processes using the Post Processing
Workflow Workflow.
Infrastructure Component
Archive Development With ADK you archive not only table records but also business instances.
Kit (ADK) Using BOPF you can select which BO instances have to be archived and then
trigger the archiving process for them.
Change Documents BOPF uses the SAP NetWeaver Change Documents solution for recording
changes on business object data.
Application Logging Application logging is used to record particular events during the execution
of an application so that you can reconstruct them later, if necessary.
More: Application Log - Guidelines for Developers on the SAP help portal
Enterprise Search The search service of SAP NetWeaver provides a framework for enterprise-
wide indexing of and searching for structured (business objects) and
unstructured data (documents).
BOPF design time provides you with a convenient way for enabling BOPF
business objects for Enterprise Search. Using an existing BO model, you
have the option of importing the BO data and, in this way, creating a
corresponding Enterprise Search model.
More: Enterprise Search on SCN
Business Rules Framework BRF+ is a rule engine. It provides a comprehensive API and user interface for
plus (BRF+) defining and processing business rules and expressions.
The business objects are the basic units of the BOPF-based programming model. Business
applications or processes operate on certain business objects. A business object is represented as a
hierarchical tree of nodes. A single node includes a set of semantically related business object data
and the corresponding business logic. On the technical level, each node is implemented with a
regular Dictionary table, where each node instance corresponds to a single table entry (table rows).
Nodes, attributes, and alternative keys set up the data part of a business object. Again from a
technical viewpoint, attributes form the columns of the table. A node serves as an anchor point for
connecting the business logic of the business object.
For each node, several types of entities can be defined to form the specific business logic partof a
business object.
Determination to provide functions that are automatically executed as soon as certain trigger
conditions are fulfilled. A determination is triggered internally due to changes
made to the instance of a business object.
Validation to either validate whether a specific action can be executed on a specific node
instance (action validations) or whether a set of node instances is consistent
(consistency validations).
Query to search for business object instances that meet certain search criteria.
Authorization to provide an authorization concept for BO nodes (for their data and for the
Check operations on their data).
Association to connect business object instances that are located on different nodes.
Figure: Business object metadata model
SAP Internal Design These are internal SAP tools and utilities that provide developers with a
Time Tools (BOBF) comprehensive feature set for building BO-centered applications for SAP Business
Suite.
Customers can enhance SAP business objects and also create their own simple
business objects in the customer namespace. The creation of business objects,
nodes, actions, determinations, queries, or validations is extensively supported by
wizard-driven tools and utilities.
BO Builder is available through SAP GUI and is released for customers with SAP
Business Suite EHP5 upwards
BO Builder for eXperts is available through SAP GUI and is released for customers
with SAP Business Suite EHP6 upward.
(Eclipse-Based) With SAP Business Suite EHP7 a new Eclipse-based client application
ABAP Development
Tools for BOPF will be provided as part of an ABAP Development Tools installation. This set of
tools runs on top of the Eclipse-based ABAP IDE and is aimed at supporting all
kinds of BO-centric use cases: it allows both the creation of new BOPF-
applications and the enhancement of existing ones.
BO Builder Test Starting from development environment, you can always test the current range of
Environment (BOBT) functions of each business object (or enhancement).
BO-Specific Debugging on the business object entities level enables you to speed up the
Debugging troubleshooting process:
You can set breakpoints for entities such as actions, determinations, and
validations. Furthermore, you have the option of setting watchpoints for certain
activities on the nodes (for example, to update a particular node attribute).
Integrated BO Test BOPF supports test automation and test-driven development. From the outset,
Infrastructure you can apply ABAP unit tests to test isolated units of business objects, such as
actions, validations, or determinations. In addition, with the scenario testing
function, you can extend the tests to processes that consist of several steps.
NOTE
As a SAP customer, you can use the BO Builder (BOB), the BO Builder for eXperts (BOBX), or the
Eclipse-based client for the development of your own business objects. Here, you will benefit from
that interoperability is ensured between these development tools. So you can, for example,
create new business objects in the BOB tool and then edit them in another tool on.
Getting Started with BOPF guides you through all the steps required to create from scratch your first
business object and to implement a basic operation using the BOPF API.
NAVIGATING THE BOPF: PART 2 BUSINESS OBJECT OVERVIEW
According to SAPs BOPF Enhancement Workbench documentation, business objects within the
BOPF are a representation of a type of uniquely identifiable business entity described by a structural
model and an internal process model. This is to say that BOPF business objects:
Have a well-defined process model which governs the business object lifecycle, behaviours, etc.
Execute within a container-like environment which handles low-level tasks such as caching,
transaction management, and so on.
In this regard, BOs in the BOPF are not unlike objects developed in other component architectures
(e.g. EJBs in Java, Microsoft COM+, etc.).
From a modelling perspective, BOs are made up of several different types of entities:
NODES
Nodes are used to model a BOs data.
Nodes are arranged hierarchically to model the various dimensions of the BO data. This hierarchy is
organized underneath a single root node (much like XML). From there, the hierarchy can be nested
arbitrarily deep depending upon business requirements.
There are several different node types supported by the BOPF. However, most of the time youll find
yourself working with persistent nodes (e.g. nodes which are backed by the database). It is also
possible to define transient nodes whose contents are loaded on demand at runtime. These types of
nodes can come in handy whenever we want to bridge some alternative persistence model (e.g. data
obtained via service calls).
Each node consists of one or more attributes which describe the type of data stored within the node:
Attributes come in two distinct varieties: persistent attributes and transient attributes. Persistent
attributes represent those attributes that will be persisted whenever the BO is saved. Transient
attributes are volatile attributes which are loaded on demand.
A nodes attributes are defined in terms of structure definitions from the ABAP Dictionary.
At runtime, a BO node is like a container which may have zero, one, or many rows. If youre familiar
with the concept of controller contexts with the Web Dynpro programming model, then this concept
should feel familiar to you. If not, dont worry; well demonstrate how this works whenever we look at
the BOPF API.
ACTIONS
Actions define the services (or behaviour) of a BO.
The functionality provided by an action is (usually) defined in terms of an ABAP Objects class that
implements the /BOBF/IF_FRW_ACTION interface.
To some extent, it is appropriate to think of actions as being similar to the methods of an ABAP
Objects class.
ASSOCIATIONS
Though BOs are designed to be self-contained, autonomous entities, they do not have to exist in
isolation. With associations, we can define a direct and unidirectional relationship from one BO to
another.
DETERMINATIONS
According to the aforementioned BOPF enhancement guide, a determination is an element assigned
to a business object node that describes internal changing business logic on the business object.
In some respects, determinations are analogous to database triggers. In other words, they are
functions that are triggered whenever certain triggering conditions are fulfilled. These conditions are
described in terms of a series of patterns:
This pattern allows us to react to changes made to a given BO node. For example, we might use this
event to go clean up some related data.
This pattern allows us to hang some custom logic on a given BO node before it is saved. This could
be as simple as using a number range object to assign an ID value to a node attribute or as complex
as triggering an interface.
This pattern is often used in conjunction with UI development. Here, we might want to load labels and
descriptive texts into a series of transient attributes to be displayed on the screen.
Note: This determination can be bypassed via the API if the lookup process introduces unnecessary
overhead.
Determination patterns are described in detail within the aforementioned BOPF enhancement guide.
The logic within a determination is defined via an ABAP Objects class that implements
the /BOBF/IF_FRW_DETERMINATION interface.
VALIDATIONS
According to the BOPF enhancement guide, validations are an element of a business object node
that describes some internal checking business logic on the business object.
Action Validations
Action validations are used to determine whether or not a particular action can be executed against a
BO node.
Consistency Validations
As the name suggests, consistency validations are used to ensure that a BO node is consistent. Such
validations are called at pre-defined points within the BOPF BO transaction cycle to ensure that BO
nodes are persisted in a consistent state.
The validation logic is encapsulated within an ABAP Objects class that implements
the /BOBF/IF_FRW_VALIDATION interface.
QUERIES
Queries are BO node entities which allow us to search for BOs using various types of search criteria.
Queries make it possible for consumers to access BOs without knowing the BO key up front.
Queries also integrate quite nicely with search frameworks and the like.
Node attribute queries are modeled queries whose logic is defined within the BOPF runtime. These
simple queries can be used whenever you simply need to search for BO nodes by their attributes (e.g.
ID = 12345).
Custom Queries
Custom queries allow you define your own query logic by plugging in an ABAP Objects class that
implements the /BOBF/IF_FRW_QUERY interface.
The figure below illustrates how all of these entities fit together within a BO node definition. Here, Ive
pulled up a BO called /BOBF/DEMO_SALES_ORDER in Transaction /BOBF/CONF_UI. Here, the BO
metadata is organized into several different panels:
On the top left-hand side of the screen, you can see the BOs node structure. Here, you can see that
the node structure is organized underneath a top-level ROOT node which models sales order header
data. Underneath this node are several child nodes which model sales order items, customer
assignment, and texts. The ITEM node in turn encompasses its own child nodes to model item-level
data.
On the bottom left-hand side of the screen, we can browse through the node collection of a BO and
view the entity assignments of a given node. As you can see in the figure, each node contains folders
which organize assigned actions, validations, and so on.
In the middle of the screen, we can view additional details about a selected node by double-clicking
on a node within the Node Structure panel on the left-hand side of the screen. Here, we can look at a
nodes data model, implementation classes, and so on.
Well have an opportunity to get a little more hands on with these entities in upcoming blog entries.
For now, our focus is on grasping how pieces fit together and where to go to find the information we
need to get started with a BO.
NEXT STEPS
At this point, you should have a decent feel for how BOs are modeled at design time. In my next blog,
well shift gears and begin manipulating BOs using the provided BOPF APIs. This will help put all of
these entities into perspective.
NAVIGATING THE BOPF: PART 3 WORKING WITH THE BOPF API
the construction of a simple ABAP report program used to perform CRUD operations on a sample
BOPF BO shipped by default by SAP: /BOBF/DEMO_CUSTOMER.
Before we begin coding with the BOPF API, lets first take a look at its basic structure. The UML class
diagram below highlights some of the main classes that make up the BOPF API. At the end of the
day, there are three main objects that well be working with to perform most of the operations within
the BOPF:
/BOBF/IF_TRA_TRANSACTION_MGR
This object reference provides a transaction manager which can be used to manage transactional
changes. Such transactions could contain a single step (e.g. update node X) or be strung out across
multiple steps (add a node, call an action, and so on).
/BOBF/IF_TRA_SERVICE_MANAGER
The service manager object reference provides us with the methods we need to lookup BO nodes,
update BO nodes, trigger validations, perform actions, and so on.
/BOBF/IF_FRW_CONFIGURATION
This object reference provides us with metadata for a particular BO. Well explore the utility of having
access to this metadata coming up shortly.
In the upcoming sections, Ill show you how these various API classes collaborate in typical BOPF
use cases. Along the way, well encounter other useful classes that can be used to perform specific
tasks. You can find a complete class listing within package /BOBF/MAIN.
Note: As youll soon see, the BOPF API is extremely generic in nature. While this provides
tremendous flexibility, it also adds a certain amount of tedium to common tasks. Thus, in many
applications, you may find that SAP has elected to wrap the API up in another API that is more
convenient to work with. For example, in the SAP EHSM solution, SAP defines an Easy Node
Access API which simplfies the way that developers traverse BO nodes, perform updates, and so on.
CASE STUDY: BUILDING A SIMPLE REPORT PRO GRAM TO MANIPULATE CUSTOMER OBJECTS
To demonstrate the BOPF API, well build a custom report program which performs basic CRUD
operations on a sample BO provided by SAP: /BOBF/DEMO_CUSTOMER. The figure below shows the
makeup of this BO in Transaction /BOBF/CONF_UI.
Our sample program provides a basic UI as shown below. Here, users have the option of creating,
changing, and displaying a particular customer using its ID number. Sort of a simplified Transaction
XK01-XK03 if you will.
GETTING STARTED
To drive the application functionality, well create a local test driver class called LCL_DEMO. As you can
see in the code excerpt below, this test driver class loads the core BOPF API objects at setup
whenever the CONSTRUCTOR method is invoked. Here, the factory classes illustrated in the UML class
diagram shown in the previous section are used to load the various object references.
METHODS:
constructor RAISING /bobf/cx_frw.
ENDCLASS.
For the most part, this should seem fairly straightforward. However, you might be wondering where I
came up with the IV_BO_KEY parameter in
the GET_SERVICE_MANAGER() and GET_CONFIGURATION() factory method calls. This value is provided
to us via the BOs constants interface (/BOBF/IF_DEMO_CUSTOMER_C in this case) which can be found
within the BO configuration in Transaction /BOBF/CONF_UI (see below). This auto-generated
constants interface provides us with a convenient mechanism for addressing a BOs key, its defined
nodes, associations, queries, and so on. Well end up using this interface quite a bit during the course
of our development.
CREATING NEW CUSTOME RS
Once we have the basic framework in place, we are ready to commence with the development of the
various CRUD operations that our application will support. To get things started, well take a look at
the creation of a new customer instance. For the most part, this involves little more than a call to
the MODIFY() method of the /BOBF/IF_TRA_SERVICE_MANAGER object reference. Of course, as you can
see in the code excerpt below, there is a fair amount of setup that we must do before we can call this
method.
FIELD-SYMBOLS:
<ls_mod> LIKE LINE OF lt_mod.
/bobf/if_demo_customer_c=>sc_association-root-root_text.
<ls_mod>-source_key = lr_s_root->key.
<ls_mod>-key = lr_s_txt->key.
<ls_mod>-data = lr_s_txt.
/bobf/if_demo_customer_c=>sc_association-root-root_long_text.
<ls_mod>-source_key = lr_s_root->key.
<ls_mod>-key = lr_s_txt_hdr->key.
<ls_mod>-data = lr_s_txt_hdr.
"Create the CONTENT node:
CREATE DATA lr_s_txt_cont.
lr_s_txt_cont->key = /bobf/cl_frw_factory=>get_new_key( ).
lr_s_txt_cont->language = sy-langu.
lr_s_txt_cont->text_type = 'MEMO'.
lr_s_txt_cont->text_content = 'Demo customer created via BOPF API.'.
lo_driver->mo_bo_conf->query_node(
iv_proxy_node_name = 'ROOT_LONG_TXT.CONTENT' ).
<ls_mod>-change_mode = /bobf/if_frw_c=>sc_modify_create.
<ls_mod>-source_node = /bobf/if_demo_customer_c=>sc_node-root_long_text.
<ls_mod>-source_key = lr_s_txt_hdr->key.
<ls_mod>-key = lr_s_txt_cont->key.
<ls_mod>-data = lr_s_txt_cont.
<ls_mod>-association =
lo_driver->mo_bo_conf->query_assoc(
iv_node_key = /bobf/if_demo_customer_c=>sc_node-root_long_text
iv_assoc_name = 'CONTENT' ).
IF lv_rejected EQ abap_true.
lo_driver->display_messages( lo_message ).
RETURN.
ENDIF.
As you can see in the code excerpt above, the majority of the code is devoted to building a table
which is passed in the IT_MODIFICATION parameter of the MODIFY() method. Here, a separate record
is created for each node row that is being modified (or inserted in this case). This record contains
information such as the node object key (NODE), the edit mode (CHANGE_MODE), the row key (KEY) which
is an auto-generated GUID, association/parent key information, and of course, the actual data (DATA).
If youve ever worked with ALE IDocs, then this will probably feel vaguely familiar.
Looking more closely at the population of the node row data, you can see that were working with data
references which are created dynamically using the CREATE DATA statement. This indirection is
necessary since the BOPF API is generic in nature. You can find the structure definitions for each
node by double-clicking on the node in Transaction /BOBF/CONF_UI and looking at the COMBINED
STRUCTURE field (see below).
Once the modification table is filled out, we can call the MODIFY() method to insert the record(s).
Assuming all is successful, we can then commit the transaction by calling the SAVE() method on
the /BOBF/IF_TRA_TRANSACTION_MANAGER instance. Should any errors occur, we can display the error
messages using methods of the /BOBF/IF_FRW_MESSAGE object reference which is returned from both
methods. This is evidenced by the simple utility method DISPLAY_MESSAGES() shown below. Thats
pretty much all there is to it.
"Sanity check:
CHECK io_message IS BOUND.
"Output each of the messages in the collection:
io_message->get_messages( IMPORTING et_message = lt_messages ).
LOOP AT lt_messages ASSIGNING <ls_message>.
lv_msg_text = <ls_message>-message->get_text( ).
WRITE: / lv_msg_text.
ENDLOOP.
ENDMETHOD. " METHOD display_messages
ENDCLASS.
If you look closely at the customer creation code illustrated in the previous section, you can see that
each node row is keyed by an auto-generated GUID of type /BOBF/CONF_KEY (see below). Since most
users dont happen to have 32-character hex strings memorized, we typically have to resort to queries
if we want to find particular BO instances. For example, in our customer demo program, we want to
provide a way for users to lookup customers using their customer ID value. Of course, we could have
just as easily defined an alternative query selection to pull the customer records.
As we learned in the previous blog post, most BOs come with one or more queries which allow us to
search for BOs according to various node criteria. In the case of the /BOBF/DEMO_CUSTOMER business
object, we want to use the SELECT_BY_ATTRIBUTES query attached to the ROOT node (see below). This
allows us to lookup customers by their ID value.
The code excerpt below shows how we defined our query in a method
called GET_CUSTOMER_FOR_ID(). As you can see, the query is executed by calling the aptly
named QUERY() method of the /BOBF/IF_TRA_SERVICE_MANAGER instance. The query parameters are
provided in the form of an internal table of type /BOBF/T_FRW_QUERY_SELPARAM. This table type has a
similar look and feel to a range table or SELECT-OPTION. The results of the query are returned in a
table of type /BOBF/T_FRW_KEY. This table contains the keys of the node rows that matched the query
parameters. In our sample case, there should be only one match, so we simply return the first key in
the list.
/bobf/if_demo_customer_c=>sc_query_attribute-root-select_by_attributes-
customer_id.
<ls_parameter>-sign = 'I'.
<ls_parameter>-option = 'EQ'.
<ls_parameter>-low = iv_customer_id.
/bobf/if_demo_customer_c=>sc_query-root-select_by_attributes
it_selection_parameters = lt_parameters
IMPORTING
et_key = lt_customer_keys.
With the query logic now in place, we now know which customer record to lookup. The question is,
how do we retrieve it? For this task, we must use the
As you can see in the code excerpt below, we begin our search by accessing the customer ROOT node
using the RETRIEVE() method. Here, the heavy lifting is performed by
the GET_NODE_ROW() and GET_NODE_TABLE() helper methods. Looking at the implementation of
the GET_NODE_TABLE() method, you can see how were using
the /BOBF/IF_FRW_CONFIGURATION object reference to lookup the nodes metadata. This metadata
provides us with the information we need to construct an internal table to house the results returned
from the RETRIEVE() method. The GET_NODE_ROW() method then dynamically retrieves the record
located at the index defined by the IV_INDEX parameter.
Within the DISPLAY_CUSTOMER() method, we get our hands on the results by performing a cast on the
returned structure reference. From here, we can access the row attributes as per usual.
After the root node has been retrieved, we can traverse to the child nodes of
the /BOBF/DEMO_CUSTOMERobject using the RETRIEVE_BY_ASSOCIATION() method. Here, the process is
basically the same. The primary difference is in the way we lookup the association metadata which is
used to build the call to RETRIEVE_BY_ASSOCIATION() . Once again, we perform a cast on the returned
structure reference and display the sub-node attributes from there.
PRIVATE SECTION.
METHODS:
get_node_table IMPORTING iv_key TYPE /bobf/conf_key
iv_node_key TYPE /bobf/obm_node_key
iv_edit_mode TYPE /bobf/conf_edit_mode
DEFAULT /bobf/if_conf_c=>sc_edit_read_only
RETURNING VALUE(rr_data) TYPE REF TO data
RAISING /bobf/cx_frw,
DEFAULT /bobf/if_conf_c=>sc_edit_read_only
iv_index TYPE i DEFAULT 1
RETURNING VALUE(rr_data) TYPE REF TO data
RAISING /bobf/cx_frw,
DEFAULT /bobf/if_conf_c=>sc_edit_read_only
iv_index TYPE i DEFAULT 1
RETURNING VALUE(rr_data) TYPE REF TO data
RAISING /bobf/cx_frw.
ENDCLASS.
iv_key = lv_customer_key
iv_node_key = /bobf/if_demo_customer_c=>sc_node-root
iv_index = 1 ).
WRITE: / 'Display Customer', lr_s_root->customer_id.
ULINE.
WRITE: / 'Sales Organization:', lr_s_root->sales_org.
WRITE: / 'Address:', lr_s_root->address.
SKIP.
iv_key = lv_customer_key
iv_node_key = /bobf/if_demo_customer_c=>sc_node-root
iv_assoc_key = /bobf/if_demo_customer_c=>sc_association-root-root_text
iv_index = 1 ).
WRITE: / 'Short Text:', lr_s_text->text.
CATCH /bobf/cx_frw INTO lx_bopf_ex.
lv_err_msg = lx_bopf_ex->get_text( ).
WRITE: / lv_err_msg.
ENDTRY.
ENDMETHOD. " METHOD display_customer
METHOD get_node_table.
"Method-Local Data Declarations:
DATA lt_key TYPE /bobf/t_frw_key.
DATA ls_node_conf TYPE /bobf/s_confro_node.
DATA lo_change TYPE REF TO /bobf/if_tra_change.
METHOD get_node_row.
"Method-Local Data Declarations:
DATA lr_t_data TYPE REF TO data.
METHOD get_node_table_by_assoc.
"Method-Local Data Declarations:
DATA lt_key TYPE /bobf/t_frw_key.
DATA ls_node_conf TYPE /bobf/s_confro_node.
DATA ls_association TYPE /bobf/s_confro_assoc.
DATA lo_change TYPE REF TO /bobf/if_tra_change.
DATA lo_message TYPE REF TO /bobf/if_frw_message.
ls_node_conf = ls_association-target_node->*.
METHOD get_node_row_by_assoc.
"Method-Local Data Declarations:
DATA lr_t_data TYPE REF TO data.
Note: In this simple example, we didnt bother to drill down to display the contents of
the ROOT_LONG_TEXTnode. However, if we had wanted to do so, we would have needed to create a
separate service manager instance for the /BOBF/DEMO_TEXT_COLLECTION business object since the
data within that node is defined by that delegated BO as opposed to the /BOBF/DEMO_CUSTOMER BO.
Otherwise, the process is the same.
The process of modifying a customer record essentially combines logic from the display and create
functions. The basic process is as follows:
Next, we use the RETRIEVE*() methods to retrieve the node rows we wish to modify. Using the
returned structure references, we can modify the target attributes using simple assignment
statements.
Finally, we collect the node row changes into the modification table that is fed into MODIFY()method
provided by the /BOBF/IF_TRA_SERVICE_MANAGER instance.
The code excerpt below shows how the changes are carried out. Here, were simply updating the
address string on the customer. Of course, we could have performed wholesale changes if we had
wanted to.
FIELD-SYMBOLS:
<ls_mod> LIKE LINE OF lt_mod.
lr_s_root ?=
lo_driver->get_node_row( iv_key = lv_customer_key
iv_node_key = /bobf/if_demo_customer_c=>sc_node-root
iv_edit_mode = /bobf/if_conf_c=>sc_edit_exclusive
iv_index = 1 ).
IF lv_rejected EQ abap_true.
lo_driver->display_messages( lo_message ).
RETURN.
ENDIF.
NEXT STEPS
I often find that the best way to learn a technology framework is to see how it plays out via code. At
this level, we can clearly visualize the relationships between the various entities and see how they
perform at runtime. Hopefully after reading this post, you should have a better understanding of how
all the BOPF pieces fit together. In my next blog post, well expand upon what weve learned and
consider some more advanced features of the BOPF API.
NAVIGATING THE BOPF: PART 4 ADVANCED BOPF API FEATURES
In keeping with the object-oriented paradigm, business objects (BOs) are designed to combine
business data and business functions together into one tidy capsule (hence the
term ENCAPSULATION). One of the primary benefits of combining these entities is to ensure that
updates to business data are reliably filtered through a set of business rules. To put this concept into
perspective, imagine a BO which defines a header-level status field (e.g. 01 = Initial, 02 = In Process,
03 = Closed). Now, from a pure data perspective, theres nothing stopping us from updating the status
field using the MODIFY() method of the /BOBF/IF_TRA_SERVICE_MANAGER interface (or heck, even via
an SQL UPDATE statement). However, from a business perspective, there are probably some rules
which define when, where, and how we should change the status field. For example, it might be that
the BO cannot be closed until any open line items are closed out, etc.
Whatever the business rules might be, the point is that we want to ensure that a BO is consistent
throughout each checkpoint in its object lifecycle. As we learned in part 2 of this blog series, the
BOPF allows us to define these consistency checks in the form of VALIDATIONS. For example, in
the screenshot below, you can see how SAP has created a validation called CHECK_ROOT for
the ROOT node of the /BOBF/DEMO_SALES_ORDER demo BO. This validation is used to perform a
consistency check on the sales order header-level fields to make sure that they are valid before an
update is committed to the database.
One of the nice things about validations like CHECK_ROOT is that they are automatically called by the
BOPF framework at specific points within the transaction lifecycle. However, sometimes we might
want to trigger such validations interactively. For example, when building a UI on top of a BO, we
might want to provide a check function which validates user input before they save their changes.
This is demonstrated in the /BOBF/DEMO_SALES_ORDER Web Dynpro ABAP application shown below.
From a code perspective, the heavy lifting for the check operation is driven by
the CHECK_CONSISTENCY()method of the /BOBF/IF_TRA_SERVICE_MANAGER interface as shown in the
code excerpt below. Here, we simply provide the service manager with the target node key and the
BO instance key and the framework will take care of calling the various validations on our behalf. We
can then check the results of the validation by looking at the /BOBF/IF_FRW_MESSAGE instance which
was introduced in the previous blog.
EXPORTING
iv_node_key = /bobf/if_demo_sales_order_c=>sc_node-root
it_key = lt_key
iv_check_scope = '1'
IMPORTING
eo_message = lo_message.
...
...
ENDTRY.
Ill show you how to implement validations within a BO in an upcoming blog entry.
TRIGGERING ACTIONS
The behaviors of a business object within the BOPF are defined as ACTIONS. From a conceptual
point-of-view, actions are analogous to methods/functions in the object-oriented paradigm. The
following code excerpt demonstrates how actions are called using the BOPF API. Here, were calling
the DELIVER action defined in the ROOT node of the /BOBF/DEMO_SALES_ORDER demo BO. As you can
see, the code reads like a dynamic function/method call since we have generically pass the name of
the action along with its parameters to the DO_ACTION() method of
the /BOBF/IF_TRA_SERVICE_MANAGER interface. Other than that, its pretty much business as usual.
ls_parameters-item_no = '001'.
EXPORTING
iv_act_key = /bobf/if_demo_sales_order_c=>sc_action-root-deliver
it_key = lt_key
is_parameters = lr_s_parameters
IMPORTING
eo_change = lo_change
eo_message = lo_message
et_failed_key = lt_failed_key
et_failed_action_key = lt_failed_act_key.
...
...
ENDTRY.
We can verify if a BOPF action call was successful by querying the EO_MESSAGE object reference
and/or the ET_FAILED_KEY table parameters returned by the DO_ACTION() method. Refer back to my
previous blog for an example of the former technique. As always, remember to commit the transaction
using the
In part two of this blog series, I mentioned that there are technically two different types of validations:
the consistency check validations discussed earlier in this blog and action validations which are used
to determine whether or not an action can be executed at runtime. Since the BOPF framework
invokes action validations automatically whenever actions are invoked, it is rare that you will have a
need to invoke them directly. However, the /BOBF/IF_TRA_SERVICE_MANAGER interface does provide
the DO_ACTION() method if you wish to do so (see the code excerpt below).
TRY.
ls_parameters-item_no = '001'.
EXPORTING
iv_act_key = /bobf/if_demo_sales_order_c=>sc_action-root-deliver
it_key = lt_key
is_parameters = lr_s_parameters
IMPORTING
eo_message = lo_message
et_failed_key = lt_failed_key
et_failed_action_key = lt_failed_act_key.
...
...
ENDTRY.
TRANSACTION MANAGEMENT
Another element of the BOPF API that we have glossed over up to now is the transaction manager
interface /BOBF/IF_TRA_TRANSACTION_MGR . This interface provides us with a simplified access point
into a highly sophisticated transaction management framework. While the details of this framework
are beyond the scope of this blog series, suffice it to say that the BOPF transaction manager does
more here than simply provide basic object-relational persistence. It also handles caching,
transactional locking, and more. You can see how some of these features are implemented by looking
at the TRANSACTIONAL BEHAVIOR settings of a business object definition in Transaction
/BOBF/CONF_UI (see below).
So far, we have seen a bit of the /BOBF/IF_TRA_TRANSACTION_MGR on display whenever we looked at
how to insert/update records. Here, as you may recall, we used SAVE() method of
the /BOBF/IF_TRA_TRANSACTION_MGR interface to save these records. In many respects,
the SAVE() method is analogous to the COMMIT WORK statement in ABAP in that it commits the
transactional changes to the database. Here, as is the case with the COMMIT WORK statement, we
could be committing multiple updates as one logical unit of work (LUW) e.g. an insert followed by a
series of updates.
During the course of a transaction, the BOPF transaction manager tracks the changes that are made
to individual business objects internally so that it can determine what needs to be committed and/or
rolled back. If desired, we can get a peek of the queued up changes by calling
the GET_TRANSACTIONAL_CHANGES() method of the /BOBF/IF_TRA_TRANSACTION_MGR interface. This
method will return an object reference of type /BOBF/IF_TRA_CHANGE that can be used to query the
change list, modify it in certain cases, and so on.
NEXT STEPS
At this point, we have hit on most of the high points when it comes to interacting with the BOPF API
from a client perspective. In my next blog, well shift gears and begin looking at ways of enhancing
BOs using the BOPF toolset.
WHAT TO ENHANCE?
Before we dive into the exploration of specific enhancement techniques, lets first take a look at the
kinds of entities were allowed to enhance in a business object. Aside from implicit enhancements
applied to implementation classes using the Enhancement Framework, the types of entities that we
can enhance within a business object are as follows:
CUSTOM ATTRIBUTES
For a given node, we might want to define a handful of additional custom attributes.These attributes
could be persistent (i.e., they get appended to the target database table which contains the node
data) or transient in nature.
NEW SUB-NODES
In some cases, we may need to do more than simply define a few new attributes on an existing node.
Using the relational data model as our guide, we may determine that a new sub-node is needed to
properly model some new dimension of data (e.g. 1-to-many relations, etc.). Depending on the
requirement, the sub-node(s) might be persistent or transient in nature.
DETERMINATIONS
If we add new custom attributes to a given node, it stands to reason that we might also want to create
a custom determination to manage these attributes.
Or, we might have a standalone requirement which calls for some sort of trigger to be fired
whenever a specific event occurs (e.g. fire an event to spawn a workflow, etc.).
CONSISTENCY VALIDATIONS
If we are enhancing the data model of a business object, we might want to define a consistency
validation to ensure that the new data points remain consistent.
A custom validation might also be used to graft in a new set of business rules or a custom security
model.
ACTIONS
If we have certain operations which need to be performed on a business object, we would prefer to
encapsulate those operations as an action on the business object as opposed to some standalone
function module or class.
QUERIES
In some cases, the set of defined queries for a business object might not be sufficient for our needs.
In these situations, we might want to define custom queries to encapsulate the selection logic so that
we can use the generic query services of the BOPF API as opposed to some custom selection
method.
You can find a detailed treatment of supported enhancement options in the BOPF ENHANCEMENT
WORKBENCH HELP documentation which is provided as a separate download in SAP Note
#1457235. This document provides a wealth of information concerning the use of the BOPF
Enhancement Workbench, enhancement strategies, and even the BOPF framework in general. Given
the amount of detail provided there, I wont attempt to re-invent the wheel in this blog post. Instead, Ill
simply hit on the high points and leave the nitty-gritty details to the help documentation.
WORKING WITH THE ENHANCEMENT WORKBENCH
When enhancing a business object, youll be spending quite a bit of time with the BOPF
Enhancement Workbench which can be accessed using Transaction BOPF_EWB. Here,
enhancement projects are organized into ENHANCEMENT OBJECTS. From a conceptual point-of-
view, enhancement objects bear a lot of similarities to sub-classes in the object-oriented programming
(OOP) paradigm. This is to say that enhancement objects INHERIT all of the entities of their parent
BO. With this foundation in place, we can begin defining custom entities in much the same way we
might add new attributes/methods to a subclass in the ABAP Class Builder tool. However, as is the
case with classes in the OOP world, we cannot extend BOs which are marked as final or that do not
have the Business Object can be enhanced flag set (see below).
All of the BOs which are eligible for enhancement will show up in the Enhancement Browser
perspective of the BOPF Enhancement Workbench shown below. To create an enhancement, simply
right-click on the BO that you wish to enhance and select the Create Enhancement menu option (see
below). From here, the BOPF Enhancement Workbench will guide you through a wizard process
which allows you to select the name of the enhancement object, the constants interface for the
enhancement object, and so on.
Once the enhancement is created, you will be able to edit your enhancement object in the workbench
perspective of the BOPF Enhancement Workbench shown below. As you can see, it has a similar
look-and-feel to that of the normal BO browser tool (Transaction /BOBF/CONF_UI). From here, we
can begin adding custom entities by right-clicking on the target node and selecting from the available
menu options. Well see how this works in the upcoming sections.
One final item I would draw your attention to with enhancement objects is the assigned constants
interface (highlighted above). This constants interface can be used to access the enhancement object
entities in the same way that the super BOs constants interface is used for BOPF API calls, etc.
Perhaps the most common type of enhancement to BOs in the BOPF is the addition of new fields.
Here, we have the option of adding new fields to existing nodes or creating sub-nodes to model more
complex relationships. In the former case, we sometimes dont even need to create an enhancement
object; just a simple append structure will suffice (see below).
For more complex data requirements, we typically need to define sub-nodes. This can be achieved by
right-clicking on the parent node and selecting the Create Subnode menu option. This kicks off a
wizard process in which you can select the sub-nodes name, its persistent and/or transient
structures, and the rest of the auto-generated dictionary types which go along with a node definition
(e.g. combined structure/table type, database table, etc.). Most of this is pretty standard stuff, but I
would draw your attention to the step which creates the persistent and/or transient structures. Note
that these structures must exist in the database BEFORE you move on from the Attributes step in the
wizard process. And, in the case of the persistent structure, you must include
the /BOBF/S_ADMIN structure as the first component.
After the custom sub-node is created, you can fill out its attributes by adding components to the
persistent/transient structures defined by the sub-node. If the sub-node is a persistent node, then we
can create, modify, and retrieve node instances using the BOPF API as per usual. However, in the
case of transient nodes, we need determinations to pre-fetch the data for us. Well see how to define
such determinations next.
DEFINING DETERMINATIONS
According to the help documentation, determinations encapsulate internal changing business logic on
a business object. Unlike the logic encapsulated in actions which can be triggered at any time, the
business logic contained within determinations is triggered as specific times within the BO life cycle
(e.g. right before a node is saved, etc.). So, in a way, it is appropriate to think of determinations as
being a little bit like user exits/BAdIs/enhancement spots in that they provide a place to hang custom
logic at particular points within the process flow.
Once we determine (no pun intended) that we want to create a determination for a given node, we
can do so by simply right-clicking on that node and selecting the Create Determination menu option.
This will spawn a wizard which guides us through the process. Here, there are two main properties
that we must account for:
IMPLEMENTING CLASS:
We must create or assign an ABAP Objects class that implements
the /BOBF/IF_FRW_DETERMINATION interface.
DETERMINATION PATTERN:
This property defines the event which triggers the determination. As you can see below, the set of
available patterns will vary depending on the type of node youre enhancing, its location in the node
hierarchy, and so on.
Once a pattern is selected, you may be presented with additional options for refining when an event is
triggered. For example, if we select the pattern Derive dependent data immediately after
modification, we will have the opportunity to specify if the dependent data should be created/modified
after any modification, only when the node is created the first time, etc.
Because determinations can be used for a lot of different things, they can be implemented in a lot of
different ways. Here, it is very important that you pay close attention to selecting the right pattern for
the right job. The aforementioned help documentation provides a good set of guidelines to assist
here. Other valuable resources include the interface documentation for
the /BOBF/IF_FRW_DETERMINATIONinterface in the Class Builder tool and SAP standard-delivered
determinations implementations available in the system youre working on.
The process of defining a custom consistency validation is quite similar to the one used to define
determinations. Walking through the wizard process, there are three main properties that we must
account for:
IMPLEMENTING CLASS:
Here, we must create/assign an ABAP Objects class which implements
the /BOBF/IF_FRW_VALIDATION interface.
REQUEST NODES:
This property allows us to specify which node operations should force a validation to occur (e.g.
during creates, updates, etc.)
IMPACT:
With this property, we can specify the behavior of the BOPF framework in cases where the validation
fails. For example, should we simply return an error message, prevent the requested operation from
proceeding, or both?
We can create a brand new action definition for a given node (standard or custom).
The first case is pretty straightforward. Basically, we simply follow along with the wizard process up to
the point that we reach the Settings step shown below. Here, we must define three main properties for
the action:
IMPLEMENTING CLASS:
This property is used to specify the ABAP Objects class which encapsulates the action logic. The
class must implement the /BOBF/IF_FRW_ACTION interface.
ACTION CARDINALITY:
The action cardinality property defines the scope of the action. This is somewhat analogous to the
way we have the option of defining class methods or instance methods within a regular ABAP Objects
class. In this case however, we also have the third option of defining a sort of mass-processing
action which works on multiple node instances at once.
PARAMETER STRUCTURE:
If we wish to pass parameters to the action, we can plug in an ABAP Dictionary structure here to
encapsulate the parameters.
Once the action is created, we simply need to plug in the relevant logic in the defined implementation
class. You can find implementation details for this in the interface documentation and/or sample
action classes in the system.
In order to create a pre/post action enhancement, the target action definition in the super BO must
have its Action Can Be Enhanced flag set (see below). Assuming that the flag is set, then we can
proceed through the corresponding wizard process in much the same way we would if we were
creating a custom action from scratch. Indeed, as is the case with regular actions, the implementation
class(es) for pre/post action enhancements must implement the /BOBF/IF_FRW_ACTION interface.
Before you go to implement a pre/post action enhancement, I would definitely recommend that you
read through the help documentation so that you understand what you can and cannot do within an
action enhancement. Most of the rules are intuitive, but you can definitely get into trouble if you abuse
these enhancements by using them for things they werent designed for.
Compared to the various enhancement options weve seen thus far, custom queries are perhaps the
easiest entities to create within an enhancement object. Indeed, if all we want is a simple node
attribute query, we can zip through the wizard and have a working model up and running in a matter
of minutes. If we want something a little more custom/sophisticated, our job is only marginally more
difficult (at least from a configuration perspective) in that we must assign an implementing class and
an optional data type which serves as the parameter structure passed into the query from the client
side (see below).
From an implementation perspective, all of the query logic for a custom query gets encapsulated in
the implementation class (which must implement the /BOBF/IF_FRW_QUERY interface). For the most
part, youll find that the framework doesnt really get in the way with regards to how we go about
implementing the query. Basically, it passes in the query parameters up front and its up to us to figure
out how to find all of the node instances which match the given parameters. Here, we must pay
careful attention to the SQL statements that we use since the query may be used extensively by a
number of different clients.
NEXT STEPS
Hopefully by now you have a general feel for how BOs are enhanced and the basic steps required to
achieve these enhancements. As is the case with most programming-related subjects, the best way to
really drive these concepts home is to look at live examples and experiment for yourself. I would also
highly recommend that you read through the aforementioned help documentation as it devotes quite a
bit of time to understanding when and where to apply specific enhancement techniques.
In my next and final blog post in this series, Ill demonstrate another useful tool within the BOPF
toolset: the BO test tool. This tool can be used to experiment with BOs and perform ad hoc unit tests,
etc.
NAVIGATING THE BOPF: PART 6 TESTING & UI INTEGRATION
We can access the BOPF Test UI by opening up Transaction /BOBF/TEST_UI. When you initially
open up the tool, youll be presented with a screen like the one shown below. From here, we can
begin working with a BO instance by plugging in the BO type in the SELECT BO input field (either
key it in or use the provided input help) and hitting the ENTER key. This causes the BO metadata to
be loaded into context so that we can use it to guide ourselves through the editing process.
EDITING BO INSTANCES
To create a new BO instance, we can double-click on the root node contained in the Metadata and
Instances tree on the left-hand side of the editor screen and then select the Create button in the
toolbar (see below). This will cause a new record to be created and loaded into an editable ALV grid.
From here, we can begin filling in node attributes, creating sub-node instances, and so on. Here, I
would draw your attention to the MESSAGES panel located in the bottom left-hand corner of the
editor. These messages can be used to help you fill in the right data.
If the BO instance that we want to maintain/display exists already, then we can load it into context
using the LOAD INSTANCES button menu. As you can see in the screenshot below, this menu
affords us with several different alternatives for loading node instances: via a BOPF node query, by
the node instance key, or by an alternative key (e.g. ID). Regardless of the menu path that we take,
the system will attempt to find the target node instance(s) and then load them into the editor window.
From here, we can select individual node instances by double-clicking on them in the METADATA
AND INSTANCES tree located on the left-hand side of the screen.
To edit node instances, we can select the node instance record in the editor on the right-hand side of
the screen and choose the appropriate option from the EDIT button menu (see below). Then, we can
edit attributes for a node instance using the provided input fields. Alternatively, we also have the
option of deleting a node instance (or indeed an entire BO instance in the case of a root node
instance) by clicking on the DELETE NODE INSTANCES button.
Regardless of whether or not were creating a new BO instance or editing an existing one, the entire
scope of our changes is tracked via a BOPF transaction like the one we would create if we were doing
all this by hand using the BOPF API. At any point along the way, we can choose to commit the
changes using the SAVE TRANSACTION button, or revert the changes using the CLEANUP
TRANSACTION button. Then, we can start the process over by selecting another BO instance or
editing the existing one in place. All in all, its kind of like table maintenance on steroids. But wait,
theres more!
In addition to the basic CRUD operations described earlier, the test UI also provides functions to call
actions, validations, and even trigger determinations. For a given node instance, these functions can
be accessed in the node instance toolbar via the CHECK and ACTIONS button menus (see below). If
you read through my blog posts related to the BOPF API, then these should feel quite intuitive.
Since the focus of this blog series has been primarily on introducing the BOPF framework, I have
purposefully avoided digressing into specific applications of the BOPF (e.g. in Transportation
Management or EHSM) since these products add additional layers on top of the BOPF that can sort
of cloud the picture a bit if you dont understand core principles of the BOPF itself. However, before I
bring this blog series to a close, I would be remiss if I didnt point out one important (and relatively
generic) framework built on top of the BOPF: the FLOORPLAN MANAGER BOPF
INTEGRATION (FBI) framework. As the name suggests, this framework links BOs from the BOPF
with Web UIs based on the Floorplan Manager (FPM) framework and Web Dynpro ABAP (WDA).
If youre developing Web UIs on top of BOs from the BOPF, then the FBI is definitely something to
take a look at. Essentially, the FBI exploits the genericity of the BOPF API and the accessibility of BO
model data to enable the rapid development of GENERIC USER INTERFACE BUILDING
BLOCKS (GUIBBs) based on BO nodes. Here, for example, we could create a form GUIBB that
allows users to populate the data for a BO node using a simple input form. In many applications, this
can be achieved without having to write a single line of code. While a detailed discussion of the FBI is
beyond the scope of this blog series, a quick Google search will lead you to some pretty decent
resource materials. If youre new to FPM, I would also offer a shameless plug for my book Web
Dynpro ABAP: The Comprehensive Guide (SAP PRESS, 2012).
CONCLUSION
When I first started working with the BOPF almost a year ago, I was surprised at how little
documentation there was to get started with. So, what youve seen in this series is the result of a lot of
trial-and-error and lessons learned by debugging past application-specific frameworks into the heart
of the BOPF itself. If youre just getting started with the BOPF, then I hope that youll find this series
useful to get you up and running. In the coming months and years, I think many more learning
resources will materialize to supplement what Ive offered here. Indeed, the number of new dimension
applications based on the BOPF appears to be growing by the day
One complaint I sometimes hear from other developers is that the BOPF API is cumbersome to work
with. On this point, I can agree to a point. However, I would argue that such complexities can be
abstracted away pretty easily with a wrapper class or two and some good old fashioned RTTI code.
Other than that, once you get used to the BOPF, I think youll find that you like it. And this is coming
from a developer who has had many bad experiences with BO frameworks (both in and outside
SAPcurse you EJBs!!!). All in all though, I have found the BOPF to be very comprehensive and
flexible. For me, one of the feel tests I normally conduct to gauge the effectiveness of a framework is
to ask myself how often the framework gets in my way: either because its too intractible, limited in
functionality or whatever. I have yet to run into any such occurrences with the BOPF. It does a good
job of providing default behaviors/functionality while at the same time affording you the opportunity to
tweak just about everything. For example, if I want to build my own caching mechanism, I can do so
by plugging in my own subclass. If I want to pull data from a HANA appliance in real time, I can do so
in a determination. You get the idea. Its all there, so just poke around a bit and I think youll find what
you need.