45
Business Objects Processing Framework (BOPF) – Why ABAP developers should have waited for it! Posted by Martin Fischer in BOPF Application Framework on Oct 10, 2013 3:31:45 PM inShare There are a lot of new and very interesting and hyped new technologies in the SAP world: HANA, mobile, cloud, NetWeaver Gateway, SAP UI5 and some more. That makes life very exciting for developers and geeks. I also like to use and test new technologies just for the sake of technology. And there is BOPF, an ABAP development framework which is used inside the SAP development teams since years (e.g. SAP Transportation Management is based on BOPF). It really makes custom development faster, more stable and the applications developed with BOPF have a similar architecture. BOPF is part of the Business Suite Foundation and since this spring general available for customers. BOPF is a framework which supports developing ABAP applications. This might sound a bit boring compared to the new technologies mentioned before. In this blog I want to explain why BOPF is a MUST for every ABAP developer who ever works in custom development projects. When I started in my first big ABAP based custom development project a couple of years ago, we started to build up our own object oriented ABAP development framework. We used a common three layer architecture and built for some extend a generic database layer. This was quite a lot of work, but it made it easy for the developers to use our database layer. Only a few developers knew exactly how it works. All the others just used the provided interfaces to access it. We had a central transaction manager which was responsible for committing the data and a common database lock handling. We also used common design patterns for the rest of the framework. There I realized how important it is to have a framework in big projects. New developers could focus on implementing new business functionality and because we had our common patterns it was quite easy for them to switch from one business object to another. But I have to admit, that it was quite hard work to implement and maintain the framework. With BOPF there is no need to design and implement such a framework on your own. You can just use BOPF and it brings even more time saving features for the developers. In BOPF you start to model your business objects in a declarative manner. You decide which entities a business object has, which actions can be executed on the entity and how this entity is associated to other entities of the same or entities of other business objects. The BOPF framework itself is implemented in a very generic way. The framework interprets the defined meta model and provides methods to interact with the business objects. After modelling your business objects, including data structures, keys, associations and actions BOPF supports you generating the needed DDIC objects and also interfaces for constants. As you also define the lock behavior of each entity, you can already test your business objects using the BOPF Test UI. The declarative way of modelling the business object has also a big advantage: BOPF generates a visualization of the defined model where you can easily see how the entities relate to each other. This visualization can also be used as a base for discussions between the functional architects and developers. So it’s easier for the functional architects to understand how the system works.

SAP Business Objects Processing Framework

  • Upload
    satyajc

  • View
    655

  • Download
    32

Embed Size (px)

Citation preview

Business Objects Processing Framework (BOPF) – Why ABAP developers should have waited for it! Posted by Martin Fischer in BOPF Application Framework on Oct 10, 2013 3:31:45 PM inShare There are a lot of new and very interesting and hyped new technologies in the SAP world: HANA, mobile, cloud, NetWeaver Gateway, SAP UI5 and some more. That makes life very exciting for developers and geeks. I also like to use and test new technologies just for the sake of technology. And there is BOPF, an ABAP development framework which is used inside the SAP development teams since years (e.g. SAP Transportation Management is based on BOPF). It really makes custom development faster, more stable and the applications developed with BOPF have a similar architecture. BOPF is part of the Business Suite Foundation and since this spring general available for customers. BOPF is a framework which supports developing ABAP applications. This might sound a bit boring compared to the new technologies mentioned before. In this blog I want to explain why BOPF is a MUST for every ABAP developer who ever works in custom development projects. When I started in my first big ABAP based custom development project a couple of years ago, we started to build up our own object oriented ABAP development framework. We used a common three layer architecture and built for some extend a generic database layer. This was quite a lot of work, but it made it easy for the developers to use our database layer. Only a few developers knew exactly how it works. All the others just used the provided interfaces to access it. We had a central transaction manager which was responsible for committing the data and a common database lock handling. We also used common design patterns for the rest of the framework. There I realized how important it is to have a framework in big projects. New developers could focus on implementing new business functionality and because we had our common patterns it was quite easy for them to switch from one business object to another. But I have to admit, that it was quite hard work to implement and maintain the framework. With BOPF there is no need to design and implement such a framework on your own. You can just use BOPF and it brings even more time saving features for the developers. In BOPF you start to model your business objects in a declarative manner. You decide which entities a business object has, which actions can be executed on the entity and how this entity is associated to other entities of the same or entities of other business objects. The BOPF framework itself is implemented in a very generic way. The framework interprets the defined meta model and provides methods to interact with the business objects. After modelling your business objects, including data structures, keys, associations and actions BOPF supports you generating the needed DDIC objects and also interfaces for constants. As you also define the lock behavior of each entity, you can already test your business objects using the BOPF Test UI. The declarative way of modelling the business object has also a big advantage: BOPF generates a visualization of the defined model where you can easily see how the entities relate to each other. This visualization can also be used as a base for discussions between the functional architects and developers. So it’s easier for the functional architects to understand how the system works.

