terça-feira, 4 de outubro de 2011

JavaOne 2011 Day 2


Hoje foi o dia que realmente comecou o JavaOne 2011 - que comecou com um keynote em um auditorio lotado e com mais 2 auditorios de backup - onde os keynotes estavam sendo transmitidos simultaneamente para quem nao conseguiu espaco no auditorio principal. Um pouco antes de comecar, os teloes do auditorio mostravam os vencedores do Duke's Choice Awards desse ano (um premio dado a diversos projetos inovadores escolhidos pela comunidade), entre eles o projeto jHome que de alguma forma esta ligado a Globalcode (mas nao consegui nenhuma informacao extra).

O keynote eh iniciado por Adan Messinger (embora o anunciador tenha chamado Richard Bair, o que ficou bem estranho) falando um pouco sobre a plataforma e sobre java no cloud (mais sobre isso no final), para logo depois chamar um diretor da Intel para falar sobre o relacionamento entre a Sun/Oracle e a Intel - uma cooperacao que vem melhorando tanto o hardware quanto a JVM. Tambem foi anunciado o JavaOne blog. Por ultimo, foi anunciado um produto da Oracle que eu nao esperava: o Oracle NoSQL Database - um banco de dados NoSQL.
Logo depois, subiu ao palco Marc Reinhold - basicamente o chefe do JavaSE - falando sobre as features do java 7 e as features propostas do java 8 e java 9 e para anunciar que daria mais detalhes sobre essas versoes no keynote de estrategia de amnha. Sobe no palco, finalmente, Richard Bair para anunciar a disponibilidade da versao 2.0 do JavaFX (apenas para windows por enquanto) e a disponiblidade de uma versao beta do netbeans com suporte ao mesmo. Logo depois, foi a vez de Linda DeMichiel falar sobre JavaEE e as facilidades do JavaEE7 para o cloud, bem como pequenas melhorias na spec. Por ultimo, sobe ao palco Hinkmond Wong para falar sobre JavaME e metade do auditorio deixa a sala (para ser justo, o keynote jah estava 10 minutos alem do tempo previsto), mostrando que JavaME eh, provavelmente, irrelevante atualmente.

Com isso, iniciaram-se as technical sessions:

Arquillian the Extensible Enterprise Platform - Aslak Knutsen e Dan Allen da RedHat
Os palestrantes mostraram o Arquillian, uma plataforma de testes extensivel vencedora de um Duke's Choice Award que permite executar testes nos servidores de aplicacao (iniciando e fazendo deploy, se necessario). Achei a ideia muito interessante e algo que definitivamente olharei com mais cuidado - tambem irei assistir a demonstracao completa amanha). Integra as ferramentas normais de teste (JUnit e TestNG) e ainda pode ser integrado ao maven.

Coroutines for the JVM - Lukas Stadler da Johannes Kepler University, Linz, Austria
Coroutines (tambem conhecido como fibers em ruby ou generators em python) eh uma forma limitada de continuations. Resolvi ver como essa feature foi implementada em Java (que nao possui maneiras nao nativas de manipular o stack) e como essa features poderia ser aproveitada. O palestrante mostrou que conseguiu implementar uma versao praticamente sem limitacoes (inclusive com transporte de coroutines entre threads) e como, em geral, coroutines sao mais eficientes que threads. A feature, porem, esta em estado bem experimental e nem tem uma JSR ainda e deve entrar na JVM com sorte apenas na versao 9.

Project Lambda: To Multicore and Beyond - Alex Buckley e Daniel Smith da Oracle
Uma feature que deve vir na JDK8, lambdas sao uma forma mais simples do que closures - ou melhor, algo muito parecido. Para quem segue a lista de desenvolvimento de lambda, ha poucas novidades - mas uma delas meio bombastica: JDK8 trara (muito provavelmente) o que eles chamaram de virtual extension methods - essencialmente trazendo as traits de scala para java - como uma feature da JVM (e nao apenas do compiler, como eh o caso de scala). Nao sei quanto as pessoas se importam com isso, mas basicamente teremos uma forma limitada de heranca publica em java. Com essa feature, teremos adicao de varios metodos nas interfaces basicas de collections, permitindo manipulacao e iteracao das collections de forma paralela (usando o framework Fork-Join jah presente no JDK7).

