cancel
Showing results for 
Search instead for 
Did you mean: 

Problem in handling ABAP class instances in workflow?

former_member185998
Active Participant
0 Kudos

I have an issue using ABAP classes in workflow. I just can't see why some attributes lose their values.

The workflow handles an invoice. A custom ABAP class represents the invoice in the workflow container.

 

I pass the invoice object from the wf to the task in the binding. In the task container, I have a container element CO_INVOICE that matches the method's CHANGING parameter CO_INVOICE. The method does something, then sets the invoice object's public STATUS attribute to PARKED. When I look at the results from wf log, I see the object's STATUS attribute is initial. When I test the method directly in SE80, without using the wf, the status is correctly set to PARKED. It looks as if wf loses the attribute's value. However, the key attributes (invoice number, fiscal year) are correct.

We're running 7.02.

Accepted Solutions (1)

Accepted Solutions (1)

former_member185998
Active Participant

One thing I noticed was that when I view the contents of the container elements in the workflow log, the objects get reinstantiated. That way, the log does not necessarily correspond to the runtime values. However, I find that I can't rely on the instance attribute values in the workflow, because the workflow only stores the LPOR to the container, and any other attributes are transitory.

Former Member
0 Kudos

Yes that is what happens when you use the workflow log. If you do not have persistance, the values you see in the container are the ones initialized by the WF runtime. The methods in IF_WORKFLOW are not only meant for initializing values but also for retrieving existing values from the persistance layer.

former_member185998
Active Participant
0 Kudos

Well, I guess it's case closed then.

So sum it up, I can't pass values from the method to the workflow using object attributes, unless I implement persistent objects that explicitly save the values in the database. If I just pass the object reference (of a transient object), the object might get reinstantiated, so any attribute values would be lost.

OK, it figures. The wf engine creates and discards objects at its own pace, and only the LPOR is actually stored in the container. I was hoping I could pass the object like you'd pass an object to a method in a normal ABAP program. In ABAP, you'd be pretty disappointed if attribute values were lost while the program is running. But in wf, that's life...

Jocelyn_Dart
Product and Topic Expert
Product and Topic Expert
0 Kudos

It's actually not just "that's life" it's about the performance and intention of workflow. Remember that workflow is most of the time acting asynchronously so that its actual runtime and hence load on the system is very very small.  That would not be the case if you were passing large quantities of data around and hanging on to them all the time.  So any stored data is meant to be stored in the database and the ABAP Class for workflow is used to access that data only as and when needed. Remember also most ABAP programs run for a comparatively short period of time - generally seconds or minutes.  Whereas workflows can run for hours, days, weeks, years... and there's a lot of time when all it would be doing is tying up dialog or background processes waiting for someone to go to their inbox if it didn't take this approach to the persisistency of objects.

Hope that helps

Regards,

Jocelyn

MKM
Active Participant
0 Kudos

Hi Jocelyn,

I have an below issue.

How to know Called workflow name at runtime from Class Method

Can it be resolved by using these methods ?

Thanks,

Manoj

keohanster
Active Contributor
0 Kudos

Hi Manoj,

Please continue searching, and if you cannot find an answer, open a new discussion thread.  This thread is almost 4 years old (and should be closed by the OP).

Good luck,

Sue

former_member185998
Active Participant
0 Kudos

Hi Susan,

I've marked Samuli's answer as a correct one, so my question is marked as answered. Is there something more I can do to close the thread?

BR,

Arto

Answers (2)

Answers (2)

Jocelyn_Dart
Product and Topic Expert
Product and Topic Expert
0 Kudos

Hi Arto, Just looking at your code, including find_by_lpor inside your get_instance method is not recommended.  A better approach would just be to use a CREATE OBJECT statement at that point instead. Or if you want to have some reuse of code, have a private method that is called from both your FIND_BY_LPOR and GET_INSTANCE methods. 

There's a certain point when it can all get rather confused if you have too many cyclical calls.

It's best to leave the find_by_lpor and lpor methods for direct use by the workflow system and not make your own additional methods dependent on them.

I wrote a series of blogs on this back in 2006 that are still good - if you follow the approaches recommended you should have fewer issues. Here's a couple of them to check against.

Regards

Jocelyn

former_member185998
Active Participant
0 Kudos

Jocelyn, Samuli, Anjan,

thank you for being active!