Example of BOPF model visualization Furthermore with FBI (Floor Plan BOPF Integration) there is an easy and fast way to implement UIs for BOPF objects. This is an integration layer between BOPF and SAP Floor Plan Manager (FPM). You can define your UI elements based on your business model and the defined UI is from the beginning able to read and change data. Of course, it needs some time to be able to work with BOPF and to fully understand it. But this is a onetime effort. Afterwards you will deliver your custom development projects much faster as today. And you do not need to care about transaction handling and lock handling, because the framework takes care about all this. As it is used since years within SAP development, the framework is mature and stable. If you run into problems with the framework you can rely on your SAP support you have. If BOPF becomes common knowledge in the ABAP world, is makes it easy for developers to start in a new project. The developers can focus on the business functions and do not need to do a deep dive into the underlying development framework and architecture. With BOPF your team should have object oriented programming skills. Which is still a problem in the SAP world. But if you have a look on all the new technologies pushed by SAP, there is now future as an ABAP developer without these skills. If you are going to EMEA TechEd in Amsterdam, join Oliver Jägle’s session CD219, on November 6th: http://sessioncatalog.sapevents.com/go/ab.sessioncatalog/index.cfm?l=57&sf=1504 He will tell his experience with BOPF in a custom development project.

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.

What does BOPF stand for? 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.

Who uses BOPF? 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.

How can SAP customers use BOPF? 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. However, it is not available in the SAP NetWeaver standalone system, but only in the Business Suite Foundation layer, so that every SAP Business Suite customer can use it for their own development projects.

What are the main components of the application infrastructure? 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 don’t 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

User Interfaces and Consumption

Dynpro

BOPF provides a standard interface for consumption by the classic Dynpro UI.

Web Dynpro / Floor Plan Manager (FPM)

The generation and configuration of complex user interfaces has never been as easy as it is today with the FPM. FPM is implemented as a Web Dynpro component and 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. More: UI Developer Center on SCN

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. More: SAP NetWeaver Gateway on SCN

Business Object Layer & GenIL

. The Business Object Layer (BOL) provides a generic API for accessing business 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. More: SAP CRM on SCN

Process Integration

Post Processing Workflow

With BOPF BOs, you can integrate business processes using the Post Processing Workflow. More: Post Processing Framework (PPF) (on SCN )

Infrastructure Component

Archive Development Kit (ADK)

With ADK you archive not only table records but also business instances. Using BOPF you can select which BO instances have to be archived and then trigger the archiving process for them. More: Archive Development Kit on the SAP help portal

Change Documents

OPF uses the SAP NetWeaver Change Documents solution for recording changes on business object data. More: Change Documents on the SAP help portal

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. In BOPF, application logging is integrated with the help of the Application Log BO. This business object supports you when reading or writing application-specific log messages. 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 plus (BRF+)

BRF+ is a rule engine. It provides a comprehensive API and user interface for defining and processing business rules and expressions. With a BOPF-specific expression type, it is possible to create BO data retrieval expressions in BRF+. These expressions make data of BOs available in BRF+ environments that support any kind of rule processing. More: Business Rule Framework plus on SCN

What are the elements of the programming model? The business objectsare 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 and attributes 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 part of a business object.

Node Entity is used ...

Action to implement a service (operation or behavior) of a business object. An action is explicitly triggered by a service consumer, such as the user interface.

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.

Association to connect business object instances that are located on different nodes.

Figure: Business object metadata model

What kind of development environment is available for BOPF?

Tool Support Features

SAP Internal Design Time Tools (BOBF)

These are internal SAP tools and utilities that provide developers with a comprehensive feature set for building BO-centered applications for SAP Business Suite.

BO Builder (BOB)

With this tool, customers can enhance SAP business objects and also create their own ones 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 Test Environment (BOBT)

Starting from development environment, you can always test the current range of functions of each business object (or enhancement).

BO-Specific Debugging

Debugging on the business object entities level enables you to speed up the 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 Infrastructure

BOPF supports test automation and test-driven development. From the outset, 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.

How to get started? • 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. • Read the related blogs posts on SCN: o Navigating the BOPF: Part 2 - Business Object Overview o Navigating the BOPF: Part 3 - Working with the BOPF API o Navigating the BOPF: Part 4 - Advanced BOPF API Features o Navigating the BOPF: Part 5 - Enhancement Techniques o Navigating the BOPF: Part 6 - Testing & UI Integration

Navigating the BOPF: Part 2 - Business Object Overview Posted by James Wood in ABAP Development on Jan 4, 2013 10:12:04 PM inShare In my previous blog post, I briefly introduced the BOPF framework and its positioning within the ABAP development landscape. With that information in tow, we're now ready to begin peeling back the layers of the BOPF and seeing how all the pieces fit together from a technical perspective. In this blog post, we'll get things started by taking a look at the design time aspects of business objects.

