[SOLVED] Adding Scheduling Functionality to a Custom Module

I am looking to adding scheduled actions to a custom module, where can I find recent code examples, and best practices.

What is the recommended approach?

@ssmusoke

Generally you would create a scheduled “Task”, and then you need to schedule it, probably by inserting into the relevant tables via liquibase.

Code example of a task (in core):

This page documents things, but it is very incomplete: https://wiki.openmrs.org/x/p4Js

Would you mind, at the end of this thread, improving that page?

@darius Just bringing this back up, I have been able to set the Auto Close Visits tasks to automatically startup on my server, however I have found a couple of issues where I need some clarification:

  1. SchedulerService:
  • Currently it supports getting a task by id (system generated) or by name (which can be edited). Can a function to return a task by UUID be added to be consistent with other services
  • How about getTaskByClassName() function too, unless there is need for multiple task configurations of the same class to be run with different parameters.
  1. All examples like AutoCloseVisitTask are done via SQL https://github.com/openmrs/openmrs-core/blob/master/api/src/main/resources/liquibase-update-to-latest.xml#L6202 why not in code like the rest of the configurations (@wyclif)

I think getting a task by id or name should be fine, if no task is found that matches the id or name e.g if it has been edited or deleted, your code should just fail out loud.

@wyclif but throughout the core code, there is getObjectByUUID() which does not change. The getTaskById depends on any existing tasks since its an auto-increment one therefore brittle if you are handling multiple implementation sites.

@ssmusoke, if you’re asking whether it’s okay to add a getTaskByUuid method to the API, then yes, that sounds great, you’re welcome to do it, and it can be backported to the supported release branches.

As a workaround, I think you can write your code to assume a name, and instruct admins not to change that.

I do agree we need to add getTaskByUuid and you can create a ticket for it, but still get by name should be able to work in the meantime, if a given implementation changes the name and the module can’t find it, then it fails to start. But before getByUuid is added and a new release made, alternatively if your custom module has a service and a DAO if not you can add them and then add the getTaskByUuid method to it, infact you can make it generic like below,

<T> T getObjectByUuid(Class<T> clazz, String uuid);

That way you don’t have to wait for it to be added to the platform

Thanks @wyclif will add the method to my Service method and also to core

Good point Wyclif! I hadn’t thought of doing it that way, but it’s better.

@darius @wyclif I ended up using the getTaskByName() method of the Scheduler service, as this abstracts the class name from my custom module which now does not have to be tied to the module containing the Task that I want to configure.

Will find some time to get the getTaskByuuid method into the scheduler service class.

The method I suggested uses a class object and not a class name, in any case class names for domain objects never really change so I wouldn’t worry about that, but if the task name option works for you then I guess it’s fine.

@wyclif The problem with a class object is that it introduces a dependency on the module containing the class into my custom module which Is not desirable. Using the scheduler with UUID or Task Name would not introduce this dependency as it is resolved behind the scenes

But the TaskDefinition class in in core, the class is already on the classpath anyways since your module depends on core, there is no new dependency you’re introducing.

Could it be that we’re not saying the same thing?

Sorry for bringing up an old thread. What could stop a scheduler from processing a loop? I have added a scheduled task which should process a list of objects. Unfortunately, it only processes 2 items and then halts processing with no info/error shown. I have also observed that the task’s last execution time is not updated which means that processing doesn’t complete.

Has anyone experienced such an issue? Thanks in advance. @dkayiwa @mseaton @mksd @ssmusoke @ningosi

Can we have a look at the code of the scheduled task?

Thanks, Daniel. Please find the code at https://pastebin.com/ZWFwarx3

@aojwang Do you have any tests for the task along the different scenarios?

Thanks, @ssmusoke. Did you mean unit tests? Basically, the task is responsible for picking prepared viral load lab manifest payload and pushing them to an endpoint.

@aojwang Actually more of an integration test to check that the task works, its too much work to restart OpenMRS for each change, so better to have a running test that can be validated

Even the post to the payload can be mocked and verified