O Struts 2 foi lançado no início de fevereiro de 2007 com uma infinidade de recursos interessantes. No escopo deste artigo, vamos nos concentrar em um dos acréscimos mais recentes à estrutura do Struts 2: a linguagem OGNL (Object-Graph Navigation Language). Esta é a mais nova forma de linguagens de expressão, e tem excelente suporte a coleta, passagem gráfica de objeto e indexação.
Definiremos as páginas da Web com base hierárquica e como podemos realizar a extração de dados utilizando o código mínimo da estrutura OGNL do Struts 2. Vamos analisar como importantes conceitos de negócios estão sendo implementadas na Web usando essas construções. Vamos demonstrar como a extração de dados dessas páginas era manuseada na estrutura anterior (Struts 1.x), e como migrar para a estrutura OGNL do Struts 2 ajuda a minimizar o esforço de desenvolvimento. Neste sentido, abordaremos também os conceitos básicos de OGNL e Struts 2 e como os aproveitamos para criar uma metodologia leve para lidar com páginas hierárquicas. Demonstramos também um estudo comparativo entre os recursos do Struts 1.x e do Struts 2, que ajudaram na migração para um ambiente com menos código.
Caso 1. Estudo de um cenário existente
Vamos levar em conta o seguinte cenário de exibição de dados hierárquicos multinível e atualização de dados em uma página da Web. Um consultor de investimentos está preparando um relatório para um cliente. A captura de tela mostra uma tela de atualização de dados para o portfólio de investimentos do cliente. Os campos de entrada de dados são atributos de nível superior do portfólio. As linhas em roxo são os itens de dados de primeiro nível sob o portfólio, e os campos Target Investment Amount and Target Profit% são atributos de cada um dos itens de primeiro nível. Da mesma forma, as linhas verdes são itens de segundo nível (e os campos sob elas são atributos nesse nível). Por fim, as linhas amarelas exibem itens de terceiro nível. Neste artigo, vamos supor que todos os campos podem ser atualizados e, quando o usuário envia a página, os dados no backend devem ser atualizados em conformidade. Note também que os números das linhas de primeiro, segundo e terceiro nível são dinâmicos por natureza.
Figura 1. Representação gráfica
Implementação atual para resolver o problema (com Struts 1.x)
A Listagem 1 mostra o modelo de dados da página acima.
Listagem 1. Estrutura de dados anterior
public class ClientPortfolioDetailsBean
{
//Domain model for the client portfolio details
//Portfolio level attributes
private String investorName = null;
private String emailId = null;
private String contactNumber = null;
//..... Other portfolio level attributes
//List to hold the details of Stocks / Mutual Funds etc investment category
private List <InvestmentCategoryDetailsBean> invCategoryList = null;
}
public class InvestmentCategoryDetailsBean {
//Bean to hold the information about investment category details
//like Stocks, Mutual Funds etc. Represents the first level rows
private String categoryName = null;
private double targetInvstAmt = 0.0;
private double expectedAnnualReturn = 0.0;
//Other attributes follows
//List of stocks and fund details bean to hold the
//second level items
private List <StocksAndFundDetailsBean> itemList = null;
}
public class StocksAndFundDetailsBean {
//Bean to hold the information about investment category details
//like Stocks, Mutual Funds etc. Represents the second level rows
//Attributes at that level
private String stockFundCategoryName = null;
private double targetInvstAmt = 0.0;
private double targetProfit =0.0;
//Other attribute follows
//List to hold the stock details
private List <StocksDetailsBean> stockDetailsList = null;
}
public class StocksDetailsBean {
//Represents the third level item details
//Details of individual stock/fund details
private String stockFundName = null;
private double noOfStocks = 0.0;
private double buyingPrice =0.0;
private Date buyingDate = null;
}
|
No Struts 1.x, a solução para o primeiro estudo de caso exige a definição de um form bean em um modo de nível único e simples. Para obter os valores atualizados inseridos pelo usuário, a Listagem 2 fornece a estrutura necessária de form bean.
Listagem 2. Estrutura de form beans do Struts 1.x
public class PortfolioDetailsBean extends ActionForm
{
//Top level attributes
private String investorName = null;
private String emailId = null;
//........... Other Top level attributes
//Fields to capture Attributes of First level rows
private double[] firstLevelTargetAmt = null;
private double[] firstLevelExpectedAnnlReturn = null;
//......... Other First Level row attributes
//Fields to capture Attributes of Second level rows
private double[] secondLevelTargetAmt = null;
private double[] secondLevelTargetInvPeriod = null;
//......... Other Second level row attributes
//Fields to capture Attributes of Second level rows
private double[] thirdLevelNoOfStocks = null;
private double[] thirdLevelByingPrice = null;
//......... Other Third level row attributes
}
|
Para converter esses elementos de form bean de nível único no modelo de domínio multinível mostrado na Listagem 1, a classe de ação requer codificação adicional, como mostrado na Listagem 3. Note que a lógica aqui aplicada depende da estrutura do modelo de domínio e, à medida que o nível e número de atributos aumentam, a complexidade da conversão aumenta na classe de ação.
Listagem 3. Código personalizado para converter elementos de form bean de nível único em modelo de domínio multinível
public class UpdatePortfolioAction extends Action
{
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
ActionErrors errors = new ActionErrors();
ActionForward forward = new ActionForward(); // valor de retorno
PortfolioDetailsBean portfolioDetailsBean = (PortfolioDetailsBean) form;
try {
//Counter to track index of 2nd level index
int counter2ndLevelIndex= 0;
//Converting the form bean to the domain model
ClientPortfolioDetailsBean clientBean = new ClientPortfolioDetailsBean();
//Setting the top level attributes
clientBean.setEmailId(portfolioDetailsBean.getEmailId());
//.........................................
//Similarly other top level fields are created
//Get the size of first level row sizes from session
int sizeInvestmentCategory = getInvestmentCategorySize
(request.getSession());
clientBean.setInvCategoryList(new ArrayList
<InvestmentCategoryDetailsBean>(sizeInvestmentCategory));
for(int firstLevelIndex=0;firstLevelIndex<
sizeInvestmentCategory;firstLevelIndex++)
{
//Create the updated first level bean
InvestmentCategoryDetailsBean invCatBean =
new InvestmentCategoryDetailsBean();
//Set the investment category Bean in the parent bean
clientBean.getInvCategoryList().add(invCatBean);
//set the updated properties
invCatBean.setExpectedAnnualReturn(
portfolioDetailsBean.getFirstLevelExpectedAnnlReturn()[firstLevelIndex]);
//.........................................
//Set the other properties at first level
//Get the size of 2nd level row sizes from session
int sizeStockAndFundDetails = getStockAndFundDetailsSize
(request.getSession(),firstLevelIndex);
invCatBean.setItemList(
new ArrayList<StocksAndFundDetailsBean>(sizeStockAndFundDetails));
//Fill Up the second level objects
for(int secondLevelIndex=0;secondLevelIndex<
sizeStockAndFundDetails;secondLevelIndex++)
{
StocksAndFundDetailsBean stkFundDetails =
new StocksAndFundDetailsBean();
//Set the object to the parent Bean
invCatBean.getItemList().add(stkFundDetails);
//Set the individual attributes
stkFundDetails.setTargetInvstAmt(
portfolioDetailsBean.getSecondLevelTargetAmt()[counter2ndLevelIndex++]);
//............... Other fields follows
// Setting up of 3rd level list follows here
//......................................
}
}
} catch (Exception e) {
// Report the error using the appropriate name and ID.
}
}
|
Uma das tarefas tediosas do desenvolvimento de aplicativos da Web é a transferência de dados de form beans para beans de dados. Converter dos tipos Strings para Java, e vice-versa, aumenta a complexidade. É necessário analisar os valores da cadeia de caractere em duplas e números inteiros, além de resolver as exceções que possam surgir a partir de dados incorretos.
Transferências de dados e conversões de tipo acontecem em ambas as extremidades do ciclo de processamento do pedido. Quando o resultado é renderizado, os dados são reconvertidos do tipo Java de volta para um formato de cadeia de caractere. Esse processo acontece com quase todos os pedidos em um aplicativo da Web, e é parte integrante do domínio. Apoiar formas dinâmicas com conjuntos dinâmicos de registros requer códigos e lógica complicados. O desenvolvimento e a manutenção desse código de lógica tornam-se complicados.
A automação da transferência de dados e da conversão de tipo é um dos recursos mais poderosos do Struts 2. Com a ajuda de OGNL, a estrutura do Struts 2 permite a transferência de dados para tipos do lado de Java mais complexos, como Lista, Mapa, etc. Os conversores personalizados também podem ser desenvolvidos para estender o mecanismo de conversão de tipo, e pode lidar com qualquer tipo de dados, incluindo tipos definidos pelo usuário.
O OGNL é a interface entre a saída e entrada HTTP baseada em cadeia de caracteres da estrutura do Struts 2 e o processamento interno baseado em Java. Para desenvolvedores familiarizados com OGNL, ela melhorará muito a eficiência e reduzirá os problemas com a manutenção.
Figura 2. Pilha de OGNL
Da perspectiva do desenvolvedor, a OGNL possui dois componentes:
- Linguagem de expressão — Normalmente usada em nomes de campo de entrada de formulário e tags JSP. As expressões de OGNL são usadas para ligar propriedades de dados do lado de Java a cadeias de caractere nas camadas de visualização baseadas em texto.
- Conversores de tipo — Responsáveis pela conversão de tipos de dados. Cada vez que dados se movem de ou a um ambiente Java, deve ocorrer uma conversão entre a versão de cadeia de caractere dos dados que residem em HTML e o tipo de dados Java adequado. A estrutura oferece conversores integrados para lidar com muito mais do que solicitamos, ou o desenvolvedor tem a opção de criar conversores personalizados.
A estrutura transfere automaticamente dados e faz a conversão de dados dos parâmetros do pedido, mas para onde vão os dados e como a OGNL descobre o destino? ValueStack no Struts 2 é a construção que é um aglomerado de propriedades de objetos como propriedades de um único objeto virtual. Em caso de propriedades duplicadas (ou seja, dois objetos na pilha com a propriedade employeeId ), será usada a propriedade do objeto mais alto na pilha. A estrutura do Struts 2 tem suporte integrado para a conversão entre as cadeias de caractere nativas de HTTP e a seguinte lista de tipos Java:
- Array
- booleano/Booleano
- Car/Caractere
- int/inteiro, flut/Flutuante, longo/Longo, duplo/Duplo
- Data
- Cadeia de caractere
- Mapa
- Lista
Usando OGNL para resolver dificuldades de transformação
As expressões de OGNL do Struts 2 reduzem a dificuldades de escrever um código complexo para transformar form beans de nível único em objetos de domínio multinível. No Struts 2, os nomes de campos de entrada em HTML podem ser gerados como expressões de OGNL, as quais, por sua vez, ajudam a eliminar codificação complexa na classe de ação. Como exemplo, se um nome de campo de entrada em HTML (como uma caixa de texto) for definido como clientPortfolioDetailsBean.investorName, o getClientPortfolioDetailsBean() da classe de ação será chamado. Isso deve retornar o ClientPortfolioDetailsBean, seguido pela chamada do método setInvestorName() com o valor fornecido pelo usuário na tela. A nomenclatura inteligente de campos de entrada em HTML atualizados pelos usuários rejeita a escrita de código complexo na classe de ação. O modelo de domínio é preenchido adequadamente dentro da classe de ação.
Precisamos acrescentar um tipo de atributo ClientPortfolioDetailsBean à classe de ação, juntamente com seu método get. Dentro do método get, um bean deve ser retornado da sessão se sua instância não estiver configurada na instância de classe de ação atual. Durante o carregamento da página, a instância de bean detalhada pelo cliente deve ser mantida no objeto de sessão. No caso de objetos maiores, o método getClientDetailsFromSession() pode ser substituído por getClientDetailsFromDB(), no qual os dados devem ser obtidos a partir do banco de dados.
Listagem 4. Fragmento de código do Caso 1
public class ClientPortfolioAction extends ActionSupport {
private ClientPortfolioDetailsBean clientDetailsBean = null;
//Accessor for the clientDetailsBean.
public ClientPortfolioDetailsBean getClientDetailsBean() {
if(clientDetailsBean==null)
{
//If the bean is null for this action class instance
//load the data from the session
clientDetailsBean = getClientDetailsFromSession();
}
return clientDetailsBean;
}
}
|
Listagem 5. Fragmento de código do Caso 2
<%@ taglib prefix="s" uri="/struts-tags" %>
<s:textfield name="clientDetailsBean.investorName"
value="%{clientDetailsBean.investorName}"/%>
<s:textfield name="clientDetailsBean.emailId"
value="%{clientDetailsBean.emailId}"/%>
<s:textfield name="clientDetailsBean.contactNumber"
value="%{clientDetailsBean.contactNumber}"/%>
<s:iterator value="clientDetailsBean.invCategoryList"
var="invCatDetails" status="firstLevelIdx"%>
<s:property value="%{#invCatDetails.categoryName}"/%>
<s:textfield name="clientDetailsBean[%{#firstLevelIdx.index}]
.targetInvstAmt"
value="%{invCatDetails.targetInvstAmt}"/%>
<s:textfield name="clientDetailsBean[%{#firstLevelIdx.index}]
.expectedAnnualReturn"
value="{invCatDetails.expectedAnnualReturn}"/%>
<s:iterator value="#invCatDetails.itemList"
var="stkFundDetails" status="secondLevelIndx"%>
<s:property value="%{#stkFundDetails.
stockFundCategoryName}"/%>
<s:textfield name="clientDetailsBean
[%{#firstLevelIdx.index}].itemList[%{secondLevelIndx.index}].
targetInvstAmt" value="%{#stkFundDetails.targetInvstAmt}" /%>
<s:textfield name="clientDetailsBean[%{#firstLevelIdx.index}]
.itemList[%{secondLevelIndx.index}].
targetProfit" value="%{#stkFundDetails.targetProfit}" /%>
<s:iterator value="#stkFundDetails.stockDetailsList"
var="stkDetails" status="thirdLevelIndx"%>
<s:property value="%{stkDetails.stockFundName}" /%>
<s:textfield name="clientDetailsBean[%{#firstLevelIdx.index}]
.itemList[%{secondLevelIndx.index}].stockDetailsList
[%{thirdLevelIndx.index}].noOfStocks" value="%{#stkDetails.noOfStocks}" /%>
<s:textfield name="clientDetailsBean[%{#firstLevelIdx.index}]
.itemList[%{secondLevelIndx.index}].stockDetailsList
[%{thirdLevelIndx.index}].buyingPrice" value="%{#stkDetails.buyingPrice}"/%>
</s:iterator>
</s:iterator>
</s:iterator>
|
Agora, vamos analisar uma página JSP na qual os nomes de caixa de texto de entrada são gerados por expressões de OGNL válidas, e como o campo "N° de ações" da "ABC Incorporated" é atualizado pela expressão de OGNL:
<s:textfield
name="clientDetailsBean[%{#firstLevelIdx.index}]
.itemList[%{secondLevelIndx.index}].stockDetailsList
[%thirdLevelIndx.index}].noOfStocks}'/> |
Essa linha será convertida no seguinte código HTML:
<input type="text"
name="clientDetailsBean[0].itemList[0].stockDetailsList[0].noOfStocks"
value="<somevalue>" /> |
A expressão de OGNL clientDetailsBean[0].itemList[0].stockDetails[0].noOfStocks
é equivalente à seguinte chamada de código:
getClientDetailsBean().getItemList().get(0).getStockDetailList(0).get(0).setNoOfStocks(<some
value>) na instância de classe de ação. De modo similar, outros campos serão atualizados correspondentemente no modelo de domínio. No método de ação, se getClientDetailsBean() for chamado, todos os campos serão atualizados pela estrutura do Struts 2 em vigor. Isso permite evitar a codificação complexa na classe de ação (como mostrado no exemplo do Struts 1) para converter o form bean no objeto relacionado de modelo de domínio.
As vantagens da estrutura do Struts 2 incluem:
- A linguagem de expressão do OGNL permite mapear campos de formulário em propriedades do lado de Java.
- Os conversores de tipo de OGNL convertem automaticamente os dados (em forma de cadeia de caractere) de parâmetros de pedido para os tipos Java reais das propriedades.
- Ao renderizar a visualização, a linguagem de expressão e o conversor de tipo convertem novamente de tipos Java para o valor de cadeia de caractere de forma automática.
- Suporta um conjunto flexível e amplo de conversões de e para Coleta e Array.
- Permite o desenvolvimento de Web sites complexos, o que reduz a complexidade dos códigos de backend.
- Reduz o tempo de desenvolvimento da transferência automática de dados reutilizáveis.
- Redução significativa de código tedioso para conversão de dados e manipulação de exceções, permitindo concentrar-se na lógica básica de negócio.
Neste artigo, discutimos o aspecto OGNL do Struts 2, e como ele auxilia os profissionais a implementar aplicativos de negócios mais ágeis. Em artigos futuros, revisaremos o Struts 2 a partir de um ponto de vista do desenvolvimento. O Struts 2 veio para ficar, pois torna-se cada vez mais estável, e esperamos ajudá-lo a aproveitar ao máximo os benefícios dos seus recursos aprimorados.
Aprender
-
Saiba mais sobre o Struts 2 e estatísticas de Internet.
-
Visite a Struts 2.Net.
-
Para ouvir entrevistas e discussões interessantes para desenvolvedores de software, confira os Podcasts do developerWorks
.
-
Fique por dentro dosEventos técnicos e webcasts do developerWorks.
-
Siga o DeveloperWorks no Twitter.
-
Confira conferências, feiras, webcasts e outros eventos que estão para acontecer no mundo todo e que são do interesse de desenvolvedores de software livre da IBM.
-
Visite a Zona de software livre para obter informações detalhadas sobre instruções, ferramentas e atualizações de projetos para ajudar você a se desenvolver com tecnologias de software livre e usá-las com produtos da IBM, bem como nossos artigos e tutoriais mais populares.
-
Assista e aprenda sobre a IBM e tecnologias de software livre e funções de produto com as demos gratuitas on demand do developerWorks.
Obter produtos e tecnologias
-
Inove seu próximo projeto de desenvolvimento de software livre com Versão de teste do software IBM, disponível para download ou em DVD.
- Faça o download das Versões de avaliação de produto IBM
ou explore as versões de teste on-line no IBM SOA Sandbox e entre em contato com as ferramentas de desenvolvimento de aplicativos e produtos de middleware do DB2 ®, Lotus®, Rational®, Tivoli®e WebSphere®.
Discutir
-
Participe dos Blogs do developerWorks
e participe da comunidade do developerWorks.
-
Participe da comunidade do developerWorks. Entre em contato com outros usuários do developerWorks, enquanto explora os blogs, fóruns, grupos e wikis orientados ao desenvolvedor.
Rudranil Dasgupta é IT Architect associado à IBM e IT Specialist autorizado em consultoria, que trabalha como líder de projeto no segmento de mercado de eletrônica. Ele possui quase sete anos de experiência no projeto e desenvolvimento, arquitetura, estratégias técnicas, soluções para a criação e aproveitamento de ativos em solução de cliente, e fornecimento de liderança técnica. Ele possui profunda experiência no trabalho com soluções WebSphere, SOA, Web 2.0, gerenciamento de conteúdo e Java/J2EE. Ele possui múltiplas publicações em fóruns IBM, incluindo no IBM developerWorks. Ele também possui diversas sessões sobre Web 2.0 e colaboração social em fóruns reconhecidos.
Kaushik Dutta é arquiteto de aplicativos e atualmente trabalha como arquiteto técnico para a GBSC Asset Reuse Enablement Initiative. Ele possui mais de 10 anos de experiência profissional em projeto de software, desenvolvimento, análise e soluções de arquitetura. Ele esteve envolvido na análise, arquitetura, e projeto e desenvolvimento de sistema de aplicativos da Web utilizando a tecnologia Java, J2EE, EJB, SOA, Web 2.0, WebSphere Adapter, JMS, serviços da Web, JSP, Servlet, JDBC, RMI, XML, Oracle, HTML e JavaScript.
Sudip Dutta é desenvolvedor senior no segmento de mercado de eletrônica. Ele possui aproximadamente sete anos de experiência no projeto e desenvolvimento, criação de protótipos, fornecimento de estratégias técnicas, soluções para a criação e aproveitamento de ativos em solução de cliente, bem como no fornecimento de liderança técnica. Ele possui profunda experiência no trabalho com soluções WebSphere, SOA, Web 2.0, Java/J2EE, .NET e soluções DB2. Ele tem mestrado em aplicativos de computador e bacharelado em matemática.