How to get context value from EngineAPI

Hello,
I’m in trouble for getting context value from the EngineAPI.
By default : processApi.getProcessInstanceExecutionContext(MyProcessInstanceId) return a Map<String, Serializable>.

This Map can have mutliple type of object : SimpleBusinessDataReference, MultipleBusinessDataReference, Document, etc.

In my case, when i doing something like that:

Map<Expression, Map<String, Serializable>> expressions = new HashMap<>();
//loop aver all context var
for (Map.Entry<String, Serializable> myContextEntry: context.entrySet()){
 if(myContextEntry instanceof MultipleBusinessDataReference){
ExpressionBuilder builder = new ExpressionBuilder();
builder.createNewInstance(myContextEntry.getKey());

MultipleBusinessDataReference contextElement = (MultipleBusinessDataReference) entry.getValue();
logger.info("MultipleBusinessDataReference Name = " + myContextEntry .getKey() + " Type = " + contextElement.getType());
if (contextElement.getStorageIds().isEmpty()) {
	expression = builder.createBusinessDataExpression(contextElement.getName(), "java.util.Collections$EmptyList");
} else {
	expression = builder.createBusinessDataExpression(contextElement.getName(), "java.util.ArrayList");
}
}
Map<String, Serializable> searchExpression= new HashMap<>();
searchExpression.put(entry.getKey(), entry.getValue());
expressions.put(expression, searchExpression);
..
}
processApi.evaluateExpressionsOnActivityInstance(instance.getId(),expressions).
..

My bonita server throw an OutOfMemory…
Someone have already do somethink like that for getting all context value ?

Regards

Here is an example of code to get your multiple business variable values in your Java client application:

    // Configure the client
    Map<String, String> settings = new HashMap<>();
    settings.put("server.url", "http://localhost:21410");
    settings.put("application.name", "bonita");
    APITypeManager.setAPITypeAndParams(ApiAccessType.HTTP, settings);

    // Instantiate the API client and login
    APIClient apiClient = new APIClient();
    apiClient.login("walter.bates", "bpm");

    // Get a reference to processAPI and used it to get the process instance execution context
    final ProcessAPI processAPI = apiClient.getProcessAPI();
    Map<String, Serializable> processInstanceExecutionContext = processAPI.getProcessInstanceExecutionContext(2L);

    // This will create a zip file with the required dependency for this client library
    try (FileOutputStream fos = new FileOutputStream("/home/antoine/dependencies.zip")) {
        fos.write(apiClient.getTenantAdministrationAPI().getClientBDMZip());
    }

    // Display the content of the context
    for (String key : processInstanceExecutionContext.keySet()) {
        System.out.println(key);
    }


    // From the context get the BusinessDataReference using the name of the business variable + "ref_"
    // Cast the BusinessDataReference to the appropriate subtype: SimpleBusinessDataReference or MultipleBusinessDataReference
    MultipleBusinessDataReference refCars = (MultipleBusinessDataReference) processInstanceExecutionContext.get("cars_ref");

    // Create a list of objects to store our pultiple business variable values
    List<Car> cars = new ArrayList<>();

    // Get a reference to the business object DAO
    CarDAO carDAO = apiClient.getDAO(CarDAO.class);

    // Iterate over the list of storage ids provided by the MultipleBusinessDataReference
    for (Long storageId : refCars.getStorageIds()) {
        System.out.println("Storage id: " + storageId);
        // Use the DAO to get the business object value and add it to the list
        cars.add(carDAO.findByPersistenceId(storageId));
    }

    for (Car car : cars) {
        System.out.println(car.getBrand() + " " + car.getColor());
    }

The big limitation of this solution is the dependency management. In order to run it the class com.company.model.CarDAOImpl is required. And in Community Edition this class is available in a Jar file only available on the server (you can get the Jar content using an API method). I include in my code example the download of the zip file (from the Bonita server) that includes all the required jar file.

Currently I don’t have any easier solution.

If someone want to get all context over EngineAPI:

Business entity

public class BusinessEntity implements Serializable {
    /**
     * <Column name>/<Column value>
     */
    Map<String, Object> data = new HashMap<>();

    public Map<String, Object> getData() {
        return this.data;
    }

    public void setData(Map<String, Object> data) {
        this.data = data;
    }
}	

Function in my generic Repository:

default List<BusinessEntity> find(@NotNull JdbcTemplate jdbcTemplate, @NotNull String tableName, Map<String, Object> parameters) {
        List<BusinessEntity> result = new ArrayList<>();
        jdbcTemplate
                .query("SELECT * FROM public." + tableName + " t " + createWhereClause(parameters),
                        (rs, rowNum) -> result.addAll(getObject(rs, rowNum)));
        logger.debug("Number of entities that have been find : "+result.size());
        return result;
    }

And my code to get my context:

