Estrutura da Activiti (Java) - descrição do fluxo de tarefas XML (bpm) e seu gerenciamento. Aqui vou descrever os conceitos básicos básicos e como construir processos de negócios simples.
O principal conceito da Activiti é um processo e uma tarefa. Um processo é todas as tarefas interconectadas por fluxos e ramificações direcionadas.
Vou abordar esses aspectos:
- - Activiti em sua forma mais pura
- - Usuários, funções
- - Conecte o SpringBoot
- - API REST
- - Trabalho e delegado
O movimento dos fluxos segue etapas de tarefa para tarefa, cada uma dessas etapas pausa o processo enquanto aguarda a conclusão da entrada e da tarefa, todas as ações intermediárias são armazenadas no banco de dados.
Onde, o que levar, vou indicar abaixo. Vamos começar com um exemplo simples - o processo de desenvolvimento do programa, que consiste em escrever código e testar. Abaixo está um diagrama de processo.

Tudo isso é um processo, possui um ID, nome e outras características.

Tem:
O início do processo, duas tarefas “Desenvolver” e “Teste”, uma ramificação (gateway) e o final do processo. Em palavras, tudo acontece assim:
- loading bpm description
- iniciar o processo
- após o início, caímos imediatamente na tarefa de desenvolvimento
- após a execução do Develop, ele entra em teste e, de acordo com o resultado do teste, o processo termina ou retorna novamente ao desenvolvimento.
Activiti consiste em um conjunto de serviços
Aqui estão os principais:
- RepositoryService: controla o carregamento de descrições de processo
- RuntimeService: inicia processos
- TaskService: executa tarefas
- FormService: acesso a variáveis de tarefa
- HistoryService: acesso ao histórico do processo
- IdentityService: usuários e funções
Activiti em sua forma mais pura
Mas tudo começa com a configuração e o arquivo - activiti.cfg.xml.
A partir disso
ProcessEngineConfiguration cfg = ProcessEngineConfiguration .createProcessEngineConfigurationFromResource("activiti.cfg.xml");
Se você não usar sua configuração, o Activiti implantará o banco de dados na própria memória H2, isso não combina comigo, mas meu amado Oracle está cheio, há opções para conectar bancos de dados diferentes.
Aqui está minha configuração
activiti.cfg.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"> <property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:xe" /> <property name="jdbcDriver" value="oracle.jdbc.driver.OracleDriver" /> <property name="jdbcUsername" value="BPM" /> <property name="jdbcPassword" value="1" /> <property name="databaseSchemaUpdate" value="false" /> <property name="asyncExecutorActivate" value="false" /> <property name="mailServerPort" value="5025" /> </bean> </beans>
Alteramos os valores em "nome da propriedade = jdbc *" e conectamos outro banco de dados
Estrutura do projeto