Business Objects Overview According to SAP's 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 component model. • Have a well-defined process model which governs the business object lifecycle, behaviors, 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.).

Anatomy of a Business Object From a modeling perspective, BOs are made up of several different types of entities:

• Nodes o Nodes are used to model a BO's data. o 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.

o There are several different node types supported by the BOPF. However, most of the time you'll 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).

o 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 node's attributes are defined in terms of structure definitions from the ABAP Dictionary.

o At runtime, a BO node is like a container which may have zero, one, or many rows. If you're familiar with the concept of controller contexts with the Web Dynpro programming model, then this concept should feel familiar to you. If not, don't worry; we'll demonstrate how this works whenever we look at the BOPF API.

• Actions o Actions define the services (or behavior) of a BO.

o Actions are assigned to individual nodes within a BO. o The functionality provided by an action is (usually) defined in terms of an ABAP Objects

class that implements the /BOBF/IF_FRW_ACTION interface. o To some extent, it is appropriate to think of actions as being similar to the methods of an

ABAP Objects class. • Associations o 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.

o For example, in just a moment, we'll take a look at a sample BO called /BOBF/DEMO_SALES_ORDER which is used to model sales orders. Here, we'll see how the product assignments for sales order items is defined in terms of an association with a product BO called /BOBF/DEMO_PRODUCT. This composition technique makes it possible to not only leverage the product BOs data model, but also its behaviors, etc.

o Associations allow us to integrate BOs together in complex assemblies à la Legos™. • Determinations o 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".

o 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:

� "Derive dependent data immediately after modification" � 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. � "Derive dependent data before saving" � 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.

� "Fill transient attributes of persistent nodes" � 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.

� "Derive instances of transient nodes" � This pattern allows us to load transient nodes into memory on demand.

Here, for example, we might lookup real-time status data from a Web service and load it into the attributes of a transient node from downstream consumption.

o Determination patterns are described in detail within the aforementioned BOPF enhancement guide.

o The logic within a determination is defined via an ABAP Objects class that implements the /BOBF/IF_FRW_DETERMINATION interface.

• Validations o 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". o Validations come in two distinct forms: � 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.

o The validation logic is encapsulated within an ABAP Objects class that implements the /BOBF/IF_FRW_VALIDATION interface.

• Queries o Queries are BO node entities which allow us to search for BOs using various types of

search criteria. o Queries make it possible for consumers to access BOs without knowing the BO key up

front. o Queries also integrate quite nicely with search frameworks and the like. o Queries come in two varieties: � Node Attribute Queries � 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, I've 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 BO's 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 node's data model, implementation classes, and so on.

We'll 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, we'll 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 Posted by James Wood in ABAP Development on Jan 16, 2013 11:35:10 PM inShare In my previous blog post, we explored the anatomy of business objects within the BOPF. There, we were able to observe the various entities that make up a BO: nodes/attributes, actions, associations, determinations, validations, and queries. Now that you have a feel for what these entities are, we're ready to begin taking a look at the API that is used to manipulate these entities. To guide us through this demonstration, we'll explore 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. You can download the complete example program source code here. Note: The code bundle described above has been enhanced as of 9/18/2013. The code was reworked to factor out a BOPF utilities class of sorts and also demonstrate how to traverse over to dependent objects (DOs).

BOPF API Overview Before we begin coding with the BOPF API, let's 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 we'll be working with to perform most of the operations within the BOPF:

• /BOBF/IF_TRA_TRANSACTION_MGR o 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 o 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 o This object reference provides us with metadata for a particular BO. We'll explore the

utility of having access to this metadata coming up shortly.

In the upcoming sections, I'll show you how these various API classes collaborate in typical BOPF use cases. Along the way, we'll 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 you'll 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 Program to Manipulate Customer Objects To demonstrate the BOPF API, we'll 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, we'll 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. CLASS lcl_demo DEFINITION CREATE PRIVATE. PRIVATE SECTION. DATA mo_txn_mngr TYPE REF TO /bobf/if_tra_transaction_mgr. DATA mo_svc_mngr TYPE REF TO /bobf/if_tra_service_manager. DATA mo_bo_conf TYPE REF TO /bobf/if_frw_configuration. METHODS: constructor RAISING /bobf/cx_frw. ENDCLASS. CLASS lcl_demo IMPLEMENTATION. METHOD constructor. "Obtain a reference to the BOPF transaction manager:

me->mo_txn_mngr = /bobf/cl_tra_trans_mgr_factory=>get_transaction_manager( ). "Obtain a reference to the BOPF service manager: me->mo_svc_mngr = /bobf/cl_tra_serv_mgr_factory=>get_service_manager( /bobf/if_demo_customer_c=>sc_bo_key ). "Access the metadata for the /BOBF/DEMO_CUSTOMER BO: me->mo_bo_conf = /bobf/cl_frw_factory=>get_configuration( /bobf/if_demo_customer_c=>sc_bo_key ). ENDMETHOD. " METHOD constructor 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 BO's 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 BO's key, its defined nodes, associations, queries, and so on. We'll end up using this interface quite a bit during the course of our development.