Why Doesn't Java Have Instant Turnaround? - Jevgeni Kabanov da Zero TurnAround
Quase uma propaganda do produto principal da empresa (o JRebel), mas uma boa palestra de qualquer maneira (alias que ainda nao peguei nenhuma palestra de baixa qualidade). JRebel promete que quase nunca sera necessario fazer o ciclo de build / redeploy cada vez que voce mudar algo na aplicacao. Na MAPS, temos usado com sucesso o Jetty rodando de dentro do Eclipse no projeto, com praticamente o mesmo efeito. No fim, o palestrante mencionou um novo produto da empresa, dessa vez para diminuir o tempo de deployment de aplicacoes em producao - o que me deixou bem interessado.

JVM Bytecode for Dummies - Charles Oliver Nutter da EngineYard
Uma palestra bem tecnica de como gerar bytecode e como ele eh interpretado na JVM. Charles inclusive escreveu uma DSL (em ruby) para geracao de bytecode e mostrou uma biblioteca java parecida. Fora isso, os exemplos foram dados usando ASM.

Exibition Hall
Muitos dos expositores eram de empresas como a EngineYard: prestam servicos provendo plataforma de deployment e/ou facilitando deployment em ambientes de cloud. Claramente, o mundo java corporativo esta se movendo na direcao da nuvem - onde apropriado, claro.

Amanha teremos mais um dia recheado de boas palestras.

domingo, 2 de outubro de 2011

JavaOne 2011 Day 1

Este eh o primeiro de uma serie de reports sobre o que aconteceu no JavaOne 2011 (da minha perspectiva claro)


Hoje o dia foi dedicado a inscricao e recolhimento de material. Para o JavaOne, soh teve atividades para quem assinou o Java University - uma serie de cursos sobre Java. Eu achei muito basicos os cursos entao acabei nao assinando.
Mas como congressista do JavaOne tem acesso ao Oracle Open World, resolvi assistir algumas palestras (mas consegui entrar em apenas 3) e tentar conversar com alguns JUG leaders.

Key to Successful Implementations: Communication and Mandatory Education

Palestra ministrada por um consultor de Rotterdan envolvido em implantacao de varios sistemas de ERP na cidade, Gerard Stan prega que qualquer organizacao deve manter um programa obrigatorio de educacao dos seus funcionarios (com o slogan No Education, No Access) e mostrou uma ferramenta para ajudar no gerenciamento de cursos e treinamento (mas acho que soh esta disponivel para quem eh cliente do Oracle E-Business Solution). Achei a ideia da palestra bem interessante, mas nada que ja nao estamos fazemos na MAPS.

Highly Available Oracle Database: The Unknown Details

Ministrada por uma Database Architect da universidade de Utah, esta palestra mostrou varias praticas e ferramentas para se manter Alta Disponibilidade em um servidor de Banco de Dados. Esta palestra foi bem estranha - parecia muito com uma propaganda das funcionalidades de HA existentes no Oracle 11g/i. Mas algumas parecem interessantes (como o flashback, restore points manual ou automaticamente gerados) e algumas tecnicas tambem (como por exemplo, apenas ligar o SafeGuard e o FlashBack no servidor de backup para nao comprometer o servidor de producao).

Managing Your Oracle Applications in Today's Economy: Ask the Experts

Mais um painel de discussao do que uma palestra propriamente dita, esta palestra (liderada por um funcionario da IBM) falou muito sobre gerenciamento de dados e governanca de informacoes dos aplicativos da Oracle - mas acho que o conceito se aplica a qualquer software de grande porte que gera muitos dados. (por exemplo, o Pegasus). Os demais participantes do painel eram parceiros Oracle ou IBM que adotaram ou participaram da adocao das praticas de gerenciamento de informacoes discutidas. Sao elas:
-clonar ou nao banco de dados de producao para desenvolvimento / testes de atualizacao: a discussao girou em torno da criacao de um banco 'dummy' com dados parecidos com a producao mas de menor tamanho ou ainda mascaramento dos dados sensiveis. Um dos palestrantes comentou que a TravelPort conseguiu economizar quase 40% dos custos de storage do banco de dados (que usava a infra-estrutura da Oracle on-demand) usando bancos de dados dummy para desenvolvimento e testes ao inves de clones da producao.
-arquivamento de dados "mortos": softwares e processos para arquivamento de dados para mitigar os prejuizos causados pelo crescimento excessivo de dados. Os palestrantes sugeriram a criacao de um grupo para gerenciamento de tais dados arquivados e mostrou algumas ferramentas da IBM para ajudar. Este painel foi mais voltado para os clientes das aplicacoes de grande porte e menos para desenvolvedores (tanto eh que fui perguntado o que eu fazia ali).