I'm sure I'll find your tips at least helpful. I had to do some  shortcuts for now and get on with something else for a while, but I sure want to explore properly as soon as possible, and will reply you with the results I got.

Regards,

Arto

former_member185998
Active Participant
0 Kudos

Hi Jocelyn,

sure, I've read, bookmarked and re-read your blogs. They're true gems

Still, it somehow escaped me that I shouldn't use any IF_WORKFLOW methods in my own code. I thought "call them directly" referred to using them in tasks.

Anyway, I now replaced the call with a direct CREATE OBJECT, but it didn't help retaining the attribute values.

Perhaps the only way to keep those attribute values all through the call stack is indeed using persistent objects. In this case, I just added simple character type parameters to the interfaces and use them in the bindings. It does the trick, but to me, it's not as elegant as using objects. 

former_member185998
Active Participant
0 Kudos

Hi Jocelyn,

I just noticed that ch.11.4.4, page 382 of Practical Workflow for SAP, 2nd edition, says "It makes perfect sense to call FIND_BY_LPOR from GET INSTANCE". and shows an example of such coding.

Jocelyn_Dart
Product and Topic Expert
Product and Topic Expert
0 Kudos

Not my chapter sorry 😉  and not my experience.  I prefer not to have circular references... and it's at least worth trying a different approach if the one you are using is not working.

anjan_paul
Active Contributor
0 Kudos

Hi,

    Check in the constructor or find by lpor method, this attributes  are not initialized. also share the screenshot of the class.

former_member185998
Active Participant
0 Kudos

Hi,

in the method GET_INSTANCE, setting the attribute is the very last thing the method does before returning the object. So constructors etc. shouldn't get in the way. The code and a picture of the class are below.

The matter seems to be related to static vs. instance methods. GET_INSTANCE has to be static, as it creates the object for the workflow. Subsequent methods, such as CHANGE can be instance methods. I tried both versions, and when using an instance method, the status is retained.

All in all, why are there several identical elements in the task container? Firstly, there's CO_INVOICE, which was automatically created from the method parameters, according to wf engine's suggestion. Secondly, there's _Wi_Object_ID, which is also automatically created. Thirdly, in some tasks there's also the element _WI_RESULT, which refers to the same object class. All three refer to the same class, and the methods I call are similar. Curiously, the _WI_RESULT can't be seen in PFTC, but only when looking at the task container in SWI1 log.

When I want to use an instance method, I have to pass the instance to _Wi_Object_ID in the task, or else I get a null pointer error. However, I must also pass the same instance to CO_INVOICE, so it is transferred to the method.