Creating New Customers 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, we'll 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. CLASS lcl_demo DEFINITION CREATE PRIVATE. PUBLIC SECTION. CLASS-METHODS: create_customer IMPORTING iv_customer_id TYPE /bobf/demo_customer_id. ... ENDCLASS. CLASS lcl_demo IMPLEMENTATION. METHOD create_customer. "Method-Local Data Declarations: DATA lo_driver TYPE REF TO lcl_demo. DATA lt_mod TYPE /bobf/t_frw_modification. DATA lo_change TYPE REF TO /bobf/if_tra_change. DATA lo_message TYPE REF TO /bobf/if_frw_message. DATA lv_rejected TYPE boole_d. DATA lx_bopf_ex TYPE REF TO /bobf/cx_frw. DATA lv_err_msg TYPE string. DATA lr_s_root TYPE REF TO /bobf/s_demo_customer_hdr_k. DATA lr_s_txt TYPE REF TO /bobf/s_demo_short_text_k. DATA lr_s_txt_hdr TYPE REF TO /bobf/s_demo_longtext_hdr_k. DATA lr_s_txt_cont TYPE REF TO /bobf/s_demo_longtext_item_k.

FIELD-SYMBOLS: <ls_mod> LIKE LINE OF lt_mod. "Use the BOPF API to create a new customer record: TRY. "Instantiate the driver class: CREATE OBJECT lo_driver. "Build the ROOT node: CREATE DATA lr_s_root. lr_s_root->key = /bobf/cl_frw_factory=>get_new_key( ). lr_s_root->customer_id = iv_customer_id. lr_s_root->sales_org = 'AMER'. lr_s_root->cust_curr = 'USD'. lr_s_root->address_contry = 'US'. lr_s_root->address = '1234 Any Street'. APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>. <ls_mod>-node = /bobf/if_demo_customer_c=>sc_node-root. <ls_mod>-change_mode = /bobf/if_frw_c=>sc_modify_create. <ls_mod>-key = lr_s_root->key. <ls_mod>-data = lr_s_root. "Build the ROOT_TEXT node: CREATE DATA lr_s_txt. lr_s_txt->key = /bobf/cl_frw_factory=>get_new_key( ). lr_s_txt->text = 'Sample Customer Record'. lr_s_txt->language = sy-langu. APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>. <ls_mod>-node = /bobf/if_demo_customer_c=>sc_node-root_text. <ls_mod>-change_mode = /bobf/if_frw_c=>sc_modify_create. <ls_mod>-source_node = /bobf/if_demo_customer_c=>sc_node-root. <ls_mod>-association = /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. "Build the ROOT_LONG_TEXT node: "If you look at the node type for this node, you'll notice that "it's a "Delegated Node". In other words, it is defined in terms "of the /BOBF/DEMO_TEXT_COLLECTION business object. The following "code accounts for this indirection. CREATE DATA lr_s_txt_hdr. lr_s_txt_hdr->key = /bobf/cl_frw_factory=>get_new_key( ). APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>. <ls_mod>-node = /bobf/if_demo_customer_c=>sc_node-root_long_text. <ls_mod>-change_mode = /bobf/if_frw_c=>sc_modify_create. <ls_mod>-source_node = /bobf/if_demo_customer_c=>sc_node-root. <ls_mod>-association = /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.'. APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>. <ls_mod>-node = 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' ). "Create the customer record: CALL METHOD lo_driver->mo_svc_mngr->modify EXPORTING it_modification = lt_mod IMPORTING eo_change = lo_change eo_message = lo_message. "Check for errors: IF lo_message IS BOUND. IF lo_message->check( ) EQ abap_true. lo_driver->display_messages( lo_message ). RETURN. ENDIF. ENDIF. "Apply the transactional changes: CALL METHOD lo_driver->mo_txn_mngr->save IMPORTING eo_message = lo_message ev_rejected = lv_rejected. IF lv_rejected EQ abap_true. lo_driver->display_messages( lo_message ). RETURN. ENDIF. "If we get to here, then the operation was successful: WRITE: / 'Customer', iv_customer_id, 'created successfully.'. CATCH /bobf/cx_frw INTO lx_bopf_ex. lv_err_msg = lx_bopf_ex->get_text( ). WRITE: / lv_err_msg. ENDTRY. ENDMETHOD. " METHOD create_customer ENDCLASS. 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 you've 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 we're 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. That's pretty much all there is to it. CLASS lcl_demo DEFINITION CREATE PRIVATE. PRIVATE SECTION. METHODS: display_messages IMPORTING io_message TYPE REF TO /bobf/if_frw_message. ENDCLASS. CLASS lcl_demo IMPLEMENTATION. METHOD display_messages. "Method-Local Data Declarations: DATA lt_messages TYPE /bobf/t_frw_message_k. DATA lv_msg_text TYPE string. FIELD-SYMBOLS <ls_message> LIKE LINE OF lt_messages.

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

