How to properly delete bytea variable from process?

The problem - trying to handle the process of storing base64 encoded variables.
My investigation led to the point of act_ge_bytearray, where I see the variable stored as byte array.

By means of @EventListener I’ve basically done two things:

  1. delegateTask.deleteVariable(myFileVariable)
  2. delegateTask.setVariable(myFileVariableReplacement), which only holds an UUID to the properly stored file.

Everything seemed fine, but then I realized that the original variable still ends up in that table with the byte array.

What am I doing wrong?

I think you need to process the variable into your external storage BEFORE it gets to Camunda.

Something like (note: build this in your programming language of choice, NOT in Camunda)

1 Like

@GotnOGuts as always, thanks for being that person who responds when other people keep silent :slight_smile:

Going with storing the file(s) before Camunda is the main problem - “people wanted” to have the files submitted (and by magic) processed by Camunda. This is basically “part 2” of the topic, where I was looking for a way of proper file submission. I do realize all the majority of problems sending files as regular variables poses, but “not my decision to make, sadly”.

I was hoping that by editing the variable BEFORE it gets submitted (as was suggested by this fine person here - Listener for form submit - #12 by Draakzward), the original variable doesn’t get persisted, so all should be fine. Turns out that place is “too late”.

Alternatively, I thought of “smashing in” my own FormServiceWithBlackJackAndLadies, but that approach, although possible, bricks the DI logic creating cross links and thus crashing the app. Also need to spend time trying to reproduce the precise core Camunda config (can’t just @Bean the FormServiceImpl2). So that’s currently out of the scope of possibilities.

And I don’t see a way of actually removing that variable. Not without doing a native query, but I already sense that I will be reminded of my sins somewhere in the future.

Mark the task as “Async Before” which will force the DB write before the task.
The follow the instructions in Listener for form submit - #14 by jonathan.lukas

I can’t say I fully understand it, but it’s basically saying to intentionally make the DB transaction fail, so it gets rolled back, and then update it with just those parts that you want updated.

I missed that this was in the C7 area, so there’s a few extra tricks that you can pull compared to C8.

I already feel a headache and “this maneuver will cost us 3 thousand nervous cells” in the future. Also this already has a “haha, no”, where I’m “writing a reusable solution”, which should dispatch automatically and not have special additions to the bpmn.

But even so, if I mark a transaction to rollback (yes, async will use a different transaction, and technically the model will jump to the next step), it’s a “Flintstones’s car break with bare feet” approach - brutal, but possible.

The main problem here is that I’m trying to mop the floor after a crowd of children had run, instead of doing the needed work on the start, the FormService.submitTask(taskID, submittedVariableMap).

There was a suggestion to slap a Filter and intersect the POST request, but that’s request.getReader and I don’t want to shoot my leg with a horse…

Going further into the logic made me believe that attempting to do a DIRTY_READ on the DB, altering the record before it gets persisted is my best option (if I’ll manage to do so).

If I try to slip a new FormService, which I intent to use with other beans, is a recursive dependency. I can go with attempting to replace all the autowires with delegate.getProcessEngine, basically unplugging DI, but that’s cancer.

If I try to set a FormService to Camunda’s root config, I realize that the original FormService was shared between Camunda’s internals long before @Configuration -> @PostConstruct kicks in.

If I try to go deeper and find when RestResource (or what was it) gets initiated (that one is instantiated via constructor for every request), I struck the wall with something grotesque like slipping in a modified .class in the ClassLoader (I have done stupid things, but this one is asking for the DumbThingsI’veDoneOnAProject award). And anything in between that object is fixed for ages by ducktape.

That’s basically the solution proposed and accepted in the thread that you linked to. I’m not sure there’s a better way, since you need to block the “SetVariable” (containing the file) from hitting the DB.

That’s the “I gave up” solution, since I clearly saw that nothing more would be retrieved from that topic. Also it’s the “Camunda specialist, person, who has superior knowledge of Camunda to what I know” provided options.

I see two, and both pull more change:

  1. Custom submit-form endpoint. Good, but “good bye default localhost → tasklist → testing the model flow”.
  2. Attempt to update the DB entry, wiping the original variable bytea value. But this involves dirty read (I’m not even sure if it will work, just a thought). I’m assuming that @EventListener(status == complete) and the fact that I’m getting the variable from delegate.getVariable means that the actual write (and currently in the air) to that table has been made, so my hopes are that I can get my hands on that non-persisted db entry by executing a native query (I wonder how many things will I brake though…).

If you want to get the file from delegate.getVariable, then it has to be written to the DB (in the bytea table)
Even updating the value will still leave it in the variable History table.

Putting a filter in the UserTask Complete (much like your option 1), could allow you to intercept the file as it’s being sent in, and do something else with it, and potentially rewrite the “variables” array before the engine actually processes it.

@GotnOGuts, can you please share how that would look like?

Because I assumed that this is what I’m doing right now:

@EventListener (condition = "#delegateTask.eventName == 'complete'")
	@Order (1)
	public void processFormSubmit(DelegateTask delegateTask) {

Well, at least I found a list of SQL operations done for a regular UserTask step… (gonna go and shoot myself now)

Really don’t want to undo these operations every time I’m trying to delete a file based variable.

@GotnOGuts I’ve struck an interesting point of code while I was debugging the upper screen

/**
 * <p>A command interceptor to catch {@link ProcessEngineException} errors and assign error codes.
 *
 * <p>The interceptor assigns an error code to the {@link ProcessEngineException}
 * based on the built-in or custom {@link ExceptionCodeProvider}.
 */
public class ExceptionCodeInterceptor extends CommandInterceptor {

Any ideas how to squeeze in something here? (a new interceptor)

Can you please elaborate a bit on what you are doing? Why does the variable exist in the first place if you don’t want it to be stored?

Can you share a test case/code that reproduces the problem?

edit: btw I didn’t read through the entire thread, please disregard my comment if this has been covered

@thorben

Why does the variable exist in the first place if you don’t want it to be stored?

As a user, I want to submit base64 encoded files using /task/.../submit-form.

And as a sad BE person, I want to capture submitted variables with a specific structure, and instead of persisting that data in the db, I want to process that variable differently - get it’s content, store as file, replace the value of that variable with a graceful solution. (And I am fully aware that even the fact of submitting the content of a file this way is BAD. But, as always, some bright decisions are left outside of my reach).

edit: btw I didn’t read through the entire thread, please disregard my comment if this has been covered

I do understand that this is becoming hard to read from the beginning. Long story short described in the previous sentences :slight_smile:

Can you share a test case/code that reproduces the problem?

For the test code. HM.

I think this should be in order - File upload questions - #5 by GotnOGuts
And from the BE side - try to capture it (I used @EventListener to try and replace the variable while it’s still in transaction).

Have you considered submitting the file as a transient variable? Those will not be persisted and are only available during the transaction that submits it. So you don’t have to attempt deleting the variable at all.

The request body would look something like this:

{
    "variables": {
        "field1":{
                "type": "File",
                "valueInfo": {
                        "filename": "pic.png",
                        "mimeType": "image/png"
                        "transient": true
                },
                "value": "iVBORw0KGgoAAAANSUhEUgAABTAAAAJQCAYAAAC5Pku2AAAAAXNSR0IArs4c6QAAIABJREFUeF7s3Qm8TeX7//8rocxEkjmSimRsIqIoKT5IRSIlQ6XJECmlNItSCJUMiTIUUSKJSoWiKInMKmTI0Mz/8V7f39n/"
            }
    }
}

You can find the docs here: Camunda Platform REST API (check in the request body description under variables => property name* => valueInfo).

edit: corrected the payload JSON structure

 "transient": true

@thorben where have you been all my life? :smiley:

I did, however, found the right combo to do what I wanted…
image

Not sure I understand your last sentence. Soooo, problem solved? :slight_smile:

1 Like

More or less. Need to see where it leads me.
Thank you for the moment :slight_smile:

1 Like

@thorben sorry, it took me a while to return to this question.

Finally got an opportunity to test it, and it doesn’t seem to work.

Here is the structure:

{
  "variables": {
    "myVar": {
      "value": {
        "content": "[base64 content here]",
        "transient": true,
        "type": "File",
        "valueInfo": {
          "filename": "file.png",
          "mimeType": "image/png"
        }
      }
    }
  }
}

UPD: as Murphy’s law states - “If something stupid is bound to happen, it will happen” (with me being completely blind to the fact that valueInfo should be in the root of variable submission).
On the other side, I realized that valueInfo should be sitting on the same level as variables.MYVAR.value

https://docs.camunda.org/rest/camunda-bpm-platform/7.19/#tag/Execution/operation/getLocalExecutionVariable

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.