Skip to content

Commit

Permalink
implementation and documentation
Browse files Browse the repository at this point in the history
Issue imixs#664
  • Loading branch information
rsoika committed Apr 24, 2020
1 parent d3bbf48 commit bbba499
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,23 @@
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;

import javax.annotation.Resource;
import javax.ejb.SessionContext;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;

import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.QuerySelector;
import org.imixs.workflow.WorkflowKernel;
import org.imixs.workflow.engine.scheduler.Scheduler;
import org.imixs.workflow.engine.scheduler.SchedulerException;
import org.imixs.workflow.engine.scheduler.SchedulerService;
import org.imixs.workflow.exceptions.AccessDeniedException;
import org.imixs.workflow.exceptions.ModelException;
import org.imixs.workflow.exceptions.QueryException;

/**
* This EJB implements a Imixs Scheduler Interface and scans workitems for
Expand All @@ -66,6 +73,8 @@ public class WorkflowScheduler implements Scheduler {
final static public int OFFSET_HOURS = 2;
final static public int OFFSET_DAYS = 3;
final static public int OFFSET_WORKDAYS = 4;

final static private int MAX_WORKITEM_COUNT=1000;

private static Logger logger = Logger.getLogger(WorkflowScheduler.class.getName());

Expand All @@ -80,6 +89,11 @@ public class WorkflowScheduler implements Scheduler {

@Inject
private SchedulerService schedulerService;

@Inject
@Any
protected Instance<QuerySelector> selectors;


@Resource
private SessionContext ctx;
Expand Down Expand Up @@ -385,9 +399,10 @@ public ItemCollection run(ItemCollection configItemCollection) throws SchedulerE
* event is identified by the attribute keyScheduledActivity="1"
*
* The method goes through the latest or a specific Model Version
* @throws ModelException
*
*/
protected Collection<ItemCollection> findScheduledEvents(String aModelVersion) throws Exception {
protected Collection<ItemCollection> findScheduledEvents(String aModelVersion) throws ModelException {
Vector<ItemCollection> vectorActivities = new Vector<ItemCollection>();
Collection<ItemCollection> colProcessList = null;

Expand Down Expand Up @@ -424,9 +439,11 @@ protected Collection<ItemCollection> findScheduledEvents(String aModelVersion) t
* lates model version. (issue #482)
*
* @param event - a event model element
* @throws ModelException
* @throws QueryException
* @throws Exception
*/
protected void processWorkListByEvent(ItemCollection event, ItemCollection configItemCollection) throws Exception {
protected void processWorkListByEvent(ItemCollection event, ItemCollection configItemCollection) throws ModelException, QueryException {

// get task and event id form the event model entity....
int taskID = event.getItemValueInteger("numprocessid");
Expand All @@ -442,14 +459,23 @@ protected void processWorkListByEvent(ItemCollection event, ItemCollection confi

if (searchTerm.isEmpty()) {
// build the default selector....
// searchTerm = "($taskid:\"" + taskID + "\" AND $modelversion:\"" +
// modelVersionEvent + "\")";
// we are build the default selector based on workflowgroup (see isseu #482)....
searchTerm = "($taskid:\"" + taskID + "\" AND $workflowgroup:\"" + workflowGroup + "\")";
}

List<ItemCollection> worklist =null;
// test if selector is a CDI Bean
String classPattern="^[a-z][a-z0-9_]*(\\.[A-Za-z0-9_]+)+$";
if (Pattern.compile(classPattern).matcher(searchTerm).find()) {
QuerySelector selector=findSelectorByName(searchTerm);
if (selector!=null) {
schedulerService.logMessage("...CDI selector = " + searchTerm , configItemCollection, null);
worklist=selector.find(MAX_WORKITEM_COUNT,0);
}
} else {
schedulerService.logMessage("...selector = " + searchTerm , configItemCollection, null);
worklist = documentService.find(searchTerm, MAX_WORKITEM_COUNT, 0);
}

schedulerService.logMessage("...selector = " + searchTerm + " ...", configItemCollection, null);
Collection<ItemCollection> worklist = documentService.find(searchTerm, 1000, 0);

logger.finest("......" + worklist.size() + " workitems found");
for (ItemCollection workitem : worklist) {

Expand Down Expand Up @@ -548,4 +574,37 @@ private Date adjustBaseDate(Date baseDate, int offsetUnit, int offset) {
return null;
}




/**
* This method returns an injected Plugin by name or null if no plugin with the
* requested class name is injected.
*
* @param selectorClassName
* @return plugin class or null if not found
*/
private QuerySelector findSelectorByName(String selectorClassName) {
if (selectorClassName == null || selectorClassName.isEmpty())
return null;
boolean debug = logger.isLoggable(Level.FINE);

if (selectors == null || !selectors.iterator().hasNext()) {
if (debug) {
logger.finest("......no CDI selectors injected");
}
return null;
}
// iterate over all injected selectors....
for (QuerySelector selector : this.selectors) {
if (selector.getClass().getName().equals(selectorClassName)) {
if (debug) {
logger.finest("......CDI selector '" + selectorClassName + "' successful injected");
}
return selector;
}
}

return null;
}
}
113 changes: 39 additions & 74 deletions src/site/markdown/engine/workflowscheduler.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
# The Workflow Scheduler
The Imixs WorkflowScheduler provides a TimerService to schedule workitems based on scheduled Workflow Events. A scheduled Workflow Event can be defined using the [Imixs-BPM Modeler](../modelling/index.html) by the property tab 'Timer'. If a workitem is in a status where scheduled events are defined, the WorkflowScheduler will process this workitem automatically based on the model definition. The Workflow Scheduler can be used to automatically process workflow tasks - e.g. a reminder or a escalation task.

The Imixs-Workflow Scheduler is used for the automated processing of *scheduled events*. A *scheduled event* is a BPMN event assigned to a BPMN task. *Scheduled events* can be used e.g. for a auto reminding or an escalation task.

The Imixs WorkflowScheduler implements a TimerService for processing workitems based on *scheduled events*. A *scheduled event* can be configured, using the [Imixs-BPM Modeler](../modelling/index.html), on the property tab 'Timer'. If a workitem is in a status with scheduled events, the Imixs-Workflow Scheduler will process this workitem automatically based on the corresponding configuration.

<center><img src="../images/modelling/bpmn_screen_35.png" style="max-width: 750px;" /></center>

## The Scheduled Event

A *scheduled event* can be configured in the property tab 'Timer'.


| | Description |
|------------|------------------------------------------------------------------------------|
|enabled | choose 'yes' to enable a scheduled event (default 'no') |
|selection | optional definition to select workitems based on a query or selector class |
|delay | delay in minutes, hours, days or working days |
|from | base item to compute the delay |

**Note:** A scheduled event must define a base datetime item to compute schedule. You can choose a standard item like '$created', '$modified' or '$lastprocessdate' or you can define a application managed item (e.g. a due-date or an invoice-date) .




## The Configuration
## The Timer Configuration

The _WorkflowSchedulerService_ expects the scheduling configuration document. The scheduling configuration defines the timer interval to run the workflow scheduler.

Expand All @@ -27,7 +46,7 @@ A timer configuration can be created with the following item definitions:
</document>


## Scheduling
### Scheduling

The Imixs WorkflowSchedulerService uses a calendar-based syntax for scheduling based on the EJB 3.1 Timer Service specification. The syntax takes its roots from the Unix cron utility.
The following attributes can be stored in the txtConfiguration property of the workflowScheulderSercice configuration:
Expand Down Expand Up @@ -57,31 +76,31 @@ Each attribute of an expression supports values in different formats. For exampl

So you can configure the scheduler is several ways. Here a some typical exampls for possible configuration:

###Every hour
### Every hour
hour=*

###Every five minutes with the hour
### Every five minutes with the hour

minute=*/15
hour=*

###Every hour from Monday to Friday
### Every hour from Monday to Friday

hour=*
dayOfWeek=1-5

###Every 30 minutes with the hours from 8:00 AM to 5:00 PM, from Monday to Friday
### Every 30 minutes with the hours from 8:00 AM to 5:00 PM, from Monday to Friday

minute=*/30
hour=8-17
dayOfWeek=1-5

###Only Sunday on 1:00 AM
### Only Sunday on 1:00 AM

hour=1
dayOfWeek=7

The scheudler information is stored in the property 'txtConfiguration' as a String List.
The scheduler information is stored in the property 'txtConfiguration' as a String List.
The configuration entity for the WorkflowSchedulerService holds the following additional information.

| property |type | description |
Expand All @@ -91,8 +110,8 @@ The configuration entity for the WorkflowSchedulerService holds the following ad
|statusmessage|String | last status message (read only) |
|Schedule | String | scheduling information (read only) |
|nextTimeout | Date | Timestamp for next timeout |
|timeRemaining | Long | milisecnds until next timeout |
|datLastRun | Date | Timestamp of last sucessfull run (read only) |
|timeRemaining | Long | milliseconds until next timeout |
|datLastRun | Date | Timestamp of last successful run (read only) |
|numInterval | int | optional- timer interval if no txtConfiguration is defined |
|datStart | Date | optional- start date for timer if no txtConfiguration is defined |
|datStop | Date | optional- stop date for timer if no txtConfiguration is defined |
Expand All @@ -111,8 +130,16 @@ The selector can be overwritten by the BPMN event. For example the modelversion
($taskid:"[TASKID]" AND $workflowgroup:"[MY-WORKLFOWGROUP]")


### Custom QuerySelector

An application can implement a *QuerySelector* CDI bean to provide a custom selection of scheduled workitems. The full qualified class name of a QuerySelector can be used instead of a search query.

myapp.selectors.MyCustomSelector

## Ignored Workitems
If a *QuerySelector* is defined, the WorkflowScheduler tries to inject the selector CDI Bean and calls the find method to select the workitems to be scheduled.

### Ignored Workitems

The _WorkflowSchedulerService_ processes all kinds of workitems which are assigned to a valid workflow model definition with scheduled events.
A workitem is ignored by the _WorkflowSchedulerService_ only in case the workitem type ends with the sufix 'deleted'

Expand All @@ -124,66 +151,4 @@ or the workitem is marked as immutable

See the [DocumentService](./documentservice.html) for details.


## The WorkflowSchedulerService EJB
The WorkflowSchedulerService EJB implements the following methods:

public ItemCollection loadConfiguration() ;

public ItemCollection saveConfiguration(ItemCollection configItemCollection)
throws AccessDeniedException;

public ItemCollection start() throws AccessDeniedException;

public ItemCollection stop() throws AccessDeniedException ;

public boolean isRunning();

The methods "_loadConfiguration()_" and "_saveConfiguration()_" can be used to lookup
or update the current workflow scheduler configuration entity. The methods "_start()_" and "_stop()_" can be used to start or stop the timer service. The method "_isRunning()_" returns true if the TimerService was started. If the workflowScheduler was started the timerService can be identified by the $UniqueID of the scheduler configuration entity.


### Security & Deployment
The ScheduledWorkflowService EJB is embedded into the security concepts of the Imixs Workflow Engine. As the ScheduledWorkflowService needs full access rights to all workitems
to perform a scheduled activity the EJB is annotated with the @RunAs declaration:

@DeclareRoles( { "org.imixs.ACCESSLEVEL.MANAGERACCESS" })
@Stateless
@RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS")
@Remote(org.imixs.workflow.jee.ejb.WorkflowScheduler.class)
public class WorkflowSchedulerService implements WorkflowSchedulerServiceRemote {
....
}

This means that the WorkflowSchedulerService EJB need to be deployed with sufficient security role mappings. For Glassfish it is necessary to declare a principal-name which will be used by the EJB Container calling the @TimeOut method. You can use the glassfish specific glassfish-application.xml deployment descriptor in the jee module. To declare a principal-name with manager access use the following example where the userID "IMIXS-WORKFLOW-Service" is mapped to the Role "org.imixs.ACCESSLEVEL.MANAGER" in the realm "imixsrealm":

<glassfish-application>
....
<security-role-mapping>
<role-name>org.imixs.ACCESSLEVEL.MANAGERACCESS</role-name>
<group-name>Manager</group-name>
<principal-name>IMIXS-WORKFLOW-Service</principal-name>
</security-role-mapping>
<realm>imixsrealm</realm>
...
</glassfish-application>

So in this case the TimerService will be performed by the user "Manfred".

<strong>Note:</strong> Be careful to declare this role and principal-name with care and a setting which is reasonable in your server environment. For example use a reserved user account like "IMIXS-WORKFLOW-Service" which is not used by persons in your organization.


### galssfish-ejb-jar.xml

Using the galssfish-ejb-jar.xml descriptor it is also necessary to add a principal user to the ejb declaration which will be associate to the @RunAs annotation. If no principal is defined in galssfish-ejb-jar.xml, the application server uses a principal from the security-role-mapping. If there is only one principal associated with the role, that principal will be taken as the default run-as principal value. But if there is more than one principal associated with the role, you need to explicitly set the run-as principal in the galssfish-ejb-jar.xml. The following example demonstrates setting the run-as principal in galssfish-ejb-jar.xml:

...
<ejb>
<ejb-name>WorkflowSchedulerService</ejb-name>
<principal>
<name>IMIXS-WORKFLOW-Service</name>
</principal>
</ejb>
...


0 comments on commit bbba499

Please sign in to comment.