Performing Customer Queries 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 don't 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. CLASS lcl_demo DEFINITION CREATE PRIVATE. PRIVATE SECTION. METHODS: get_customer_for_id IMPORTING iv_customer_id TYPE /bobf/demo_customer_id RETURNING VALUE(rv_customer_key) TYPE /bobf/conf_key RAISING /bobf/cx_frw. ENDCLASS. CLASS lcl_demo IMPLEMENTATION. METHOD get_customer_for_id. "Method-Local Data Declarations: DATA lo_driver TYPE REF TO lcl_demo. DATA lt_parameters TYPE /bobf/t_frw_query_selparam. DATA lt_customer_keys TYPE /bobf/t_frw_key. DATA lx_bopf_ex TYPE REF TO /bobf/cx_frw. DATA lv_err_msg TYPE string. FIELD-SYMBOLS <ls_parameter> LIKE LINE OF lt_parameters. FIELD-SYMBOLS <ls_customer_key> LIKE LINE OF lt_customer_keys. "Instantiate the test driver class: CREATE OBJECT lo_driver.

"Though we could conceivably lookup the customer using an SQL query, "the preferred method of selection is a BOPF query: APPEND INITIAL LINE TO lt_parameters ASSIGNING <ls_parameter>. <ls_parameter>-attribute_name = /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. CALL METHOD lo_driver->mo_svc_mngr->query EXPORTING iv_query_key = /bobf/if_demo_customer_c=>sc_query-root-select_by_attributes it_selection_parameters = lt_parameters IMPORTING et_key = lt_customer_keys. "Return the matching customer's KEY value: READ TABLE lt_customer_keys INDEX 1 ASSIGNING <ls_customer_key>. IF sy-subrc EQ 0. rv_customer_key = <ls_customer_key>-key. ENDIF. ENDMETHOD. " METHOD get_customer_for_id ENDCLASS.

Displaying Customer Records 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 RETRIEVE() and RETRIEVE_BY_ASSOCIATION() methods provided by the /BOBF/IF_TRA_SERVICE_MANAGER instance. As simple as this sounds, the devil is in the details. Here, in addition to constructing the calls to the RETRIEVE*() methods, we must also dynamically define the result tables which will be used to store the results. 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 we're using the /BOBF/IF_FRW_CONFIGURATION object reference to lookup the node's 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_CUSTOMER object 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. CLASS lcl_demo DEFINITION CREATE PRIVATE. PUBLIC SECTION. CLASS-METHODS:

display_customer IMPORTING iv_customer_id TYPE /bobf/demo_customer_id. 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, get_node_row 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 iv_index TYPE i DEFAULT 1 RETURNING VALUE(rr_data) TYPE REF TO data RAISING /bobf/cx_frw, get_node_table_by_assoc IMPORTING iv_key TYPE /bobf/conf_key iv_node_key TYPE /bobf/obm_node_key iv_assoc_key TYPE /bobf/obm_assoc_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, get_node_row_by_assoc IMPORTING iv_key TYPE /bobf/conf_key iv_node_key TYPE /bobf/obm_node_key iv_assoc_key TYPE /bobf/obm_assoc_key iv_edit_mode TYPE /bobf/conf_edit_mode 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. CLASS lcl_demo IMPLEMENTATION. METHOD display_customer. "Method-Local Data Declarations: DATA lo_driver TYPE REF TO lcl_demo. DATA lv_customer_key TYPE /bobf/conf_key. DATA lx_bopf_ex TYPE REF TO /bobf/cx_frw. DATA lv_err_msg TYPE string. DATA lr_s_root TYPE REF TO /bobf/s_demo_customer_hdr_k. DATA lr_s_text TYPE REF TO /bobf/s_demo_short_text_k. "Try to display the selected customer: TRY. "Instantiate the test driver class: CREATE OBJECT lo_driver. "Lookup the customer's key attribute using a query: lv_customer_key = lo_driver->get_customer_for_id( iv_customer_id ). "Display the header-level details for the customer: 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_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. "Traverse to the ROOT_TEXT node to display the customer short text: lr_s_text ?= lo_driver->get_node_row_by_assoc( 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. DATA lo_message TYPE REF TO /bobf/if_frw_message. FIELD-SYMBOLS <ls_key> LIKE LINE OF lt_key. FIELD-SYMBOLS <lt_data> TYPE INDEX TABLE. "Lookup the node's configuration: CALL METHOD mo_bo_conf->get_node EXPORTING iv_node_key = iv_node_key IMPORTING es_node = ls_node_conf. "Use the node configuration metadata to create the result table: CREATE DATA rr_data TYPE (ls_node_conf-data_table_type). ASSIGN rr_data->* TO <lt_data>. "Retrieve the target node: APPEND INITIAL LINE TO lt_key ASSIGNING <ls_key>. <ls_key>-key = iv_key. CALL METHOD mo_svc_mngr->retrieve EXPORTING iv_node_key = iv_node_key it_key = lt_key IMPORTING eo_message = lo_message eo_change = lo_change et_data = <lt_data>.