Agora esta tendo o keynote do Larry Ellion mas precisei voltar ao hotel e me preparar para amanha, quando as palestras tecnicas do JavaOne comecam.

sábado, 18 de junho de 2011

Algumas ferramentas da JDK

Algumas pessoas veem me perguntar às vezes quais ferramentas usar para diagnosticar uma aplicação que não está se comportando como deveria. Na maioria das vezes é possível diagnosticar uma aplicação em execução apenas com as ferramentas que vem junto com a JDK mas que muitas pessoas desconhecem mas que (na minha opinião) todos deveríamos conhecer. A maioria das ferramentas abaixo está presente tanto na JDK5 quanto na JDK6 e a maioria delas precisa ser executada com o mesmo usuário que executa o processo. Por último, os comandos abaixo são da versão solaris da JDK e podem ter alguma diferença se executadas em outros sistemas operacionais.

JPS
Parecida com o ps dos UNIX'es, mas lista apenas os processos java em execução, mostrando o pid (o process id) e o nome da classe main que iniciou o processo. Com a opção '-v', mostra toda a linha de comando usada para iniciar o processo e com a opção '-l' mostra o nome completo da classe main.

JSTACK
Uma vez identificado o pid da aplicação, precisamo ver o que acontece com ela. O comando jstack mostra na tela um stacktrace de todas as threads da aplicação e verifica se há algum deadlock entre as threads. Além disso, é possível identificar se alguma thread está parada em algum ponto ou esperando por I/O.

JINFO
jinfo mostra todas as sytem properties e todas as flags da jvm.

JMAP
jmap mostra todas as bibliotecas ligadas à sua aplicação, bem como o offset de memória em que estão mapeadas. Esta informação não é tão útil a menos que estejamos tentando identificar a causa de um crash da jvm. Normalmente usaremos jmap -heap para ver o estado da memória separado por região incluindo o permgen. Caso um problema de memória seja identificado, é possível gerar um histograma com os contadores de todos as instâncias dos objetos vivos com a flag -histo ou ainda gerar o heap dump com a flag -dump:.

JCONSOLE
Se tudo mais falhar, é possível tentar usar uma ferramenta gráfica chamada jconsole que se conecta à um processo java e consegue ver todas as informações anteriores e ainda inspecionar objetos expostos via JMX.

Como pudemos ver, é possível obter várias informações sobre uma aplicação em execução (e possivelmente diagnosticar problemas) apenas usando as ferramentas que vem junto com a JDK.

segunda-feira, 4 de outubro de 2010

OutOfMemoryError explicado

A resposta para o problema anterior passa por duas explicações:

A pilha de execução
Todos os operadores na máquina virtual operam sobre elementos da pilha de execução. Isso significa que, por exemplo, a expressão abaixo:


int x = 1 + 2;


se torna algo como:

empilha 1; // a pilha agora contem 1
empilha 2; // a pilha agora contem 1, 2
soma; // os 2 elementos da pilha sao desempilhados e somados. A pilha contem 3
guarda em x; //o 3 eh desempilhado e guardado em x


Parecido com assembly ASM.


Variáveis locais

Variáveis locais (e argumentos de métodos) são guardados em uma "tabela" por índice, na ordem em que são declaradas em um método. Ou seja:


public static void foo() {
int i = 0;
int j = 1;
}


A variável 'i' ocupa o índice 0 da tabela de variáveis locais e a variável 'j' ocupa o índice 1. Se o método é não estático, a referência ao objeto (o 'this') ocupa o índice 0. Se o método possui argumentos, ele ocupa os índices subsequentes. Assim sendo:



public void foo(String arg0) {
int i = 0;
int j = 1;
}


O 'this' ocupa o índice 0, o 'arg0' ocupa o índice 1 e assim por diante. Quando uma variável local sai de escopo, o compilador reutiliza o índice daquela variável para a próxima declaração de variável local. Em outras palavras, no código abaixo:


public static void foo() {
{
int i = 0;
}
int j = 0;
}


a variável 'j' ocupa o índice '0' da tabela pois a variável 'i' saiu de escopo.

O problema
Sabendo disso, voltando ao problema original:


public static void exec(int size) {
{
byte[] someData0 = new byte[size];
process(someData);
}
//final do bloco
byte[] someData1 = new byte[size]; // #
process(someData);
System.out.println("ok");
}


Quando declaramos a segunda variável local 'someData1', o código a ser executado fica mais ou menos assim (em pseudo bytecode):


empilha size; // a pilha contem size e o slot 1 contem o array antigo ainda
cria_array_bytes; // desempilha um inteiro e cria um array de bytes com o tamanho passado
guarda_em_1; // sobrescreve a variável local anterior


Quando a instrução de criar um novo array de bytes é executado, a JVM lança uma exceção dizendo que não há mais espaço. Isso significa que o código, em bytecode, fica parecido com:


public static void exec(int size) {

byte[] someData0 = new byte[size]; // #1
process(someData);

//final do bloco
someData0 = new byte[size]; // #2
process(someData);
System.out.println("ok");
}


e o array criado em #1 só pode ser coletado depois que a instrução em #2 for executada - ou seja, tarde demais.
Isso explica também porque os outros exemplos funcionam corretamente. Na verdade, podemos perceber que basta declarar uma variável local qualquer entre as criações dos arrays para que o primeiro array seja coletado. De fato, o código abaixo funciona corretamente:


public static void exec(int size) {
{
byte[] someData0 = new byte[size];
process(someData);
}
int j = 0;
//final do bloco
byte[] someData1 = new byte[size]; // #
process(someData);
System.out.println("ok");
}


Conclusão
Não há nada de errado, tecnicamente, com o GC e nem com o código postado. Apenas (no bytecode) não estamos avisando que o primeiro array pode ser coletado, talvez isso devesse ser trabalho do compilador. Porém, na maioria dos casos, inserir uma instrução para anular as variáveis locais quando elas saem de escopo é desnecessário.

PS:
Estes artigos são meramente traduções/adaptações de um artigo que pode ser visto no java specialists newsletter. O artigo original e a resposta se encontram aqui e aqui.
A versão JIT-compiled da classe funciona normalmente devido a alguma otimização da JVM. Isso pode ser visto ao executar a classe com o modo 'compiled' ao invés do modo híbrido (o default) usando a flag -Xcomp.
Qualquer 'branching' (como um if ao invés do escopo vazio) vai fazer com que a JVM execute o código como esperado (sem a exceção), mesmo no modo interpretado.
Por último, alguns GC's mais espertos (como o G1) também são capazes de identificar o primeiro array como lixo e coletar corretamente.

sexta-feira, 24 de setembro de 2010

OutOfMemoryError (??)

Embora o título desse post possa parecer estranho, ele refere-se a um OutOfMemoryError que pode ocorrer em uma situação (bem específica é verdade), mas que aparentemente não deveria. Sem mais delongas, segue o código:


public class Foo {

public static void exec(int size) {
{
byte[] someData = new byte[size];
process(someData);
}
//final do bloco
byte[] someData = new byte[size];
process(someData);
System.out.println("ok");
}

private static void process(byte[] data) {
}

public static void main(String[] args) {
exec((int) (0.7 * Runtime.getRuntime().maxMemory()));
}
}


O código acima lança uma OutOfMemoryError: java heap space, muito embora não devesse, já que o objeto 'someData' sai de escopo após a linha 7, quando o bloco termina. A primeira impressão que pode ficar é, como as duas alocações estão próximas, não dá tempo para o GC limpar a memória. Tentaremos, então, chamar explicitamente o GC para ser executado no meio tempo:



public class Foo {

public static void exec(int size) {
{
byte[] someData = new byte[size];
process(someData);
}
//final do bloco
System.gc();
byte[] someData = new byte[size];
process(someData);
System.out.println("ok");
}

private static void process(byte[] data) {
}


public static void main(String[] args) {
exec((int) (0.7 * Runtime.getRuntime().maxMemory()));
}
}


Mesmo desse jeito, o código continua com o OutOfMemoryError. Algumas pessoas familiarizadas com o funcionamento do GC podem afirmar que chamar System.gc() não necessariamente implica no GC ser executado de verdade. Mas o código abaixo mostra(?) que, se formos insistentes, a memória é coletada apropriadamente:



public class Foo {

public static void exec(int size) {
{
byte[] someData = new byte[size];
process(someData);
}
//final do bloco
for(int i = 0; i < 10; i++) System.gc();

byte[] someData = new byte[size];
process(someData);
System.out.println("ok");
}

private static void process(byte[] data) {
}

public static void main(String[] args) {
exec((int) (0.7 * Runtime.getRuntime().maxMemory()));
}
}


E também podemos ver que gerenciando "manualmente" a memória (ou seja, anulando a variável someData ao final do bloco), o código funciona normalmente:



public class Foo {

public static void exec(int size) {
{
byte[] someData = new byte[size];
process(someData);
someData = null;
}
//final do bloco
byte[] someData = new byte[size];
process(someData);
System.out.println("ok");
}

private static void process(byte[] data) {
}


public static void main(String[] args) {
exec((int) (0.7 * Runtime.getRuntime().maxMemory()));
}
}


Por que será que se insistirmos, o problema some? Mais do que isso, há um problema com o gerenciamento de memória da VM? Devemos esquecer que o GC existe e gerenciar a coleta de objetos manualmente, anulando todas as variáveis quando elas deixam de ser usadas?

terça-feira, 6 de outubro de 2009

Struts 2 com Annotations

Apesar de ser um assunto não muito atual, com bastante documentação, eu tive muito trabalho para escrever minha primeira aplicação com Struts 2. O site do projeto tem muito conteúdo, mas nada mais prático para começar um projeto do zero. Em minha busca atrás de tutoriais, algo que me ajudasse, achei uma quantidade razoável de artigos/tutoriais, porém, nenhum que funcionasse efetivamente, principalmente utilizando annotations. Fiz o download de vários exemplos, uns funcionavam, outros não, mas não consegui fazer minha aplicação funcionar lendo apenas um tutorial. Dada essa dificuldade, pretendo colocar em prática o que eu aprendi, e, quem sabe, se algum desesperado precisar aprender uma aplicação simples utilizando Struts 2, esse tutorial funcione :-)

Para começar, precisamos montar nosso ambiente, vamos precisar do Eclipse, e do TomCat plugin.

Nosso projeto ficará mais ou menos assim:



Agora, criamos um projeto Java, e inserimos as bibliotecas do Struts 2 (essas são os jars necessários para o nosso exemplo simples, precisando de mais funcionalidades, vamos precisar de mais jars - a versão que estou utilizando é a 2.1.6)
  • commons-fileupload-1.1.1.jar
  • commons-logging-1.0.4.jar
  • freemaker-2.3.13.jar
  • ognl-2.6.11.jar
  • struts2-convention-plugin-2.1.6.jar (Esse jar é necessário somente se for utilizar annotations em sua aplicação, para as configurações em XML, esse jar não é necessário)
  • struts2-core-2.1.6.jar
  • xwork-2.1.2.jar

Criamos uma pasta WEB-INF no nosso projeto, que vai conter nosso querido amigo web.xml (arquivo padrão para configuração do struts). Nele, precisamos obrigatoriamente configurar um FilterDispatcher, o default do struts é o org.apache.struts2.dispatcher.FilterDispatcher, mas nada impede o mapeamento de outros filtros.








struts2
org.apache.struts2.dispatcher.FilterDispatcher