(I tried making an explicit binding from _Wi_Object_ID in the task to CO_INVOICE in the method, but that wasn't allowed. The error message reads 'The object type ZIF_...' is not (actively) available.).

Point being: what's the purpose of all those task container elements? What's the best way of passing the instance to the method?

METHOD get_instance.

   DATA ls_lpor TYPE sibflpor.

   DATA lv_inst TYPE sibfinstid.

   IF    ( NOT i_document_number IS INITIAL )

     AND ( NOT i_fiscal_year IS INITIAL )

     AND ( NOT i_company_code IS INITIAL ).

     ls_lpor-catid = 'CL'.

     ls_lpor-typeid = '/BILOT/CL_IV_AP_INVOICE_FI'.

     CONCATENATE i_company_code i_document_number i_fiscal_year INTO ls_lpor-instid.

     r_invoice_fi ?= /bilot/cl_iv_ap_invoice_fi=>bi_persistent~find_by_lpor( ls_lpor ).

     r_invoice_fi->/bilot/if_iv_ap_invoice_fi~set_status( 'NEW' ).

   ENDIF.

ENDMETHOD.


anjan_paul
Active Contributor
0 Kudos

Hi, 

  Did you implement IF_WORKFLOW interface in class. To use class in worklfow it is mandatory to use this interface and implement FIND_BY_LPOR and LPOR method.  There are lot of example how to implement it.

former_member185998
Active Participant
0 Kudos

Sure. Otherwise, I wouldn't be able to call the class from the wf. I also implemented instance management by using the static table mst_invoices in find_by_lpor.

anjan_paul
Active Contributor
0 Kudos

Hi,

Can you share the code of FIND_BY_LPOR method, also share screenshot of bindin of workflow and task with method

former_member185998
Active Participant
0 Kudos
former_member185998
Active Participant
0 Kudos

METHOD get_instance.



  DATA ls_lpor TYPE sibflpor.

  DATA lv_inst TYPE sibfinstid.



  IF    ( NOT i_document_number IS INITIAL )

    AND ( NOT i_fiscal_year IS INITIAL )

    AND ( NOT i_company_code IS INITIAL ).

    ls_lpor-catid = 'CL'.

    ls_lpor-typeid = '/BILOT/CL_IV_AP_INVOICE_FI'.

    CONCATENATE i_company_code i_document_number i_fiscal_year INTO ls_lpor-instid.

    r_invoice_fi ?= /bilot/cl_iv_ap_invoice_fi=>bi_persistent~find_by_lpor( ls_lpor ).

    r_invoice_fi->/bilot/if_iv_ap_invoice_fi~set_status( 'NEW' ).

  ENDIF.

ENDMETHOD.

Former Member
0 Kudos

Hi Arto, long time no see

What you are seeing might be a side effect of how workflow instantiates objects. Your container might not get updated even though the method is executing. What is your persistency? Can you check whether the changes have been made to the persistent object? You could place breakpoints in all the affected methods to find out when the object gets instantiated and what methods are being called and when. One other thing is that you should always define attributes as Import & Export if you make changes to them and make the mapping bi-directional between the workflow container and the task.

anjan_paul
Active Contributor
0 Kudos

Hi,

   you shared Get_instance method. Please share FIND_BY_LPOR method

former_member185998
Active Participant
0 Kudos

Hi Samuli,

I agree, I think this is a matter of instantiation and persitance. While debugging, I see that the attributes are set in the Z-code, and the values are still there when I get back to SAP's standard code. However, they never reach the container.

The original object probably gets lost, then the workflow calls FIND_BY_LPOR, which in turn calls the constructor and sets the 3 key attributes (document number, fiscal year, company code). That explains why only those attributes have values, but others lose them.

I'm not using persistent objects, but now it seems I'd have to, in order to keep the attribute values. I think that's a flaw in the wf engine, because it'd seem logical to just pass the same object on in the bindings.

The import & export checkmarks haven't posed me problems before, and I have a feeling they are rather informative than required. But yes, I should use them consistently. Now they are in place, but with no effect. The object ref did get through all the time.

former_member185998
Active Participant
0 Kudos

method BI_PERSISTENT~FIND_BY_LPOR.

  data: lv_invoice_number type belnr_d,

         lv_year type gjahr,

         lv_company_code type bukrs.

*- begin of local data

   DATA: l_invoice_fi   TYPE REF TO /BILOT/CL_IV_AP_INVOICE_FI.

   DATA: l_instance     TYPE t_instance.

   DATA: l_typename     TYPE seoclsname.

*- end   of local data

   CHECK lpor-instid IS NOT INITIAL.

  lv_invoice_number = lpor-instid+4(10).

  lv_year = lpor-instid+14(4).

  lv_company_code = lpor-instid(4).

*- set typename

   IF lpor-typeid IS INITIAL.

     l_typename = '/BILOT/CL_IV_AP_INVOICE_FI'.

   ELSE.

     l_typename = lpor-typeid.

   ENDIF.

*- instid is the key of the object

   READ TABLE mst_instances WITH KEY number = lpor-instid

        INTO l_instance.

   IF sy-subrc <> 0.

     TRY.

CREATE OBJECT L_INVOICE_FI

   EXPORTING

     COMPANY_CODE = lv_company_code

     INVOICE_NUMBER = lv_invoice_number

     FISCAL_YEAR = lv_year

     .

       CATCH cx_bo_error .

*------ object not found

         EXIT.

     ENDTRY.

     l_instance-number   = lpor-instid.

     l_instance-instance = l_invoice_fi.

     APPEND l_instance TO mst_instances.

   ENDIF.

   result = l_instance-instance.

endmethod.

anjan_paul
Active Contributor
0 Kudos

Hi,

codes look ok. Check the bindin between class method and task of the method where status is changed. If STATUS bindin is maintained for task and method.

share screenshot



former_member185998
Active Participant
0 Kudos

Ah, you perhaps meant what type of persistence in the workflow? I'm using XML persistence.

Former Member
0 Kudos

No I meant where are you storing your changes. I'm guessing your persistance is the SAP backend database. See if the changes are made there.