"Check the results: IF lo_message IS BOUND. IF lo_message->check( ) EQ abap_true. display_messages( lo_message ). RAISE EXCEPTION TYPE /bobf/cx_dac. ENDIF. ENDIF. ENDMETHOD. " METHOD get_node_table METHOD get_node_row. "Method-Local Data Declarations: DATA lr_t_data TYPE REF TO data. FIELD-SYMBOLS <lt_data> TYPE INDEX TABLE. FIELD-SYMBOLS <ls_row> TYPE ANY. "Lookup the node data: lr_t_data = get_node_table( iv_key = iv_key iv_node_key = iv_node_key iv_edit_mode = iv_edit_mode ). IF lr_t_data IS NOT BOUND. RAISE EXCEPTION TYPE /bobf/cx_dac. ENDIF. "Try to pull the record at the specified index: ASSIGN lr_t_data->* TO <lt_data>. READ TABLE <lt_data> INDEX iv_index ASSIGNING <ls_row>. IF sy-subrc EQ 0. GET REFERENCE OF <ls_row> INTO rr_data. ELSE. RAISE EXCEPTION TYPE /bobf/cx_dac. ENDIF. ENDMETHOD. " METHOD get_node_row 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. FIELD-SYMBOLS <ls_key> LIKE LINE OF lt_key. FIELD-SYMBOLS <lt_data> TYPE INDEX TABLE. "Lookup the association metadata to find out more "information about the target sub-node: CALL METHOD mo_bo_conf->get_assoc EXPORTING iv_assoc_key = iv_assoc_key iv_node_key = iv_node_key IMPORTING es_assoc = ls_association. IF ls_association-target_node IS NOT BOUND. RAISE EXCEPTION TYPE /bobf/cx_dac. ENDIF.

"Use the node configuration metadata to create the result table: ls_node_conf = ls_association-target_node->*. CREATE DATA rr_data TYPE (ls_node_conf-data_table_type). ASSIGN rr_data->* TO <lt_data>. "Retrieve the target node: APPEND INITIAL LINE TO lt_key ASSIGNING <ls_key>. <ls_key>-key = iv_key. CALL METHOD mo_svc_mngr->retrieve_by_association EXPORTING iv_node_key = iv_node_key it_key = lt_key iv_association = iv_assoc_key iv_fill_data = abap_true IMPORTING eo_message = lo_message eo_change = lo_change et_data = <lt_data>. "Check the results: IF lo_message IS BOUND. IF lo_message->check( ) EQ abap_true. display_messages( lo_message ). RAISE EXCEPTION TYPE /bobf/cx_dac. ENDIF. ENDIF. ENDMETHOD. " METHOD get_node_table_by_assoc METHOD get_node_row_by_assoc. "Method-Local Data Declarations: DATA lr_t_data TYPE REF TO data. FIELD-SYMBOLS <lt_data> TYPE INDEX TABLE. FIELD-SYMBOLS <ls_row> TYPE ANY. "Lookup the node data: lr_t_data = get_node_table_by_assoc( iv_key = iv_key iv_node_key = iv_node_key iv_assoc_key = iv_assoc_key iv_edit_mode = iv_edit_mode ). IF lr_t_data IS NOT BOUND. RAISE EXCEPTION TYPE /bobf/cx_dac. ENDIF. "Try to pull the record at the specified index: ASSIGN lr_t_data->* TO <lt_data>. READ TABLE <lt_data> INDEX iv_index ASSIGNING <ls_row>. IF sy-subrc EQ 0. GET REFERENCE OF <ls_row> INTO rr_data. ELSE. RAISE EXCEPTION TYPE /bobf/cx_dac. ENDIF. ENDMETHOD. " METHOD get_node_row_by_assoc ENDCLASS.

Note: In this simple example, we didn't bother to drill down to display the contents of the ROOT_LONG_TEXT node. 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.

Modifying Customer Records The process of modifying a customer record essentially combines logic from the display and create functions. The basic process is as follows: 1. First, we perform a query to find the target customer record. 2. 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.