struts2
/*



index.jsp





Por que preciso de um FilterDispatcher?! Bom, ele é o responsável por filtrar requisições vindas do browser! Junto com as Actions e os Interceptors, é a camada de Controller do MVC. MV O QUE?!

MVC - Model-View-Controller, é o padrão implementado pelo Struts.

Actions - Unidade mais básica do struts. É basicamente uma ação a ser executada pelo usuário.

Interceptors - Uma das grandes novidades do Struts 2 com relação ao Struts 1, é o controle sobre o fluxo de execução de uma requisição, por meio de Interceptors. Com eles podemos executar métodos antes ou depois de uma determinada Action ser executada.

Chega de teoria! Tendo o web.xml configurado, é hora de cuidar da View! O Model não é nosso foco agora, porque ele seria o Domínio da nossa aplicação. Mas ... não temos domínio, é só um exemplo simples! Então vamos criar os jsp's necessários para o nosso exemplo.

index.jsp (repare que esse é o jsp configurado no welcome-file do web.xml)


<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>







resposta.jsp


<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>




Ok, temos a View pronta. E agora? Agora é hora de criar as Actions! No Struts 2, não é necessário estender nenhuma classe, a Action pode ser um POJO simples, desde que devidamente anotada. Repare que o valor(value) da @Action definida no método a executar, é o mesmo que colocamos na action do formulário no nosso jsp. Quando subirmos nossa aplicação, o Struts vai recuperar as classes anotadas, verificar quais são as actions (essa verificação é feita com o Struts procurando classes anotadas dentro do pacote action), e é assim que o jsp vai saber que ação executar.

Exemplo de Action (repare que é um POJO comum, anotado):

package br.com.maps.action;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Result;

/**
* Action de exemplo para o tutorial de Struts 2 com annotation.
*
* @author finx
* @created Oct 19, 2009
*/
public class HelloWorldAction {

private String requisicao;

private String resposta;

/**
* Executa a Action.
*
* @return resultado da execução da Action.
*/
@Override
@Action(value = "/helloworld", results = { @Result(name = "success", location = "/resposta.jsp"), @Result(name = "input", location = "/index.jsp") })
public String execute() {
this.resposta = "Olá! Eu estou executando uma ação do usuário, com a seguinte requisição: ";
return "success";
}

/**
* Retorna a mensagem da requisição.
*
* @return a mensagem da requisição.
*/
public String getRequisicao() {
return requisicao;
}

/**
* Define a mensagem da requisição passada como parâmetro.
*
* @param requisicao a mensagem da requisição a ser definida.
*/
public void setRequisicao(String requisicao) {
this.requisicao = requisicao;
}

/**
* Retorna a resposta da requisição, chamada pelo componente s:property do jsp.
*
* @return a resposta da requisição, chamada pelo componente s:property do jsp.
*/
public String getResposta() {
return this.resposta + " [" + this.getRequisicao() + "].";
}

}


Subindo nossa aplicação, ela deve ficar com essa cara:



Hmmm bem legal ... mas quando eu aperto o botão e não escrevo nada, a minha resposta é vazia! Há, aí é que entram os interceptadores. Estendendo nossa action de ActionSupport, ganhamos um fluxo de execução com interceptadores configurado no arquivo struts-default.xml, e aí é só configurar!

Exemplo de requisição vazia:



Um exemplo de como a Action ficaria com validação:

package br.com.maps.action;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Result;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.validator.annotations.RequiredStringValidator;
import com.opensymphony.xwork2.validator.annotations.Validations;

/**
* Action de exemplo para o tutorial de Struts 2 com annotation.
*
* @author finx
* @created Oct 19, 2009
*/
public class HelloWorldAction extends ActionSupport {

private String requisicao;

private String resposta;

/**
* Executa a Action.
*
* @return resultado da execução da Action.
*/
@Override
@Action(value = "/helloworld", results = { @Result(name = "success", location = "/resposta.jsp"), @Result(name = "input", location = "/index.jsp") })
@Validations(requiredStrings = @RequiredStringValidator(fieldName = "requisicao", message = "Campo obrigatório"))
public String execute() {
this.resposta = "Olá! Eu estou executando uma ação do usuário, com a seguinte requisição: ";
return "success";
}

/**
* Retorna a mensagem da requisição.
*
* @return a mensagem da requisição.
*/
public String getRequisicao() {
return requisicao;
}

/**
* Define a mensagem da requisição passada como parâmetro.
*
* @param requisicao a mensagem da requisição a ser definida.
*/
public void setRequisicao(String requisicao) {
this.requisicao = requisicao;
}

/**
* Retorna a resposta da requisição, chamada pelo componente s:property do jsp.
*
* @return a resposta da requisição, chamada pelo componente s:property do jsp.
*/
public String getResposta() {
return this.resposta + " [" + this.getRequisicao() + "].";
}

}


E um exemplo de como ficaria a nossa requisição vazia, com validação:



Finalmente, com o fluxo completo:



E, disponibilizando o exemplo utilizado nesse tutorial na página de projetos do blog.

terça-feira, 1 de setembro de 2009

Just Java 2009

Daqui a duas semanas (dias 15, 16 e 17 de setembro) ocorrerá a oitava edição do Just Java. O evento contará com a apresentação do trabalho "Performance com Hibernate em Cluster" por membros de nossa equipe técnica. Estudaremos um "case" de processamento distribuído de dados e os seguintes tópicos serão abordados:
  • Distribuição de tarefas por JMS
  • Hibernate
    • Cuidados com FlushMode e gerenciamento do PersistenceContext
    • Lock otimista com uso de @Version
    • Uso de cache secundário
Esperamos vê-los no evento.