org.apache.commons.io.IOUtils cannot be found when task runs as Job

Given the following code:

var processDefinitionId = execution.getProcessDefinitionId();

var deploymentId = execution.getProcessEngineServices().getRepositoryService().getProcessDefinition(processDefinitionId).getDeploymentId();

var resource = execution.getProcessEngineServices().getRepositoryService().getResourceAsStream(deploymentId, 'content.txt');

var IOUtils = Java.type("org.apache.commons.io.IOUtils");
var String = Java.type("java.lang.String");

var content = new String(IOUtils.toByteArray(resource), 'UTF-8');

If you run this as a sync process, Camunda is able to load the IOUtils lib. But if you run this as a aSync/job, the IOUtils library cannot be found.

@thorben can you explain the difference between the initial sync transaction and asyc jobs in terms of libraries that are available?

Hi @StephenOTT,

is that code of a delegate?

Cheers,
Askar

@aakhmerov it is running as a inline script at the moment in the http-connector input parameterspayload` input

@aakhmerov here is a sample file that shows the exact error occurring

sampleError.bpmn (5.2 KB)

change the script task from Sync to Async, etc. When it is on Async it will fail because it cannot find the IOUtils library

note: make sure to upload a second file with your deployment called “content.txt”

Also tested this with:

var processDefinitionId = execution.getProcessDefinitionId();

var deploymentId = execution.getProcessEngineServices().getRepositoryService().getProcessDefinition(processDefinitionId).getDeploymentId();

var resource = execution.getProcessEngineServices().getRepositoryService().getResourceAsStream(deploymentId, 'content.txt');

var commonsIo = new JavaImporter(org.apache.commons.io.IOUtils);

with (commonsIo) {

   var String = Java.type("java.lang.String");

   var content = new String(IOUtils.toByteArray(resource), 'UTF-8');

};

and same issue

Hey Stephen,

Where is IOUtils located in your setup (i.e. part of application/server, etc)?

Cheers,
Thorben

@thorben this was tested using the default camunda docker setup for tomcat:7.6

Could you try to find out which JAR exactly brings that class? I’m not familiar with the docker image and how you deploy your process to it.

The process is deployed using the Camunda Rest API.

My understanding is that its the commons-io:

@thorben @aakhmerov where you able to reproduce?

Hi Stephen,

The commons-io dependency you listed is in test scope. It is also not part of any of the shared libraries in the Camunda Tomcat distribution. Are you sure this class is actually available?

Cheers,
Thorben

Hi @StephenOTT,

The reason it is working synchronously when you start it through the rest api is that commons-io is part of the engine-rest.war. It is a transitive dependency of the resteasy lib being used as JAX-RS provider on Tomcat. You will find it inside the WEB-INF/lib folder.
Executing an async task doesn’t have the engine-rest libs on classpath, only TOMCAT_HOME/lib, cos the JobExecutor is ran by the engine.
In contrast, sync execution will be done in context of engine-rest.war, where the commons-io lib is available.

Cheers,
Christian

2 Likes

@hawky4s @thorben, is there a way to access IOUtils in the JobExecutor context? Or is there another Jar that is available in the jobExecutor and the engine-rest.war that could be used for the same goal as IOUtils?

You can put IOUtils into Tomcat’s lib folder.

@thorben thats my fallback :slight_smile: Just checking if there is anything else in the default camunda deployment.

Hi @StephenOTT,

Just check what’s available in TOMCAT/lib :wink:
That’s the source of truth for the JobExecutor if it isn’t ran inside a ProcessApplication context.

Cheers,
Christian

The solution that comes to mind is to use Groovy’s InputStream .getText() method: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/InputStream.html#getText().

so something like this can be used to get the resource as text:

def processDefinitionId = execution.getProcessDefinitionId()
def deploymentId = execution.getProcessEngineServices().getRepositoryService().getProcessDefinition(processDefinitionId).getDeploymentId()

def resource = execution.getProcessEngineServices().getRepositoryService().getResourceAsStream(deploymentId, 'content.txt')

def resourceText = resource.getText('UTF-8')

in my use case, JS is prefered so adding IOUtils is preferred

There are also other options you can explore, like implementing stream to byte array conversion yourself in Javascript and loading it from the process deployment, or similar.

With plain Java, the shortest way to convert appears to be

InputStream stream = ...;
Scanner scanner = new Scanner(stream, "UTF-8");
String value = scanner.useDelimiter("\\Z").next();
scanner.close();
1 Like

@thorben NICE CATCH! I started to look at scanner but had never tried a implementation.

Working code:

var Scanner= Java.type("java.util.Scanner");

scannerResource = new Scanner(resource, "UTF-8");
var content = scannerResource.useDelimiter("\\Z").next();
scannerResource.close();

So complete working code looks like:

var processDefinitionId = execution.getProcessDefinitionId();
var deploymentId = execution.getProcessEngineServices().getRepositoryService().getProcessDefinition(processDefinitionId).getDeploymentId();
var resource = execution.getProcessEngineServices().getRepositoryService().getResourceAsStream(deploymentId, 'content.txt');

var Scanner= Java.type("java.util.Scanner");
scannerResource = new Scanner(resource, "UTF-8");

var content = scannerResource.useDelimiter("\\Z").next();

scannerResource.close();

Tested with Sync and Async and it works.

@thorben @hawky4s thanks!

1 Like