3. 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, we're simply updating the address string on the customer. Of course, we could have performed wholesale changes if we had wanted to. CLASS lcl_demo DEFINITION CREATE PRIVATE. PUBLIC SECTION. CLASS-METHODS: change_customer IMPORTING iv_customer_id TYPE /bobf/demo_customer_id. ENDCLASS. CLASS lcl_demo IMPLEMENTATION. METHOD change_customer. "Method-Local Data Declarations: DATA lo_driver TYPE REF TO lcl_demo. DATA lv_customer_key TYPE /bobf/conf_key. DATA lt_mod TYPE /bobf/t_frw_modification. DATA lo_change TYPE REF TO /bobf/if_tra_change. DATA lo_message TYPE REF TO /bobf/if_frw_message. DATA lv_rejected TYPE boole_d. DATA lx_bopf_ex TYPE REF TO /bobf/cx_frw. DATA lv_err_msg TYPE string. FIELD-SYMBOLS: <ls_mod> LIKE LINE OF lt_mod. DATA lr_s_root TYPE REF TO /bobf/s_demo_customer_hdr_k. "Try to change the address on the selected customer: TRY. "Instantiate the test driver class: CREATE OBJECT lo_driver. "Access the customer ROOT node: lv_customer_key = lo_driver->get_customer_for_id( iv_customer_id ).

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 ). "Change the address string on the customer: lr_s_root->address = '1234 Boardwalk Ave.'. APPEND INITIAL LINE TO lt_mod ASSIGNING <ls_mod>. <ls_mod>-node = /bobf/if_demo_customer_c=>sc_node-root. <ls_mod>-change_mode = /bobf/if_frw_c=>sc_modify_update. <ls_mod>-key = lr_s_root->key. <ls_mod>-data = lr_s_root. "Update the customer record: CALL METHOD lo_driver->mo_svc_mngr->modify EXPORTING it_modification = lt_mod IMPORTING eo_change = lo_change eo_message = lo_message. "Check for errors: IF lo_message IS BOUND. IF lo_message->check( ) EQ abap_true. lo_driver->display_messages( lo_message ). RETURN. ENDIF. ENDIF. "Apply the transactional changes: CALL METHOD lo_driver->mo_txn_mngr->save IMPORTING eo_message = lo_message ev_rejected = lv_rejected. IF lv_rejected EQ abap_true. lo_driver->display_messages( lo_message ). RETURN. ENDIF. "If we get to here, then the operation was successful: WRITE: / 'Customer', iv_customer_id, 'updated successfully.'. CATCH /bobf/cx_frw INTO lx_bopf_ex. lv_err_msg = lx_bopf_ex->get_text( ). WRITE: / lv_err_msg. ENDTRY. ENDMETHOD. " METHOD change_customer ENDCLASS.

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, we'll expand upon what we've learned and consider some more advanced features of the BOPF API.

Navigating the BOPF: Part 4 - Advanced BOPF API Features Posted by James Wood in ABAP Development on Jan 30, 2013 12:16:32 AM inShare In my previous blog post, I introduced the BOPF API and demonstrated how the API could be used to perform routine CRUD operations on a business object. With this basic introduction out of the way, we're now ready to tackle more advanced API features such as consistency checks, the execution of actions, and transaction management. So, without further ado, let's get started.

Performing Consistency Checks & Validations 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, there's 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. DATA lt_key TYPE /bobf/t_frw_key. FIELD-SYMBOLS <ls_key> LIKE LINE OF lt_key. DATA lo_message TYPE REF TO /bobf/if_frw_message. TRY. APPEND INITIAL LINE TO lt_key ASSIGNING <ls_key>. <ls_key>-key = iv_key. "<== Sales Order BO Key CALL METHOD mo_svc_mngr->check_consistency 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. ... CATCH /bobf/cx_frw INTO lx_frw. ... ENDTRY. I'll 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, we're 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, it's pretty much business as usual. DATA lt_key TYPE /bobf/t_frw_key. FIELD-SYMBOLS <ls_key> LIKE LINE OF lt_key. DATA ls_parameters TYPE /bobf/s_demo_sales_order_hdr_d. DATA lr_s_parameters TYPE REF TO data. DATA lo_change TYPE REF TO /bobf/if_tra_change. DATA lo_message TYPE REF TO /bobf/if_frw_message. DATA lt_failed_key TYPE /bobf/t_frw_key. DATA lt_failed_act_key TYPE /bobf/t_frw_key. TRY. "Set the BO instance key: APPEND INITIAL LINE TO lt_key ASSIGNING <ls_key>. <ls_key>-key = iv_key. "<== Sales Order BO Key "Populate the parameter structure that contains parameters passed "to the action: ls_parameters-item_no = '001'. GET REFERENCE OF ls_parameters INTO lr_s_parameters. "Call the DELIVER action defined on the ROOT node: CALL METHOD mo_svc_mngr->do_action 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. ... CATCH /bobf/cx_frw INTO lx_frw. ... 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 SAVE() method defined by the /BOBF/IF_TRA_TRANSACTION_MGR interface.