POM <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>DemoActiviti</groupId> <artifactId>DemoActiviti</artifactId> <version>1.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.activiti</groupId> <version>6.0.0</version> <artifactId>activiti-spring-boot-starter-integration</artifactId> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> <version>11.2.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>2.4.1</version> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <archive> <manifest> <mainClass>com.example.DemoActiviti</mainClass> </manifest> </archive> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>
A presença do plugin "maven-assembly-plugin" no POM permitirá criar (empacotar) um jar que é iniciado com dependências e executado -
java -jar DemoActiviti-1.0-SNAPSHOT-jar-with-dependencies.jar
Driver jdbc para Oracle instalado no repositório maven local
mvn install:install-file -Dfile={Path/to/your/ojdbc6.jar} -DgroupId=com.oracle -DartifactId=ojdbc6 -Dversion=11.2.0 -Dpackaging=jar
log4j log4j.rootLogger=WARN, ACT log4j.appender.ACT=org.apache.log4j.ConsoleAppender log4j.appender.ACT.layout=org.apache.log4j.PatternLayout log4j.appender.ACT.layout.ConversionPattern= %d{hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n
Para esse processo, definimos 4 ações: carregamento de bpm, início do processo, desenvolvimento e teste. Cada ação terá um parâmetro correspondente: implantar, iniciar, desenvolver, testar.
Tomamos scripts para um banco de dados de
activiti-começarlá na pasta \ activiti-6.0.0 \ activiti-6.0.0 \ database \ create - scripts para criar um banco de dados
Usuários, Funções
Prepare usuários e funções:
Identidade public class DemoActiviti { private static final String DEV_PROCESS = "devProcess"; public static void main(String[] args) { Locale.setDefault(Locale.ENGLISH); ProcessEngineConfiguration cfg = ProcessEngineConfiguration .createProcessEngineConfigurationFromResource("activiti.cfg.xml"); ProcessEngine processEngine = cfg.buildProcessEngine(); createIdentity(processEngine, "programmer", "programmers"); createIdentity(processEngine, "tester", "testers"); } public static void createIdentity(ProcessEngine processEngine, String userName, String userGroup) { IdentityService identityService = processEngine.getIdentityService(); String userId = userName + "Id"; if (identityService.createUserQuery().userId(userId).count() == 0) { User user = identityService.newUser(userName); user.setId(userId); user.setEmail(userName + "@gmail.com"); identityService.saveUser(user); System.out.println("user created success fully"); } String groupId = userGroup + "Id"; if (identityService.createGroupQuery().groupId(groupId).count() == 0) { Group group = identityService.newGroup(userGroup); group.setName(userGroup); group.setId(groupId); identityService.saveGroup(group); System.out.println("group created success fully"); } if (identityService.createGroupQuery().groupId(groupId).list().size() > 0) { identityService.createMembership(userId, groupId); System.out.println("user to group success fully"); } } }
Crie usuários e grupos, desenvolvedor e testador, respectivamente.
No banco de dados, todas as tabelas são divididas pelos serviços correspondentes e têm prefixos
ACT_RE_ *: repositório.
ACT_RU_ *: tempo de execução.
ACT_ID_ *: identidade.
ACT_HI _ *: histórico
e assim por diante
Depois de criar usuários, você pode ver aqui

Atribuiremos nossas tarefas na descrição aos grupos correspondentes (CandidateGroup), por exemplo, a tarefa Desenvolver ao grupo - programadores

E assim, a primeira coisa que fazemos é colocar no banco de dados "MyProcess.bpmn", executar o programa com o comando
deploy java -jar DemoActiviti-1.0-SNAPSHOT-jar-with-dependencies.jar deploy
Em seguida, inicie o processo de início
java -jar DemoActiviti-1.0-SNAPSHOT-jar-with-dependencies.jar start
Após o processo delpoy e start, as entradas correspondentes aparecerão no banco de dados.
Repositório

Tempo de execução que tarefa está em execução

a quem está designado

No código, fica assim (o código completo estará abaixo):
implantar deployment = repositoryService.createDeployment() .addClasspathResource("processes/MyProcess.bpmn").deploy()
iniciar ProcessInstance myProcess = runtimeService.startProcessInstanceByKey(DEV_PROCESS);
desenvolverDepois disso, você pode começar a concluir a tarefa de desenvolvimento.
java -jar DemoActiviti-1.0-SNAPSHOT-jar-with-dependencies.jar develop
// tasks = taskService.createTaskQuery().taskCandidateGroup("programmers").list();
Na tarefa Desenvolver, uma variável de "problema" é definida

Depois de processar as variáveis usando o FormService, a tarefa é executada
for (Task task : tasks) { System.out.println("Task:" + task.getTaskDefinitionKey() + ", id=" + task.getId()); FormData formData = formService.getTaskFormData(task.getId()); Map<String, Object> variables = new HashMap<String, Object>();

Para a tarefa de Desenvolvimento, você será solicitado a inserir uma variável.
Na tabela histórica, você pode ver as variáveis e os valores da tarefa, processo

Assim, o processo após a tarefa de Desenvolvimento parar nele, o estado será salvo no banco de dados.
Em geral, o loop se parece com isso:
Solicitar uma tarefa para um executor
tasks = taskService.createTaskQuery().taskCandidateGroup("...").list();
Definição de Variáveis
Map<String, Object> variables = new HashMap<String, Object>(); ... variables.put("var_1", value);
Execução de tarefas
taskService.complete(task.getId(), variables);
Verificando o final do processo
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() .processInstanceId(processInstance.getId()).singleResult(); if (processInstance != null && !processInstance.isEnded())
Após a execução de cada tarefa, o processo é suspenso até que uma nova tarefa seja concluída.
Portanto, depois de executar o Develop, vamos para a tarefa Test, aqui também será solicitado que você insira a variável "devResult" - o resultado do desenvolvimento (não funcionou muito bem, mesmo antes do Teste iniciar, digite o resultado) e o resultado será ramificado ou finalizado (Ok) ou desenvolvimento novamente (Não), consulte o diagrama do processo.

Nesse caso, o desenvolvimento etc. Se você agora solicitar tarefas para o desenvolvedor, elas serão, mas para teste - não.
Código do programa package com.example; import org.activiti.engine.*; import org.activiti.engine.form.FormData; import org.activiti.engine.form.FormProperty; import org.activiti.engine.repository.Deployment; import org.activiti.engine.runtime.ProcessInstance; import org.activiti.engine.task.Task; import org.apache.commons.lang3.StringUtils; import java.util.*; public class DemoActiviti { private static final String DEV_PROCESS = "devProcess"; public static void main(String[] args) { Locale.setDefault(Locale.ENGLISH); ProcessEngineConfiguration cfg = ProcessEngineConfiguration .createProcessEngineConfigurationFromResource("activiti.cfg.xml"); ProcessEngine processEngine = cfg.buildProcessEngine(); RepositoryService repositoryService = processEngine.getRepositoryService(); String mode = StringUtils.EMPTY; if (args.length > 0) { mode = args[0]; } System.out.println("Processes mode: " + mode); Deployment deployment; if ("deploy".equals(mode)) { deployment = repositoryService.createDeployment() .addClasspathResource("processes/MyProcess.bpmn").deploy(); System.out.println("deploy process success"); System.exit(0); } else { List<Deployment> myProcesses = repositoryService.createDeploymentQuery() .processDefinitionKey(DEV_PROCESS).list(); deployment = myProcesses.get(myProcesses.size()-1); System.out.println("get process success:" + deployment.getId()); }
Conecte o SpringBoot
Modificamos o projeto usando o Spring
Adicionar dependências ao POM
POM com SpringBoot <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.1.RELEASE</version> <relativePath/> </parent> <dependencies> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-spring-boot-starter-basic</artifactId> <version>6.0.0</version> </dependency> <dependency> <groupId>org.activiti</groupId> <version>6.0.0</version> <artifactId>activiti-spring-boot-starter-integration</artifactId> </dependency> ....
A classe DemoActiviti tornou-se agora
DemoActiviti - SpringBootApplication @SpringBootApplication @ImportResource("classpath:activiti.cfg.xml") public class DemoActiviti { public static void main(String[] args) { Locale.setDefault(Locale.ENGLISH); SpringApplication.run(DemoActiviti.class, args); } }
Uso um modelo misto - quando parte dos beans é descrita na configuração xml (@ImportResource ("classpath: activiti.cfg.xml")) e a outra é definida por meio de anotações.
activiti.cfg.xml - spring <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> <property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" /> <property name="driverClass" value="oracle.jdbc.driver.OracleDriver" /> <property name="username" value="BPM" /> <property name="password" value="1" /> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration"> <property name="dataSource" ref="dataSource" /> <property name="transactionManager" ref="transactionManager" /> <property name="databaseSchemaUpdate" value="true" /> <property name="asyncExecutorActivate" value="false" /> </bean> </beans>
Agora o Spring é responsável pela configuração, pode ser visto
bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration"
Adicionar processamento de linha de comando padrão para SpringBoot como um componente
Linha de Comando @Component public class CommandLine implements CommandLineRunner { @Autowired private DemoService demoService; public void run(String... args) { if ("test".equals(args[0])) { demoService.startTest(); } else if ("develop".equals(args[0])) { demoService.startDevelop(); } } }
Que processará todos esses comandos, não implementarei todos, tudo é simples por lá, mostrarei dois: testar e desenvolver. E adicione um serviço para processá-los
DemoService @Service public class DemoService { @Autowired private TaskService taskService; @Autowired private FormService formService; public void startTest() { List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("testers").list(); if (tasks.isEmpty()) { System.out.println(" "); return; } processTasks(tasks); } public void startDevelop() { List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("develop").list(); if (tasks.isEmpty()) { System.out.println(" "); return; } processTasks(tasks); } private void processTasks(List<Task> tasks) { Scanner scanner = new Scanner(System.in); for (Task task : tasks) { ...... , }
No componente CommandLine Autowir, eles têm o serviço DemoService e nele os serviços Spring Activiti já preparados
@Autowired private TaskService taskService;
Nós coletamos, executamos como antes na linha de comando.
Se quisermos usar a execução de tarefas da Web, conecte a API REST.
API REST
O SpringBoot fornecerá um servidor Tomcat incorporado por padrão e, depois, um problema técnico.
No POM, para o que é, adicione dependência da Web da primavera
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
Removemos o componente CommandLine, agora tudo passará pela URL via HTTP. Adicione RestController:
Restcontroller @RestController public class DemoRestController { @Autowired private DemoService demoService; @RequestMapping(value="/test", method= RequestMethod.GET, produces= {MediaType.APPLICATION_JSON_VALUE}) public List<String> startTest(@RequestParam String devResult) { List<String> strings = demoService.startTest(devResult); return strings; } @RequestMapping(value="/develop", method= RequestMethod.GET, produces= MediaType.APPLICATION_JSON_VALUE) public List<String> startDevelop(@RequestParam String issue) { List<String> strings = demoService.startDevelop(issue); return strings; } @RequestMapping(value="/start", method= RequestMethod.GET, produces= MediaType.APPLICATION_JSON_VALUE) public List<String> startProcess() { List<String> strings = demoService.startDevProcess(); return strings; } }
Executamos os mesmos comandos, alteramos ligeiramente as respostas do serviço DemoService, que o Autowire está no controlador.
DemoService @Service public class DemoService { @Autowired private TaskService taskService; @Autowired private FormService formService; @Autowired private RuntimeService runtimeService; public List<String> startTest(String devResult) { List<String> results = new ArrayList<>(); List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("testers").list(); if (tasks.isEmpty()) { results.add("The tasks for testing are not"); return results; } Object issue = runtimeService.getVariables(tasks.get(0).getProcessInstanceId()).get("issue"); processTasks(tasks, devResult); results.add("Task N " + issue + " - tested, result=" + devResult); return results; } public List<String> startDevelop(String issue) { List<String> results = new ArrayList<>(); List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("programmers").list(); if (tasks.isEmpty()) { results.add("There are no development tasks"); return results; } processTasks(tasks, issue); Object mIssue = runtimeService.getVariables(tasks.get(0).getProcessInstanceId()).get("issue"); results.add("Task N " + mIssue + " - taken in the develop"); return results; } public List<String> startDevProcess() { List<String> results = new ArrayList<>(); ProcessInstance myProcess = runtimeService.startProcessInstanceByKey("devProcess"); results.add("The process is started #"+myProcess.getId()); return results; } private void processTasks(List<Task> tasks, String param) { for (Task task : tasks) { FormData formData = formService.getTaskFormData(task.getId()); Map<String, Object> variables = new HashMap<>();
testando usando curl, aqui está o resultado:

Alterei a porta do Tomcat para 8081 em application.properties
server.port = 8081
Activiti job
A Activiti possui muitos designs, por exemplo, o lançamento de tarefas agendadas é "TimerStartEvent". Para que o Job comece a executar em limites, você deve especificar
property name="asyncExecutorActivate" value="true"
(consulte activiti.cfg.xml), o processo java permanecerá em execução e verificará o agendamento e as tarefas de execução.
Voltarei ao projeto inicial, onde o Activiti é usado em sua forma mais pura.
Na classe DemoActiviti, deixarei o suporte para apenas dois comandos: deploy e start farei um novo processo

Após o início do processo, ele irá para o cronômetro, que de acordo com o cronograma iniciará a tarefa "Desenvolver". O cronômetro terá uma programação - inicie a cada 10 segundos, expressão cron - "0/10 * * * * *?".

Vamos implantar o novo processo como antes, e então iniciar o processo (start). Tudo - a tarefa é executada a cada 10 segundos.
Como tarefa, o componente Activiti é selecionado - ServiceTask, a partir do qual você pode especificar como uma implementação de classe Java

classe DemoDelegate public class DemoDelegate implements JavaDelegate { @Override public void execute(DelegateExecution execution) { Date now = new Date(); execution.setVariable("issue", now.toString()); System.out.println("job start="+now); } }
Na tabela no banco de dados (selecione * de ACT_RU_TIMER_JOB t), você pode ver

Atividade do trabalho, no campo DUEDATE_, haverá um horário para o próximo início.
A variável "issue" do Delegado será registrada no histórico de execução
select * from ACT_HI_VARINST t

código para o trabalho DemoActiviti c public class DemoActiviti { private static final String DEV_PROCESS = "devProcessJob"; public static void main(String[] args) { Locale.setDefault(Locale.ENGLISH); ProcessEngineConfiguration cfg = ProcessEngineConfiguration .createProcessEngineConfigurationFromResource("activiti.cfg.xml"); ProcessEngine processEngine = cfg.buildProcessEngine(); RepositoryService repositoryService = processEngine.getRepositoryService(); String mode = StringUtils.EMPTY; if (args.length > 0) { mode = args[0]; } System.out.println("Processes mode: " + mode); Deployment deployment; if ("deploy".equals(mode)) { deployment = repositoryService.createDeployment() .addClasspathResource("processes/MyProcessJob.bpmn").deploy(); System.out.println("deploy process success"); System.exit(0); } else { List<Deployment> myProcesses = repositoryService.createDeploymentQuery() .processDefinitionKey(DEV_PROCESS).list(); deployment = myProcesses.get(myProcesses.size()-1); System.out.println("get process success:" + deployment.getId()); }
Ainda resta muito: Eventos, Ouvinte, JPA, etc., talvez eu volte a eles.
Materiais
ActivitiDesigner EclipsedevProcess bpmn <?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test"> <process id="devProcess" name="Dev process" isExecutable="true"> <startEvent id="startevent1" name="Start" activiti:initiator="programmerId"></startEvent> <userTask id="develop" name="Develop" activiti:candidateGroups="programmers"> <extensionElements> <activiti:formProperty id="issue" name="issue" type="string" required="true"></activiti:formProperty> </extensionElements> </userTask> <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="develop"></sequenceFlow> <userTask id="test" name="Test" activiti:candidateGroups="testers"> <extensionElements> <activiti:formProperty id="devResult" name="devResult" type="string" default="No" required="true"></activiti:formProperty> </extensionElements> </userTask> <sequenceFlow id="flow2" sourceRef="develop" targetRef="test"></sequenceFlow> <exclusiveGateway id="gateway" name="Exclusive Gateway" default="flowNo"></exclusiveGateway> <sequenceFlow id="flow3" sourceRef="test" targetRef="gateway"></sequenceFlow> <sequenceFlow id="flowOk" name="Ok" sourceRef="gateway" targetRef="endevent1"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${devResult == "Ok"}]]></conditionExpression> </sequenceFlow> <sequenceFlow id="flowNo" name="No" sourceRef="gateway" targetRef="develop"></sequenceFlow> <endEvent id="endevent1" name="End"></endEvent> </process> </definitions>
devProcessJob bpmn <?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test"> <process id="devProcessJob" name="Dev process Job" isExecutable="true"> <startEvent id="startevent" name="Start" activiti:initiator="programmerId"></startEvent> <sequenceFlow id="flow1" sourceRef="startevent" targetRef="timerstartevent"></sequenceFlow> <endEvent id="endevent" name="End"></endEvent> <startEvent id="timerstartevent" name="Timer start"> <extensionElements> <activiti:formProperty id="issue" name="issue" type="string"></activiti:formProperty> </extensionElements> <timerEventDefinition> <timeCycle>0/10 * * * * ?</timeCycle> </timerEventDefinition> </startEvent> <sequenceFlow id="flow2" sourceRef="timerstartevent" targetRef="servicetask1"></sequenceFlow> <sequenceFlow id="flow3" sourceRef="servicetask1" targetRef="endevent"></sequenceFlow> <serviceTask id="servicetask1" name="Develop" activiti:class="com.example.DemoDelegate"></serviceTask> </process> </definitions>