Archive for 8th August 2008

First things first

No último post, apresentei uma aplicação completa com Seam e jBPM. Bem, no mundo real, tarefas devem ser executas em uma determinada ordem. Algumas tarefas tem precedência em relação a outras. No jBPM isso é controlado através do atributo priority. O Seam possui suporte para apresentar as tasks para um determinado actor de acordo com sua prioridade. É o componente chamado taskInstancePriorityList. Podemos usar este componente para listar as tasks de um determinado usuario em ordem de prioridade (1=MAIOR, 5=MENOR).
Na tela onde você apresenta a task para o usuário basta usar este componente ao inves do taskInstanceList:

1
2
3
4
5
6
7
8
9
10
<rich:dataTable value="#{taskInstancePriorityList}" var="_task">
			<rich:column styleClass="d#{_task.priority}">
				<f:facet name="header">ID</f:facet>
				<h:outputText value="#{_task.id}"/>
			</rich:column>
			<rich:column>
				<f:facet name="header">Descri&ccedil;&atilde;o</f:facet>
				<s:link action="#{funcionarioService.avaliarRetorno}" taskInstance="#{_task}"><h:outputText value="#{_task.name}"/></s:link>
			</rich:column>
		</rich:dataTable>

Com o código acima suas tasks estarão agora ordenadas por prioridade, na linha 2, usei um stylesheet baseado na prioridade da task, assim, tasks com prioridade 1 teriam o fundo vermelho, 2 amarelo e 3 verde. O CSS (desculpe a simplicidade, mas eu não sou expert em user interface)

.d1{
	background-color: red;
}
.d2{
	background-color: yellow;
}
.d3{
	background-color: green;
}
 
.d4{
	background-color: green;
}
.d5{
	background-color: green;
}

Agora você garante que suas tarefas mais importantes serão vistas primeiro que as de menor prioridade. Estaria tudo ok se não fosse um pequeno problema. O TaskInstancePriorityList, ordena apenas as tasks atribuídas a um actor. E se precisarmos de uma lista de tasks para um pool de usuários ordenada por prioridade? O Seam, não possui, mas é muito fácil criar uma (o exemplo que coloquei aqui, eu gerei um JIRA no Seam para que eles pudessem incluir em futuras versões, se você gostou, vote nele para que possa ser executado).

O exemplo abaixo vai mostrar como é fácil criar um componente Seam:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.furiousbob.components;
 
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
 
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Transactional;
import org.jboss.seam.annotations.Unwrap;
import org.jboss.seam.bpm.Actor;
import org.jbpm.JbpmContext;
import org.jbpm.taskmgmt.exe.TaskInstance;
 
@Name("pooledTaskPriorityList")
@Install(dependencies="org.jboss.seam.bpm.jbpm",precedence=Install.BUILT_IN)
public class PooledTaskPriority implements Serializable {
	@In
	private Actor actor;
 
	@In
	private JbpmContext jbpmContext;
 
	@SuppressWarnings("unchecked")
	@Unwrap
	@Transactional
	public List<TaskInstance> getTaskPriority(){
		List<TaskInstance> tasks = jbpmContext.getTaskMgmtSession().findPooledTaskInstances(new ArrayList<String>(actor.getGroupActorIds()));
		Collections.sort(tasks, new Comparator<TaskInstance>(){
			public int compare(TaskInstance o1, TaskInstance o2) {
				return (o1.getPriority() > o2.getPriority()) ? 1 : (o1.getPriority() < o2.getPriority() ? -1 : 0);
			}
		});
		return tasks;
	}
}

Vamos lá, a linha 18 declara nosso componente, como qualquer outro, assim você pode invocar #{pooledTaskPriorityList} de qualquer página que o mesmo será invocado.
A novidade é a linha 19 com a anotação @Install. Esta anotação faz com que nosso componente seja criado no momento da inicialização do Seam. O atributo precedence garante que será instalado com a precedência mais alta, tornando sua substituição mais tarde uma tarefa fácil, e dependencies informa que poderá ser instalado apenas após os componentes de BPM do seam estarem disponibilizados (principalmente nosso jbpmContext).

Agora uma das coisas mais legais do Seam. @Unwrap “engana” o container. O que esta anotação faz é o seguinte:

  • usuário solicita uma instância de pooledTaskPriorityList
  • O Seam invoca o componente.
  • O Componente esta anotado com @Unwrap
  • Ao invés de devolver uma instância de PooledTaskPriorityList, o seam devolve um tipo definido pelo método anotado com @Unwrap.

A anotação @Unwrap lhe permite gerar componentes seam para classes que não são gerenciadas por ele. Por exemplo se você quiser injetar um EJB remoto, mas o mesmo não é gerenciado pelo Seam, basta criar uma classe com um método que faz a busca no JNDI e retorna a referência do EJB. Desta forma você pode agora usar o EJB injetando o mesmo nos seus componentes. Aqueles que não estão acostumados isso é um padrão de projeto muito antigo e muito usado em java Proxy. Cool right?

Para usar seu novo componente basta usar #{pooledTaskPriorityList} agora no seu código. A figura abaixo mostra um exemplo deste componente em ação:

Tarefas ordenadas por prioridade (clique para fullsize)

Tarefas ordenadas por prioridade (clique para fullsize)

Note que as tarefas agora não estão mais ordenadas por sua ordem natural (id), mas sim pela prioridade.
Espero que tenham gostado. Bom divertimento com Seam e jBPM, no próximo post vou falar de um outro assunto que esta relacionado a processos : SLAs.
Inté