Action Validations 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). DATA lt_key TYPE /bobf/t_frw_key. FIELD-SYMBOLS <ls_key> LIKE LINE OF lt_key. DATA ls_parameters TYPE /bobf/s_demo_sales_order_hdr_d. DATA lr_s_parameters TYPE REF TO data. DATA lo_message TYPE REF TO /bobf/if_frw_message. DATA lt_failed_key TYPE /bobf/t_frw_key. DATA lt_failed_act_key TYPE /bobf/t_frw_key. TRY. "Set the BO instance key: APPEND INITIAL LINE TO lt_key ASSIGNING <ls_key>. <ls_key>-key = iv_key. "<== Sales Order BO Key "Populate the parameter structure that contains parameters passed "to the action: ls_parameters-item_no = '001'. GET REFERENCE OF ls_parameters INTO lr_s_parameters. "Check to see if the DELIVER action can be invoked: CALL METHOD mo_svc_mngr->check_action 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. ... CATCH /bobf/cx_frw INTO lx_frw. ... 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. Once a transaction is committed, we can reset the transaction manager by calling the CLEANUP() method. Or, alternatively, we can also use this method to abandon an in-flight transaction once an error condition has been detected. In the latter case, this is analogous to using the ROLLBACK WORK statement in ABAP to rollback a transaction. 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, we'll shift gears and begin looking at ways of enhancing BOs using the BOPF toolset.

Navigating the BOPF: Part 5 - Enhancement Techniques Posted by James Wood in ABAP Development on Feb 22, 2013 11:32:31 PM

inShare In my previous two blog posts, we explored the BOPF API from a client point-of-view. Here, we learned how to perform basic CRUD operations, execute actions, and so on. Now that you have a feel for how the API operates, we're ready to take a peek behind the curtain and see these services are implemented within the business objects themselves. For now, our emphasis will be on enhancing these services since SAP does not yet support the creation of new business objects. However, whether we're enhancing existing business objects or creating new ones from scratch, the concepts remain the same.

What to Enhance? Before we dive into the exploration of specific enhancement techniques, let's first take a look at the kinds of entities we're 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 o 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 o 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 o 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. o 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 o 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. o A custom validation might also be used to graft in a new set of business rules or a

custom security model. • Actions o 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 o 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 won't attempt to re-invent the wheel in this blog post. Instead, I'll 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, you'll 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. We'll 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 BO's constants interface is used for BOPF API calls, etc.

Enhancing the BO Data Model 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 don't 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-node's 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. We'll 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:

1. Implementing Class: o We must create or assign an ABAP Objects class that implements the

/BOBF/IF_FRW_DETERMINATION interface. 2. Determination Pattern: o 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 you're enhancing, its location in the node hierarchy, and so on.

o 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_DETERMINATION interface in the Class Builder tool and SAP standard-delivered determinations implementations available in the system you're working on.

Defining Consistency Validations 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:

1. Implementing Class: o Here, we must create/assign an ABAP Objects class which implements the

/BOBF/IF_FRW_VALIDATION interface. 2. Request Nodes: o This property allows us to specify which node operations should force a validation to

occur (e.g. during creates, updates, etc.) 3. Impact: o 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? From an implementation perspective, the /BOBF/IF_FRW_VALIDATION interface provides us with everything we need to perform the validation check: the context of the validation, the current data within the node instance being validated, and so on. For more information about how to implement the validation class, I would highly recommend that you read through the interface documentation for the /BOBF/IF_FRW_VALIDATION interface in the Class Builder tool. It can also be helpful to look at various standard-delivered classes which already implement this interface to see common patterns/idioms used by SAP.

Working with Actions When it comes to the customization of actions, we have a couple of options:

• We can create a brand new action definition for a given node (standard or custom). • We can enhance existing actions with pre/post action enhancements.

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: o 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: o 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: o 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 weren't designed for.

Defining Custom Queries Compared to the various enhancement options we've 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, you'll find that the framework doesn't 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 it's 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, I'll 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 Posted by James Wood in ABAP Development on Mar 4, 2013 10:59:35 PM inShare In my previous blog post, we looked at ways of enhancing business objects (BOs) defined within the BOPF. Once these enhancements are in place, it is a good idea to unit test the changes to make sure that the BO is working correctly. If you subscribe to the test-driven development (TDD) model, then an obvious choice here would be to use the BOPF API to write some ABAP Unit test cases. However, sometimes we just want to run a quick ad hoc test. Or, we might just want to display BO data at a

glance without constructing a multi-table join in Transaction SQVI. For these tasks and others, SAP provides us with a very slick tool: the BOPF Test UI. In this final blog post, I will introduce this transaction and demonstrate its basic capabilities. Then, to round things out, I'll briefly touch on UI integration and the FBI framework.

Working with the BOPF Test UI We can access the BOPF Test UI by opening up Transaction /BOBF/TEST_UI. When you initially open up the tool, you'll 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 Once the BO metadata is loaded, we have two choices for maintenance:

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

2. 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 we're 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, it's kind of like table maintenance on steroids. But wait, there's more!

Triggering Actions, Validations, & Determinations 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.

UI Integration and the FBI Framework 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 don't understand core principles of the BOPF itself. However, before I bring this blog series to a close, I would be remiss if I didn't 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 you're 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 you're 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 you've 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 you're just getting started with the BOPF, then I hope that you'll 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 I've 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 you'll 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 SAP...curse 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 it's 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. It's all there, so just poke around a bit and I think you'll find what you need.