Diesen Artikel habe ich ursprünglich im SAP Community Network veröffentlicht.
Wir kann ich die mit den Object Services (OS generierten Persistenzklassen mit Änderungsbelegschreibung (Change Documents, CDO) kombinieren?
Diese Frage wurde bereits mehrfach an verschiedenen Stellen gestellt, und bisher bin ich noch nicht auf eine Antwort gestoßen, die als Umsetzungsrichtlinie dienen könnte. Da wir die beiden Frameworks für ein Entwicklungsprojekt kombinieren müssen, habe ich beschlossen, eine kurze Machbarkeitsanalyse durchzuführen und einige Details und Fallstricke mit der Community zu teilen. Für diesen Artikel benötigen Sie ein grundlegendes Verständnis sowohl der Objektdienste als auch der Änderungsdokumente und ein recht solides Verständnis der grundlegenden Prinzipien der Objektorientierung.
Der erste wichtige Punkt ist, sich daran zu erinnern, dass Object Services und Änderungsdokumente auf unterschiedlichen Abstraktionsebenen arbeiten. Die von Object Services generierten persistenten Klassen arbeiten mit der technischen Darstellung der betreffenden Entitäten – beispielsweise dem Rechnungskopf oder einer einzelnen Position einer Rechnung. Die allgemeine Faustregel lautet „eine Tabelle, eine Klasse“. Bei Änderungsdokumenten ist das anders. Normalerweise finden Sie Änderungsdokumentobjekte für logische Objekte statt der technischen Darstellungen. Ein Änderungsbelegobjekt umfasst in der Regel mehrere Tabellendefinitionen, die zum Speichern des logischen Objekts benötigt werden. Beispielsweise finden Sie ein Änderungsbelegobjekt für eine Rechnung, das sowohl Änderungen am Rechnungskopf als auch an den entsprechenden Positionen aufzeichnet. In dieser Hinsicht ähneln Änderungsbelege Sperrobjekten. Es gibt bereits ein Dokument von Katan Patel, das einige der grundlegenden Aspekte dieses Ansatzes behandelt.
In diesem Artikel verwende ich ein grundlegendes Modell einiger beliebiger Kopfzeilendaten mit einigen beliebigen Elementen. Die Einrichtung enthält zwei Tabellen mit zugehörigen persistenten Klassen und sieht ungefähr so aus:

Beachten Sie, dass ich den Agenten, den Basisagenten und die Standardklassen- und Schnittstellenmethoden zur besseren Übersicht ausgelassen habe.
Die Object Services bieten eine Möglichkeit, die gesamte Schlüsselverwaltung in die Hände der generierten
Persistenzklasse zu legen. Sie müssen lediglich den Datentyp OS_GUID
für ein Primärschlüsselfeld verwenden. Dies mag
in einigen Fällen praktisch sein, aber Sie sollten diese Funktion nicht verwenden, wenn Sie Änderungsdokumente
basierend auf den Datentabellen aufzeichnen möchten. OS_GUID
ist ein RAW
-Typ, und dies verursacht alle möglichen
Probleme im Änderungsdokumentengenerator und in den Berichten, die zur Anzeige der Änderungsdokumente verwendet werden.
In diesem Beispiel habe ich einen zusammengesetzten Geschäftsschlüssel verwendet, der aus dem Unternehmenscode und einem
beliebigen Schlüssel besteht – wahrscheinlich einer GUID, aber das ist in diesem Fall irrelevant.
Wenn die Tabellen vorhanden und aktiv sind, ist es möglich, das Änderungsbelegobjekt zu erstellen und die zugehörigen Objekte zu generieren. In diesem Fall gibt es nichts Besonderes zu beachten: Der Header wird als einzelner Eintrag übergeben, während die Elemente als Tabelle übergeben werden:

Der Funktionsbaustein zum Schreiben der Änderungsbelege kann dann mit den Standardparametern generiert werden. In diesem
Fall habe ich den Standardnamen ZOSCDO_WRITE_DOCUMENT
verwendet.
Bevor wir uns nun der eigentlichen Magie der Änderungsbelege zuwenden können, gibt es ein kleines Problem zu lösen.
Änderungsbelege werden verwendet, um die Erstellung, Änderung und Löschung von Geschäftsobjekten aufzuzeichnen. Die
Erstellung und Änderung sind einfach, aber es gibt ein Problem bei gelöschten Objekten. Die generierte Kombination aus
Agent und Basis-Agent stellt ein Ereignis namens IF_OS_FACTORY~DELETED
bereit, das immer dann ausgelöst wird, wenn
ein Objekt gelöscht wird. Leider wird das Ereignis ausgelöst, nachdem die Objektinstanz ungültig gemacht wurde, und
daher führt jeder weitere Versuch, den Geschäftsschlüssel aus dem Objekt zu erhalten, zu einer Ausnahme. Die
Schnittstelle IF_OS_FACTORY
bietet auch ein Ereignis mit dem Namen TO_BE_DELETED
, das die Lösung für dieses Problem
zu sein scheint, aber zumindest in meinem Szenario wird dieses Ereignis von den generierten Agenten nie ausgelöst. Ich
konnte nur einige Hinweise auf dieses Ereignis in älteren Agentenimplementierungen finden – es sieht so aus, als wäre
dieses Ereignis aus irgendeinem Grund aus der Standardimplementierung entfernt worden. Glücklicherweise ist es ziemlich
einfach, es wieder zu aktivieren – ich musste nur die Methode EXT_PM_DELETED_PERSISTENT
in jedem der Agenten neu
definieren und die folgende Implementierung hinzufügen:
METHOD ext_pm_deleted_persistent.
DATA: ls_backup_object_info TYPE typ_object_info,
l_backup_object_index TYPE typ_index,
lr_backup_object_iref TYPE typ_object_iref.
CALL METHOD super->ext_pm_deleted_persistent.
ls_backup_object_info = current_object_info.
l_backup_object_index = current_object_index.
lr_backup_object_iref = current_object_iref.
RAISE EVENT if_os_factory~to_be_deleted
EXPORTING
object = current_object_iref.
current_object_info = ls_backup_object_info.
current_object_index = l_backup_object_index.
current_object_iref = lr_backup_object_iref.
ENDMETHOD.
Es ist wichtig, einige der oben gezeigten Attribute zu speichern und wiederherzustellen – andernfalls würde der Lesezugriff, der später vom Event-Handler benötigt wird, diese Variablen ändern und erneut zu einem Kurzdump führen würde.
Mit diesen Vorbereitungen ist es möglich, die eigentliche Änderungsdokumenterstellung in Angriff zu nehmen. In diesem
Beispiel habe ich eine separate Klasse namens ZCL_OSCDO_CHANGE_DOC_WRITER
verwendet, um die Ereignisbehandlung und
Datenverarbeitung zu umschließen. Innerhalb dieser Klasse verwende ich die folgenden Datentypen, um die Änderungen an
den Daten zu verfolgen:
TYPES:
BEGIN OF t_item_data,
item_number TYPE zoscdo_item_number,
change TYPE cdchngind,
old_data TYPE zoscdo_item,
new_data TYPE zoscdo_item,
END OF t_item_data .
TYPES:
tt_item_data TYPE SORTED TABLE OF t_item_data
WITH UNIQUE KEY item_number .
TYPES:
BEGIN OF t_header_data,
company_code TYPE bukrs,
header_id TYPE zoscdo_header_id,
change TYPE cdchngind,
old_data TYPE zoscdo_header,
new_data TYPE zoscdo_header,
items TYPE tt_item_data,
END OF t_header_data .
TYPES:
tt_header_data TYPE SORTED TABLE OF t_header_data
WITH UNIQUE KEY company_code header_id.
Dies ist im Grunde eine In-Memory-Darstellung des Headers mit den zugehörigen Elementen. Die Klasse verfügt über ein
globales Attribut GT_CHANGE_DATA TYPE TT_HEADER_DATA
, das zur Verfolgung der Änderungen verwendet wird. Dieses
Attribut wird mithilfe der folgenden zwei Methoden befüllt:
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_OSCDO_CHANGE_DOC_WRITER->RECORD_HEADER_STATE
* +-------------------------------------------------------------------------------------------------+
* | [--->] IR_HEADER TYPE REF TO ZCL_OSCDO_HEADER
* | [--->] I_CHANGE TYPE CDCHNGIND(optional)
* | [--->] I_OLD_STATE TYPE ABAP_BOOL (default =ABAP_FALSE)
* | [--->] I_RECORD_CONTENTS TYPE ABAP_BOOL (default =ABAP_TRUE)
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD record_header_state.
FIELD-SYMBOLS: <ls_header> TYPE t_header_data.
DATA: ls_header TYPE t_header_data.
* try to locate the corresponding record in the global table
READ TABLE gt_change_data ASSIGNING <ls_header>
WITH TABLE KEY company_code = ir_header->get_company_code( )
header_id = ir_header->get_header_id( ).
IF sy-subrc <> 0.
* no record found - create a new one
ls_header-company_code = ir_header->get_company_code( ).
ls_header-header_id = ir_header->get_header_id( ).
INSERT ls_header INTO TABLE gt_change_data ASSIGNING <ls_header>.
ENDIF.
* When recording the 'old' state, we don't want to set the change indicator yet.
* Therefore only set the change indicator if it is supplied.
IF i_change IS SUPPLIED.
<ls_header>-change = i_change.
ENDIF.
* If required, record the current state, either as 'new' or as 'old'.
IF i_record_contents = abap_true.
IF i_old_state = abap_true.
ir_header->record_cdo_data( CHANGING cs_data = <ls_header>-old_data ).
ELSE.
ir_header->record_cdo_data( CHANGING cs_data = <ls_header>-new_data ).
ENDIF.
ENDIF.
ENDMETHOD. "record_header_state
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_OSCDO_CHANGE_DOC_WRITER->RECORD_ITEM_STATE
* +-------------------------------------------------------------------------------------------------+
* | [--->] IR_ITEM TYPE REF TO ZCL_OSCDO_ITEM
* | [--->] I_CHANGE TYPE CDCHNGIND(optional)
* | [--->] I_OLD_STATE TYPE ABAP_BOOL (default =ABAP_FALSE)
* | [--->] I_RECORD_CONTENTS TYPE ABAP_BOOL (default =ABAP_TRUE)
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD record_item_state.
FIELD-SYMBOLS: <ls_header> TYPE t_header_data,
<ls_item> TYPE t_item_data.
DATA: ls_header TYPE t_header_data,
ls_item TYPE t_item_data.
* try to locate the corresponding record in the global header table
READ TABLE gt_change_data ASSIGNING <ls_header>
WITH TABLE KEY company_code = ir_item->get_company_code( )
header_id = ir_item->get_header_id( ).
IF sy-subrc <> 0.
* no header record found - create a new one
ls_header-company_code = ir_item->get_company_code( ).
ls_header-header_id = ir_item->get_header_id( ).
INSERT ls_header INTO TABLE gt_change_data ASSIGNING <ls_header>.
ENDIF.
* try to locate the corresponding record in the subordinate item table
READ TABLE <ls_header>-items ASSIGNING <ls_item>
WITH TABLE KEY item_number = ir_item->get_item_number( ).
IF sy-subrc <> 0.
* no item record found - create a new one
ls_item-item_number = ir_item->get_item_number( ).
INSERT ls_item INTO TABLE <ls_header>-items ASSIGNING <ls_item>.
ENDIF.
* When recording the 'old' state, we don't want to set the change indicator yet.
* Therefore only set the change indicator if it is supplied.
IF i_change IS SUPPLIED.
<ls_item>-change = i_change.
ENDIF.
* If required, record the current state, either as 'new' or as 'old'.
IF i_record_contents = abap_true.
IF i_old_state = abap_true.
ir_item->record_cdo_data( CHANGING cs_data = <ls_item>-old_data ).
ELSE.
ir_item->record_cdo_data( CHANGING cs_data = <ls_item>-new_data ).
ENDIF.
ENDIF.
ENDMETHOD. "record_item_state
Die Methoden RECORD_CDO_DATA
sind manuelle Ergänzungen zu den persistenten Klassen, die lediglich eine Struktur mit
den Werten der Attribute füllen der Attribute füllen. Diese Methoden sind eher trivial und werden daher hier nicht
angezeigt. Wenn Sie keine Methoden zu den persistenten Klassen hinzufügen möchten, können Sie diese Logik auch zur
Klasse ZCL_OSCDO_CHANGE_DOC_WRITER
hinzufügen.
Um die Datenstruktur zur Laufzeit zu füllen, müssen wir einige Ereignisse verarbeiten, nämlich
IF_OS_FACTORY~TO_BE_DELETED
und IF_OS_FACTORY~LOADED_WITH_STATE
. Letzteres wird verwendet, um den „alten“ Status des
Objekts zu speichern, bevor Änderungen vorgenommen wurden. Da das Ereignis für den Kopf und die Elemente identisch ist,
werden die Methoden einfach in die entsprechende Referenzvariable umgewandelt und an eine der oben eingeführten Methoden
weitergeleitet.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_OSCDO_CHANGE_DOC_WRITER->ON_LOADED_WITH_STATE
* +-------------------------------------------------------------------------------------------------+
* | [--->] OBJECT LIKE
* | [--->] WRITE_ACCESS LIKE
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD on_loaded_with_state.
DATA: lr_header TYPE REF TO zcl_oscdo_header,
lr_item TYPE REF TO zcl_oscdo_item.
CASE cl_abap_classdescr=>get_class_name( object ).
WHEN co_class_name_header. " TYPE abap_abstypename VALUE '\\CLASS=ZCL_OSCDO_HEADER'
lr_header ?= object.
record_header_state( ir_header = lr_header
i_old_state = abap_true ).
WHEN co_class_name_item. " TYPE abap_abstypename VALUE '\\CLASS=ZCL_OSCDO_ITEM'
lr_item ?= object.
record_item_state( ir_item = lr_item
i_old_state = abap_true ).
ENDCASE.
ENDMETHOD. "on_loaded_with_state
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_OSCDO_CHANGE_DOC_WRITER->ON_TO_BE_DELETED
* +-------------------------------------------------------------------------------------------------+
* | [--->] OBJECT LIKE
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD on_to_be_deleted.
DATA: lr_header TYPE REF TO zcl_oscdo_header,
lr_item TYPE REF TO zcl_oscdo_item.
CASE cl_abap_classdescr=>get_class_name( object ).
WHEN co_class_name_header. " TYPE abap_abstypename VALUE '\CLASS=ZCL_OSCDO_HEADER'
lr_header ?= object.
record_header_state( ir_header = lr_header
i_change = 'D'
i_record_contents = abap_false ).
WHEN co_class_name_item. " TYPE abap_abstypename VALUE '\CLASS=ZCL_OSCDO_ITEM'
lr_item ?= object.
record_item_state( ir_item = lr_item
i_change = 'D'
i_record_contents = abap_false ).
ENDCASE.
ENDMETHOD. "on_to_be_deleted
Denken Sie bei der Definition der Konstanten für den Klassennamen an das Präfix \CLASS=
!
Mit diesen Methoden kann der Verfasser den unveränderten Zustand eines Objekts nach dem Laden aufzeichnen und es als gelöscht markieren. Wir benötigen noch eine Möglichkeit, neu erstellte Objekte zu verfolgen, die geänderten Objekte zu identifizieren und die entsprechenden neuen/alten Strukturen für die zukünftige Verwendung zu speichern. Dies kann mithilfe der grundlegenden Agentenmethoden erfolgen:
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_OSCDO_CHANGE_DOC_WRITER->COMPLETE_CHANGE_LIST
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD complete_change_list.
FIELD-SYMBOLS: <lr_object> TYPE REF TO object.
DATA: lt_objects TYPE ostyp_ref_tab,
lr_header TYPE REF TO zcl_oscdo_header,
lr_item TYPE REF TO zcl_oscdo_item.
* add the newly created headers to the change list
lt_objects = zca_oscdo_header=>agent->if_os_ca_instance~get_created( ).
LOOP AT lt_objects ASSIGNING <lr_object>.
lr_header ?= <lr_object>.
record_header_state( ir_header = lr_header
i_change = 'I' ).
ENDLOOP.
* add the changed headers to the change list
lt_objects = zca_oscdo_header=>agent->if_os_ca_instance~get_changed( ).
LOOP AT lt_objects ASSIGNING <lr_object>.
lr_header ?= <lr_object>.
record_header_state( ir_header = lr_header
i_change = 'U' ).
ENDLOOP.
* add the newly created items to the change list
lt_objects = zca_oscdo_item=>agent->if_os_ca_instance~get_created( ).
LOOP AT lt_objects ASSIGNING <lr_object>.
lr_item ?= <lr_object>.
record_item_state( ir_item = lr_item
i_change = 'I' ).
ENDLOOP.
* add the changed items to the change list
lt_objects = zca_oscdo_item=>agent->if_os_ca_instance~get_changed( ).
LOOP AT lt_objects ASSIGNING <lr_object>.
lr_item ?= <lr_object>.
record_item_state( ir_item = lr_item
i_change = 'U' ).
ENDLOOP.
ENDMETHOD. "complete_change_list
Unter der Annahme, dass der erfasste Zustand vollständig ist, ist es jetzt nur noch eine Frage der einfachen Datenmanipulation, die Änderungsdokumente zu schreiben:
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_OSCDO_CHANGE_DOC_WRITER->SAVE_CHANGE_DOCUMENTS
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD save_change_documents.
DATA: lr_persistency_manager TYPE REF TO if_os_persistency_manager,
l_user TYPE syuname,
l_tcode TYPE sytcode,
l_object_id TYPE cdobjectv,
l_item_change TYPE cdchngind,
l_object_change TYPE cdchngind,
lt_new_items TYPE TABLE OF yzoscdo_item,
lt_old_items TYPE TABLE OF yzoscdo_item.
FIELD-SYMBOLS: <ls_change> TYPE t_header_data,
<ls_item> TYPE t_item_data,
<ls_item_change> TYPE yzoscdo_item.
lr_persistency_manager = cl_os_persistency_manager=>get_persistency_manager( ).
l_user = cl_abap_syst=>get_user_name( ).
l_tcode = cl_abap_syst=>get_transaction_code( ).
LOOP AT gt_change_data ASSIGNING <ls_change>.
FREE: l_object_id, l_item_change, lt_new_items, lt_old_items.
* determine the object ID - this refers to the header, so it's MANDT BUKRS HEADER_ID
l_object_id(3) = cl_abap_syst=>get_client( ).
l_object_id+3(4) = <ls_change>-company_code.
l_object_id+7(32) = <ls_change>-header_id.
* assemble the item change data
IF <ls_change>-items IS NOT INITIAL.
l_item_change = 'U'.
LOOP AT <ls_change>-items ASSIGNING <ls_item>.
CASE <ls_item>-change.
WHEN 'I'.
APPEND INITIAL LINE TO lt_new_items ASSIGNING <ls_item_change>.
MOVE-CORRESPONDING <ls_item>-new_data TO <ls_item_change>.
<ls_item_change>-kz = 'I'.
WHEN 'U'.
APPEND INITIAL LINE TO lt_old_items ASSIGNING <ls_item_change>.
MOVE-CORRESPONDING <ls_item>-old_data TO <ls_item_change>.
<ls_item_change>-kz = 'U'.
APPEND INITIAL LINE TO lt_new_items ASSIGNING <ls_item_change>.
MOVE-CORRESPONDING <ls_item>-new_data TO <ls_item_change>.
<ls_item_change>-kz = 'U'.
WHEN 'D'.
APPEND INITIAL LINE TO lt_old_items ASSIGNING <ls_item_change>.
MOVE-CORRESPONDING <ls_item>-old_data TO <ls_item_change>.
<ls_item_change>-kz = 'D'.
ENDCASE.
ENDLOOP.
ENDIF.
* determine the overall change indicator
IF <ls_change>-change IS NOT INITIAL.
l_object_change = <ls_change>-change.
ELSEIF l_item_change IS NOT INITIAL.
l_object_id = 'U'.
ENDIF.
* update mode or direct call?
IF lr_persistency_manager->get_update_mode( ) = oscon_dmode_direct.
CALL FUNCTION 'ZOSCDO_WRITE_DOCUMENT'
EXPORTING
objectid = l_object_id
tcode = l_tcode
utime = sy-uzeit
udate = sy-datum
username = l_user
object_change_indicator = l_object_change
n_zoscdo_header = <ls_change>-new_data
o_zoscdo_header = <ls_change>-old_data
upd_zoscdo_header = <ls_change>-change
upd_zoscdo_item = l_item_change
TABLES
xzoscdo_item = lt_new_items
yzoscdo_item = lt_old_items.
ELSE.
CALL FUNCTION 'ZOSCDO_WRITE_DOCUMENT' IN UPDATE TASK
EXPORTING
objectid = l_object_id
tcode = l_tcode
utime = sy-uzeit
udate = sy-datum
username = l_user
object_change_indicator = l_object_change
n_zoscdo_header = <ls_change>-new_data
o_zoscdo_header = <ls_change>-old_data
upd_zoscdo_header = <ls_change>-change
upd_zoscdo_item = l_item_change
TABLES
xzoscdo_item = lt_new_items
yzoscdo_item = lt_old_items.
ENDIF.
ENDLOOP.
ENDMETHOD. "save_change_documents
Der schwierige Teil der Implementierung bestand darin, einen Weg zu finden, den geänderten Status unmittelbar vor dem
Speichern abzurufen und den generierten Funktionsbaustein zum richtigen Zeitpunkt auszulösen. Zu diesem Zweck habe ich
mich entschieden, den Änderungsdokumenten-Ersteller als sogenannten Save Handler zu registrieren. Die Klasse muss
hierfür die Schnittstelle IF_OS_CA_SERVICE
implementieren, aber mit zwei Ausnahmen ist keine der Methoden
erforderlich. Um einen versehentlichen Aufruf zu verhindern, habe ich einfach die Anweisung
RAISE EXCEPTION TYPE cx_os_no_implementation.
in jeder von der Schnittstelle geforderten Methode hinterlegt, mit Ausnahme der folgenden zwei Methoden.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_OSCDO_CHANGE_DOC_WRITER->IF_OS_CA_SERVICE~PREPARE_FOR_TOP_TRANSACTION
* +-------------------------------------------------------------------------------------------------+
* | [--->] I_INVALIDATE TYPE OS_BOOLEAN
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD if_os_ca_service~prepare_for_top_transaction.
FIELD-SYMBOLS: <lr_agent> TYPE REF TO if_os_ca_service .
* a new transaction is started - forget all previously stored change data
FREE gt_change_data.
* defer the actual method call to the wrapped agents
LOOP AT gt_agents ASSIGNING <lr_agent>.
<lr_agent>->prepare_for_top_transaction( i_invalidate ).
ENDLOOP.
ENDMETHOD. "if_os_ca_service~prepare_for_top_transaction
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_OSCDO_CHANGE_DOC_WRITER->IF_OS_CA_SERVICE~SAVE
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD if_os_ca_service~save.
FIELD-SYMBOLS: <lr_agent> TYPE REF TO if_os_ca_service .
* defer the actual method call to the wrapped agents
LOOP AT gt_agents ASSIGNING <lr_agent>.
<lr_agent>->save( ).
ENDLOOP.
* then complete the list of changed objects and write the change documents
complete_change_list( ).
save_change_documents( ).
ENDMETHOD. "if_os_ca_service~save
Wie Sie sehen, ist die Implementierung ziemlich einfach: Wir überlassen die komplexen Dinge den generierten Agenten (die
wir in nicht allzu ferner Zukunft in einer Tabelle namens GT_AGENTS
speichern werden) und fügen einfach unsere
Verarbeitungslogik hinzu.
Da wir nun die grundlegenden Strukturen für die Datenumschlüsselung eingerichtet haben, können wir damit beginnen, den
Änderungsdokumenten-Writer mit den Objektdiensten zu verbinden. Der Writer muss als Ereignisbehandler registriert
werden, und es wäre unklug, mehrere Instanzen zu registrieren. Aus diesem Grund habe ich es
als einfaches Singleton entworfen: Die Klasse ist als CREATE PRIVATE
gekennzeichnet und hat ein statisches Attribut
SR_INSTANCE TYPE REF TO ZCL_OSCDO_CHANGE_DOC_WRITER
sowie statische Initialisierungsmethode wie diese:
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method ZCL_OSCDO_CHANGE_DOC_WRITER=>INITIALIZE
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD initialize.
IF sr_instance IS NOT INITIAL.
CREATE OBJECT sr_instance.
sr_instance->register_handlers_for_agent( zca_oscdo_header=>agent ).
sr_instance->register_handlers_for_agent( zca_oscdo_item=>agent ).
ENDIF.
ENDMETHOD. "initialize
Während der Initialisierung werden die Ereignisbehandlungsmethoden und der Speicher-Handler mit der folgenden Methode registriert:
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_OSCDO_CHANGE_DOC_WRITER->REGISTER_HANDLERS_FOR_AGENT
* +-------------------------------------------------------------------------------------------------+
* | [--->] IR_AGENT TYPE REF TO CL_OS_CA_COMMON
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD register_handlers_for_agent.
DATA: lr_persistency_manager TYPE REF TO if_os_persistency_manager.
APPEND ir_agent TO gt_agents. " TYPE TABLE OF REF TO if_os_ca_service .
lr_persistency_manager = cl_os_persistency_manager=>get_persistency_manager( ).
lr_persistency_manager->register_save_manager_for_ca( i_save_manager = me
i_class_agent = ir_agent ).
SET HANDLER on_loaded_with_state FOR ir_agent.
SET HANDLER on_to_be_deleted FOR ir_agent.
ENDMETHOD. "register_handlers_for_agent
Nach Fertigstellung der Initialisierungsmethoden genügt ein einfacher Aufruf von
zcl_oscdo_change_doc_writer=>initialize( ).
während der Initialisierung der Anwendung, um die Aufzeichnung von Änderungsdokumenten zu starten. In diesem Artikel wird nur das grundlegende Verfahren beschrieben, aber von hier an sollte es viel einfacher sein, der Implementierung Funktionen hinzuzufügen.