public Map<String, Object> getBonitaContext(long processInstanceId) throws LoginException {
        Map<String, Object> evaluatedContex = new HashMap<String, Object>();
        APISession session = bonitaAuthenticationService.loginWithDefaultUser();
        ProcessAPI processApi;
        try {
            processApi = TenantAPIAccessor.getProcessAPI(session);
            Map<String, Serializable> context;
            //get context depends of type ( archive or starting)
            try {
                context = processApi.getProcessInstanceExecutionContext(processInstanceId);
            } catch (ProcessInstanceNotFoundException ex) {
                context = processApi.getArchivedProcessInstanceExecutionContext(processInstanceId);
            }
            
            for (Map.Entry<String, Serializable> entry : context.entrySet()) {
                try {
                    String entryName = entry.getKey();
                    //remove "_ref" if is present for the entry key ( if "ref", it's a DAO object)
                    if (entry.getKey().endsWith("_ref")) {
                        entryName = entry.getKey().replace("_ref", "");
                    }					
					//Transform simple object
					if (entry.getValue() instanceof SimpleBusinessDataReference) {
						SimpleBusinessDataReference contextElement = (SimpleBusinessDataReference) entry.getValue();
						int lastDot = contextElement.getType().lastIndexOf(".") + 1;
						String clazz = contextElement.getType().substring(lastDot, contextElement.getType().length());
						//Search the value in BDD if storage_id is not null
						if (contextElement.getStorageId() != null) {
							Map<String, Object> parameters = new HashMap<>();
							parameters.put("persistenceid", contextElement.getStorageId());
							List<BusinessEntity> result = cProcessBusinessDataRepository.findBy(clazz, parameters);
							evaluatedContex.put(entry.getKey(), result.get(0));
						} else {
							evaluatedContex.put(clazz, null);
						}
						logger.debug("SimpleBusinessDataReference Name = " + entry.getKey() + " Type = " + contextElement.getType());
					}//Transform (DAO)object list
					else if (entry.getValue() instanceof MultipleBusinessDataReference) {
						MultipleBusinessDataReference contextElement = (MultipleBusinessDataReference) entry.getValue();
						logger.debug("MultipleBusinessDataReference Name = " + entry.getKey() + " Type = " + contextElement.getType());
						//Search the value in BDD if storage_id is not null
						if (!contextElement.getStorageIds().isEmpty()) {
							int lastDot = contextElement.getType().lastIndexOf(".") + 1;
							String clazz = contextElement.getType().substring(lastDot, contextElement.getType().length());
							Map<String, Object> parameters = new HashMap<>();
							parameters.put("persistenceid", contextElement.getStorageIdsAsString());
							//Récupération des objets dans la bdd
							List<BusinessEntity> result = cProcessBusinessDataRepository.findBy(clazz, parameters);
							evaluatedContex.put(entry.getKey(), result);
						}
					}//Transform Document object
					else if (entry.getValue() instanceof Document) {
						Document contextElement = (Document) entry.getValue();
						logger.debug("Document Name = " + entry.getKey() + " Type = " + Document.class.getTypeName());
						evaluatedContex.put(entry.getKey(), contextElement);
					} else if (entry.getValue() instanceof ArrayList) {
						ArrayList contextElement = (ArrayList) entry.getValue();
						evaluatedContex.put(entry.getKey(), contextElement);
						logger.debug("Default Name = " + entry.getKey() + " Type = " + ArrayList.class.getTypeName());
					} else {
						if (entry.getValue() != null) {
							BusinessDataReference contextElement = (BusinessDataReference) entry.getValue();
							evaluatedContex.put(entry.getKey(), contextElement);
							logger.debug("Default Name = " + entry.getKey() + " Type = " + contextElement.getType());
						}
					}
					break;
                } catch (Exception ex) {
                    logger.error("Impossible de cast l'élément", ex);
                }
            }
        } catch (BonitaHomeNotSetException | ServerAPIException | UnknownAPITypeException | ProcessInstanceNotFoundException | ExpressionEvaluationException ex) {
            logger.error(ex);
        }
        return evaluatedContex;
    }

Are you sure that the issue comes from accessing the context information? Because you are also evaluating some expressions and maybe it is this actual cause of the OutOfMemory exception.

Also can you share a little bit of context about what you are trying to achieve? Evaluating expressions using Engine API is a rather advanced operation and I’m wondering if a simpler solution to your use case exists.

I don’t know how to evaluate context informations.
I konw how to get the context with all bpm objects ( only storage id), but not the value of the object. So i’m trying some code ^^’

This is an exemple of what i get from the context:

"ConfigServiceParameters_ref": {
		"name": "ConfigServiceParameters",
		"type": "com.acme.model.Properties",
		"link": "API/bdm/businessData/com.acme.model.Properties/findByIds?ids=5,6,7,18",
		"storageIds": [5, 6, 7, 18],
		"storageIds_string": ["5", "6", "7", "18"]
	}

So now, i want to get all Properties’s object who are in this list (without using restAPI, only engine).
If you have an idea, i’m aware :wink:

Hello,

Thank for your reply.
I have found a solution to do the job by myself, i will post here after somme comment in my code.
I have allready somethink like your solution, but it’s too restrictive with the DAO.
So, i have implement a generic JDBC to get all object without DAO.