Implementação de Process Actions no Lumis Portal
Objetivos
- Compreender conceitualmente o que são Process Actions;
- Compreender tecnicamente o funcionamento das Process Actions;
- Compreender como são configuradas chamadas para Process Actions a partir do douidefinition, bem como os handlers pré-definidos.
- Compreender os diversos responses padrões para uma Process Action
Referências complementares
- Ciclo de vida de uma interface
- DOUI
- DOUI versus Content
- Renderização client-side script
O que são Process Actions?
As Process Actions são controladoras que interpretam a ação de um usuário e executam uma lógica de sua aplicação. Geralmente estas ações são submissões de formulários, seja para adicionar, modificar ou excluir dados.
Se pensarmos no conhecido padrão MVC (Model-View-Controller), que tem por objetivo alcançar uma melhor separação de responsabilidades dentro da sua aplicação, podemos dizer que as Process Actions fazem o papel da controladora. Conforme temos no Wikipedia (http://pt.wikipedia.org/wiki/MVC):

"O controlador (controller) recebe a entrada de dados e inicia a resposta ao utilizador ao invocar objetos do modelo, e por fim uma visão baseada na entrada. Ele também é responsável pela validação e filtragem da entrada de dados."
Portanto, devemos pensar numa Process Action como um passo intermediário, que interpreta a ação de um usuário (ex: um clique no botão) e executa uma determinada ação. Essa ação pode consistir em obter os dados de um formulário e chamar uma classe DAO (Data Access Object) que salve essas informações no banco de dados, ou por exemplo, enviar um e-mail com o login e senha de acesso do usuário ao sistema.
Como são implementadas?
Cada process action é executada através de um ou mais objetos, chamados handlers, que irão de fato processar a ação solicitada pelo usuário. Cada interface define seus process actions e respectivos handlers.
Cada process action handler consiste em uma classe que implementa a interface Java IProcessActionHandler, normalmente estendendo-se a classe genérica ProcessActionHandler.
Cada handler pode gerar uma resposta, contendo parâmetros a serem utilizados na renderização da interface, chamados de parâmetros de renderização (render parameters). Um exemplo de resposta seria o “doui_closeWindow”, utilizado por interfaces que executam numa janela pop-up (por exemplo, ao adicionar um conteúdo). Após salvar os dados, esta resposta contém um parâmetro de renderização que instrui a interface a renderizar apenas o código Javascript para fechar a janela, no lugar do HTML normal da interface.
A execução dos process actions é realizada pelo ProcessActionContainer, responsável por instanciar as classes que implementam a interface IProcessActionHandler e terão o seguinte ciclo de vida:
- Execução do método init;
- Execução do método setParameter passando todos os parâmetros definidos;
- Execução do método processAction
Para facilitar a implementação de um IProcessActionHandler, pode-se estender a classe ProcessActionHandler e implementar apenas o método processAction.
Abaixo um exemplo de código de uma process action handler responsável pela atualização dos registros de um sistema hoteleiro.
package corporate.hoteisadmin4;
import lumis.doui.processaction.ProcessActionHandler;
import lumis.doui.source.Source;
import lumis.portal.PortalException;
public class HotelProcessActionHandler extends ProcessActionHandler<Source> {
public void processAction() throws PortalException {
String[] primaryKeys = (String [])parameters.get("hotelId");
HotelDaoJdbc hotelDao = new HotelDaoJdbc();
hotelDao.definirLotados(primaryKeys, this.transaction);
}
}
Como é feita a chamada para uma Process Action?
Cada elemento <processAction> define um process action handler através do atributo type (no caso de handlers padrão do produto) ou do atributo className (no caso de handlers não-padrão, customizados ou personalizados), que indica a classe Java do handler. Caso um process action deva executar mais de um handler, haverá mais de um elemento <processAction> com o mesmo valor para o atributo "id". Neste caso, quando um process action com esse identificador é desencadeado, todos os process actions com esse identificador são executados, na ordem em que estão definidos dentro do elemento <processActions>.
Exemplo de definição de um process action para uma determinada interface:
<controls>
<control:lum_form>
<control:lum_interfaceHeaderButtons>
<control:lum_deleteButton>
<onClick type="script">
<script>
if(!confirm('Você tem certeza que quer excluir o item?')){
return;
}
</script>
<validators>
<validator controlId="adminList.tabulardata" type="selectedOne" />
</validators>
</onClick>
<onClick type="processAction" processActionId="delete" />
</control:lum_deleteButton>
</control:lum_interfaceHeaderButtons>
<control:lum_adminList id="adminList" />
</control:lum_form>
</controls>
<processActions>
<processAction id="delete" type="tableDeleteData"/>
</processActions>
Exemplo de definição de mais de uma process action para uma determinada interface:
<processActions>
<processAction id="delete" type="generic" className="processAction.ExcluiDependenciasUsuario">
<processAction id="delete" type="tableDeleteData" sourceId="default">
<response type="doui_setRequestParameters"/>
</processAction>
</processActions>
Note que as process action handlers serão executadas na ordem em que são declaradas.
Outro exemplo: O controle de botão& Ok (lum_okButton), ao ser clicado, dispara a process action ;com identificador “commit”. O atributo type com valor tableAddData indica que a classe de process action handler será TableAddDataProcessActionHandler, responsável por adicionar dados de um formulário em uma tabela de banco de dados.
Na definição do process action, há dois tipos de resposta: o doui_refreshParent, que instrui a renderização a dar um refresh na página pai da janela pop-up da interface, e o doui_closeWindow, que instrui a renderização a fechar a janela pop-up da interface.
<controls>
<control type="lum_label" sourceId="default" dataId="name" />
<control type="lum_text" id="name" sourceId="default" />
<control type="lum_okButton">
<onClick type="processAction">
<processActionId>commit</processActionId>
</onClick>
</control>
</controls>
<processActions>
<processAction id="commit" type="tableAddData">
<response type="doui_refreshParent" />
<response type="doui_closeWindow" />
</processAction>
</processActions>
Como é feito o controle de transação nas Process Actions?
Por padrão, o Lumis mantém o mesmo escopo transacional para uma cadeia de process actions que estão sendo executadas. Portanto, caso alguma delas lance uma exceção (ex: PortalException), automaticamente as próximas não serão chamadas e um rollback será feito, impedindo inconsistências no banco de dados.
Process Action Handlers padrões do Lumis
Como vimos na seção anterior, é possível trabalhar com dois tipos de process action handler. Um grupo seria o de handlers customizados, onde criamos nossa própria classe Java e implementamos a nossa lógica.
Esta seção tem por objetivo abordar os process action handlers providos pelo Lumis para tratar as operações mais básicas, tais como adicionar, atualizar ou remover dados de um registro cujo source seja do tipo table ou do tipo content. Estes tipos padrões podem ser chamados mudando-se o valor do atributo type do nodo <processAction>. Os tipos padrões estão listados na tabela abaixo:
Valor no atributo type | Descrição |
---|---|
generic | Este process action handler apenas executa os responses que estão em sua definição. Ele é implementado por GenericProcessActionHandler. |
tableAddData, tableUpdateData e tableDeleteData | Estes process action handlers são utilizados para adicionar, atualizar e remover um dado da persistência respectivamente. As suas implementações são apropriadas para quando é utilizado o TableSource com sua forma padrão de mapeamento de dados (o source com type=“table”). As classes que implementam cada um desses process actions handlers são, respectivamente: -TableAddDataProcessActionHandler, -TableUpdateDataProcessActionHandler, -TableDeleteDataProcessActionHandler. |
tableAddMultiRowData e tableUpdateMultiRowData | Estes process action handlers são utilizados para adicionar e atualizar múltiplos registros de dados. Também é utilizado o TableSource como a forma padrão de mapeamento de dados o source comtype=“table”). As classes que implementam cada um desses process actions handlers são, respectivamente: -TableAddMultiRowDataProcessActionHandler, -TableUpdateMultiRowDataProcessActionHandaler. |
Entretanto, os process action handlers padrão de doui para adicionar, atualizar e excluir dados não podem ser usados em sources de content, uma vez que estes precisam também manipular dados específicos de metadados. No lugar de cada um desses process actions handlers de DOUI, há um substituto que deve ser utilizado em seu lugar quando o source for do tipo “contentTable”.
A tabela a seguir lista as classes de process actions handlers que devem ser utilizadas no lugar de cada uma de DOUI.
Doui | Content |
---|---|
tableAddDataProcessActionHandler | contentTableAddDataProcessActionHandler |
tableUpdateDataProcessActionHandler | contentTableUpdateDataProcessActionHandler |
tableDeleteDataProcessActionHandler | contentTableDeleteDataProcessActionHandler |
Responses: O que fazer após uma Process Action ser executada?
O elemento <response> é utilizado pela process action como resposta ao navegador, após a sua execução. Através do atributo "type" definimos os tipos de respostas possíveis.
No exemplo a seguir, na execução da segunda process action handler, a resposta é do tipo doui_hyperlink, que irá redirecionar o browser para outra página ou URL e passar um ou mais parâmetros.
<processActions>
<processAction id="delete" sourceId="voteQuestion" type="contentTableDeleteData"/>
<processAction id="results" type="generic">
<response type="doui_hyperLink">
<interfaceId>results</interfaceId>
<parameters>
<parameter name="id" />
</parameters>
</response>
</processAction>
</processActions>
A seguir, uma lista das respostas que podemos definir no atributo type:
doui_message | Exibe uma mensagem, cadastrada no arquivo de strings para cada idioma, que será traduzida adequadamente.
|
doui_refreshParent | Executa um refresh na interface pai da interface corrente, após a execução de determinado process action .
|
doui_closeWindow | Fecha a janela corrente, após a execução de determinado process action .
|
doui_runJavascript | Executa o javascript especificado em um nó <script/> dentro do response .
|
doui_setRequestParameters | Atribui todos os valores dos parâmetros correntes para o próximo render request da interface. |
doui_standardPopup | Utilizado em interfaces renderizadas em pop-ups. Internamente, ainda é convertido em dois tipos deresponses: o doui_refreshParent e doui_closeWindow. |
doui_standardEmbedded | Utilizado em interfaces embutidas em páginas (interfaces que a princípio deveriam ser rederizadas em pop-ups mas são renderizadas em páginas). É equivalente ao response doui_setRequestParameters. |
doui_standardCommit | Utilizado em interfaces padrão de adicionar e editar. Este response detecta se a interface corrente é um pop-upou uma interface embutida. Caso seja um pop-up, este response é equivalente ao doui_refreshParent seguido do doui_closeWindow. Caso seja uma interface embutida, o response irá detectar onde a interface de administração está localizada e redirecionará o portal para a página que contém esta interface. |
doui_setResponseParameters | Configura os parâmetros para os render requests da interface corrente.
|
doui_setRequestAttributes | Utilizado primeiramente para compartilhar dados entre diferentes process actions. Configura os valores dos parâmetros nos atributos request. Estes atributos podem ser lidos por process actions subsequentes que possuam o mesmo id.
|
doui_hyperLink | Utilizado para redirecionar o browser para outra página ou URL , depois da execução do process action em questão. Podem ser passados parâmetros no redirecionamento.
O exemplo acima refere-se ao redirecionamento do browser para uma página, utilizando a tag <interfaceId/>. Quando o destino for uma URL externa, deve-se utilizar o elemento <URL> . |
doui_popupInterface | Utilizado para abrir uma interface em um popup depois da execução de um process action.Além disso, parâmetros podem ser passados para a interface. |
doui_preview | Exibe um pop-up para a visualização do dado sendo editado. |
Configurando responses na sua Process Action Handler
Quando criamos uma Process Action Handler customizada e utilizamos um elemento <response> dentro do arquivo douidefinition, precisamos adicionar a chamada ao método addDefaultResponse() ao final do método processAction().
Por exemplo, se tivéssemos definido no nosso arquivo douidefinition o response doui_closeWindow para ser chamado após a Process Action Handler customizada ser executada:
<processAction id="commit" className="artigos.processAction.DefaultResponseProcessActionHandler" type="generic">
<response type="doui_closeWindow" />
</processAction>
O código Java necessitaria ao final do método, chamar o addDefaultResponse(). Caso não o chamasse, o doui_closeWindow não seria executado.
package br.com.lumis.artigos.processAction;
import lumis.doui.processaction.ProcessActionHandler;
import lumis.portal.PortalException;
public class DefaultResponseProcessActionHandler extends ProcessActionHandler {
public void processAction() throws PortalException {
// lógica da sua process action handler
// esse método fará com que os responses definidos no douidefinition sejam chamados
addDefaultResponse();
}
}
Passando parâmetros de uma Process Action Handler para outra
Muitas vezes precisamos passar parâmetros de uma process action handler para outra. Agora analisaremos dois casos possíveis: Um em que uma process action handler padrão do Lumis envia parâmetro para outra handler, e outro caso em que uma handler customizada envia parâmetro para outra handler customizada.
Caso 1: Setando atributo no response e lendo na process action handler
Quando o Lumis grava um determinado registro em uma tabela, ele automaticamente cria um atributo chamado itemId com o valor da chave primária do registro gravado. Em alguns casos, precisamos utilizar essa chave logo após o seu momento de criação, por exemplo, para fazer validações adicionais, gravar outros registros que dependam dessa chave etc.
No exemplo a seguir, imagine que tenhamos um cadastro de empresas. Logo após a adição de um registro, criamos um parâmetro de nome COD_EMPRESAM com o valor itemId. Por padrão, o Lumis irá substituir itemId pelo valor da chave primária do registro que foi gravado. Portanto, COD_EMPRESA receberá o valor da chave primária do registro. Este atributo é passado para a próxima process action handler customizada, que obtém o valor do atributo e pode executar operações adicionais.
<processActions>
<processAction id="commit" type="contentTableAddData">
<response type="doui_setRequestAttributes">
<parameter name="COD_EMPRESA">
<value type="attributeValue">itemId</value>
</parameter>
</response>
</processAction>
<processAction id="commit" className="artigos.processAction.LerAtributoProcessActionHandler" />
</processActions>
public class LerAtributoProcessActionHandler extends ProcessActionHandler {
public void processAction() throws PortalException {
String codEmpresa = (String)douiContext.getRequest().getAttribute("COD_EMPRESA");
addDefaultResponse();
}
}
Caso 2: Setando atributo na process action handler para ler na próxima handler
Neste caso, estamos dentro de uma process action handler e gostaríamos de passar uma informação para a próxima handler na cadeia. O exemplo a seguir mostra como podemos adicionar um atributo (que pode ser um objeto de qualquer tipo).
Para ler o atributo, podemos utilizar a classe do exemplo anterior (LerAtributoProcessActionHandler).
public class SetarAtributoProcessActionHandler extends ProcessActionHandler {
public void processAction() throws PortalException {
String codEmpresa = getCodEmpresa();
douiContext.getRequest().setAttribute("COD_EMPRESA", codEmpresa);
addDefaultResponse();
}
}
Chamando uma Process Action via Javascript
Opcionalmente, podemos chamar as Process Actions através de Javascript. O código a seguir modifica o campo doui_processActionId com o id da Process Action a ser chamada e submete o formulário.
<control:lum_okButton>
<onClick type="script">
<script>
<![CDATA[
var formName = '<lum:formName/>';
var arrTemp = new Array();
arrTemp = formName.split("_");
var strDestId = arrTemp[1];
document.forms[formName].elements['doui_processActionId'].value='commit';
LumisPortal.onSubmitForm(formName, strDestId, null, true);
]]>
</script>
</onClick>
</control:lum_okButton>
Boas Práticas
Vários métodos por Process Action Handler
Muitas vezes em uma mesma interface, podemos ter diferentes ações que necessitariam disparar a chamada para diferentes process actions. Exemplo:
<processAction id="adicionar" className="ClasseProcessActionHandlerAdicionar" />
<processAction id="editar" className="ClasseProcessActionHandlerEditar" />
<processAction id="excluir" className="ClasseProcessActionHandlerExcluir" />
No exemplo, o desenvolvedor teria criado três classes diferentes, uma para cada ação. Entretanto, o desenvolvedor pode achar interessante implementar apenas uma process action com vários métodos, sendo cada método responsável por tratar uma determinada ação. Exemplo:
<processAction id="adicionar" className="ClasseProcessActionHandler" type="adicionar" />
<processAction id="editar" className="ClasseProcessActionHandler" type="editar" />
<processAction id="excluir" className="ClasseProcessActionHandler" type="excluir" />
No exemplo, adicionamos o atributo type, que recebeu valores diferentes de acordo com a process action. Entretanto, a classe Java chamada é a mesma, e poderia ser implementada da seguinte maneira:
public void processAction() throws PortalException {
String processActionType = XmlUtil.readAttributeString("type", this.processActionNode);
if (processActionType == null)
throw new RuntimeException("Necessário informar a tag type do processAction");
if (processActionType.equals("adicionar"))
doAdicionar();
else if (processActionType.equals("editar"))
doEditar();
else if (processActionType.equals("excluir"))
doExcluir();
addDefaultResponse();
}
Não acessar o banco de dados diretamente a partir da Process Action Handler
Outra boa prática que podemos citar ao construir uma process action handler é que ela não deve fazer acesso direto ao banco de dados ou qualquer meio de persistência utilizado. Esta prática é recomendada para que não se misture a lógica da sua aplicação com a da persistência, possibilitando assim criar classes mais coesas (com responsabilidades claras). Um padrão que pode ser utilizado para fazer essa separação de responsabilidades é o DAO (Data Access Object):
"DAO (acrônimo de Data Access Object), é um padrão para persistência de dados que permite separar regras de negócio das regras de acesso a banco de dados. Numa aplicação que utilize a arquitetura MVC, todas as funcionalidades de bancos de dados, tais como obter as conexões, mapear objetos Java para tipos de dados SQL ou executar comandos SQL, devem ser feitas por classes de DAO." (http://pt.wikipedia.org/wiki/Data_Access_Object)
Exemplo de chamadas para DAO's a partir da process action:
public void processAction() throws PortalException {
String codAcao = getParameter("COD_ACAO", String.class);
AcaoDAO acaoDAO = new AcaoDAO(transaction);
Acao acao = acaoDAO.findById(codAcao);
AcaoRelIndicadoresDAO acaoRelIndicadoresDAO = new AcaoRelIndicadoresDAO(transaction);
acaoRelIndicadoresDAO.deleteByCodAcao(codAcao);
}
Particularidade de uma Process Action Handler em interfaces com renderização client-side-script
Nas propriedades avançadas de uma interface, podemos definir dois modos de renderização: Server-side e Client-side.
- Server Side: Propriedade default que define que quando o browser fizer uma requisição ao servidor web/servidor de aplicação, este fará uma requisição aos serviços e montará a página com tal interface;
- Client Side Script: Esta propriedade define que, quando o browser fizer uma requisição ao servidor web/servidor de aplicação, este retornará um javascript, que via tecnologia AJAX renderizará somente a interface, não sendo realizado um refresh na página inteira.
Se a interface executar alguma process action, é importante saber que será realizado o refresh da página inteira, devido ao fato do resultado da sua execução poder ser um redirect, ou ter impacto nas outras interfaces da mesma página. Caso a process action passe parâmetros de renderização, a interface não pode ser configurada como client-side.
Conclusão
Uma process action é um ponto de partida para a execução da lógica da sua aplicação. Atentem-se às boas práticas citadas no artigo, elas serão importantes para a organização do sistema desenvolvido. É importante conhecer bem os handlers e os responses existentes, pois muitas vezes acabamos criando uma process action customizada sem a real necessidade.
Autor: Aman Rathie (XTI)