Tipos de Scaffold com Grails

September 21st, 2011 No comments

No último sábado, dia 17/09/2011 fizemos um HandsOn do JUGMS com Groovy 1.8 e Grails 2.0.0.M2. Vimos desde o básico, começando com um overview de Groovy e depois vendo o basicão de Grails. Como muitas dessas coisas não é muito dificil de conseguir ver na net, vou focar em coisas que nesse momento agregam um pouco mais de valor. Aí sim, depois que postar as coisas menos fáceis de achar na net, posso postar um projeto desde o início. Mas até lá (se esse lá chegar) tem um monte de vídeo bacana na net com esse início.

Uma coisa bacana dessas ferramentas modernas como RoR, Grails, Roo, e mais um monte, é o scaffold. Mas apesar de ser meio carne de vaca, vou tentar falar disso mostrando umas coisinhas a mais.



Scaffold

Esse conceito foi copiado da engenharia civil, e quer dizer andaime. Eles servem para sustentar os trabalhadores enquanto a estrutura final não está pronta, ou seja, é algo temporário, um quebra galho.
Na programação é a mesma coisa… uma estrutura temporaria que serve para que possamos nos focar no mais importante em vez de ficar metade do tempo do projeto enfeitando CRUD. Ao mesmo tempo que os scaffolds são massa pra caramba numa primeira olhada, logo depois começamos a passar uma certa raiva com o código gerado, pois mais cedo ou mais tarde temos que mexer nesse código. Bom, para lidar melhor com isso vamos dar uma olhada em três formas de scaffold que podemos usar com o Grails.

Para mostrar um código mais próximo do real vou usar como exemplo o projeto jugmotors (criado no HandsOn) e suas respectivas classes de domínio.



1. Scaffold estático

Esseé o mais basicão de todos. É o que conseguimos ao executar

grails genereate-all jugmotors.Pedido

Isso gera o controlador PedidoController e as views correspondentes às operações de CRUD.
Ponto positivo: o código está ali, pronto para você customizar
Ponto negativo: se você mudar a domain class o CRUD vai ficar desatulizado, faltando campos, e aí vem erro ao salvar por campos requeridos não estarem na tela, etc.



2. Scaffold dinâmico

Em vez de gerar tudo, só crie um controlador assim:

class ModeloController {
    static scaffold = true
}

Nesse caso, como o controlador tem o mesmo nome (fora o sufixo) que a domain class Modelo, automaticamente será gerado todo o scaffold em tempo de execução. Se o controlador não tivesse o mesmo nome, você teria o mesmo efeito com:

class AlgumNomeController {
    static scaffold = Modelo
}

O legal é que a gente pode ir sobrescrevendo cada elemento do scaffold, como uma action do controlador ou então uma view do CRUD, ou só a show.gsp, ou só a list.gsp e assim sucessivamente.

Ponto positivo: você pode alterar livremente a classe de domínio que o scaffold se adapta automaticamente, pois não existe um código “físico” executando.
Ponto negativo: se não tivermos outro scaffold estatico gerado para ter noção do código que vai ser executado, acabamos ficando vendidos, pois não vemos o código. Pelo mesmo motivo, quando formos sobrescrever algum elemento do scaffold, podemos esquecer algum passo importante.



3. Install-templates

Tá certo que esse não chega a ser um tipo de scaffold, mas executando

grails install-templates

A gente ganha os pontos positivos do scaffold estatico e do dinâmico. Tanto o código gerado no scaffold estático, quanto o que executa no dinâmico (apesar de não vermos) veem dos templates. Esses templates ficam dentro da instalação do grails, mas quando executamos esse script copiamos ele para dentro do projeto. A partir de então, toda vez que mandarmos o grails fazer alguma coisa, como gerar um controlador, view ou mesmo um scaffold inteiro, ele vai usar o template do projeto, e não do grails. Obviamente ele primeiro olha no projeto e se não encontrar vai para o grails.

No projeto jugmotors, eu fiz um scaffold estático sem customização: pedido; e um customizado: modelo.

O CRUD de pedido eu deixei como foi gerado, mas o de modelo eu fiz umas mudanças simples só para exemplificar a customização dos templates. Na view list.gsp eu coloquei as mesmas opções presentes na view show (editar e deletar). Então em vez da listagem padrão que só tem um link para a visualização, agora temos um botão que manda direto para e edição e um que apaga.

Depois de ter customizado essa tela, eu voltei para o código do template list.gsp e coloqui o mesmo estilo nele (não que esteja bom ou bonito, a ideia é exemplificar).

Agora se executarmos o CRUD de opcional, que é dinâmico, vemos que fica bem parecido com o de modelo. Agora se eu executar

grails generate-all <domain class>

O código gerado já vai sair de acordo com o meu novo template. Isso é muito legal, pois não temos que ficar “reféns” do código gerado pelo framework, pois agora o código é meu.

Ponto positivo: Tanto o scaffold estático quanto o dinâmico respeitam o nosso template :)
Ponto negativo: A gente vai ter um pouco mais de trabalho para fazer um template do que para fazer um arquivo comum, seja um gsp ou um groovy. Isso porque temos que prestar atenção nas expressões que escrevem outas expressões… então temos que usar muito escape. Exemplo simples do template list.gsp:

<g:link class="edit" action="edit" id="\${${propertyName}.id}">Editar</g:link>

Aqui no caso a propertyName se refere ao nome da propriedade do g:each que itera todos os objetos. Supondo que em tempo de execução propertyName seja igual a “pedidoInstance”, o resultado seria:

<g:link class="edit" action="edit" id="${pedidoInstance.id}">Editar</g:link>

Espero que a partir de agora a gente use mais essa funcionalidade de instalar os templates e assim customizá-los. E apesar de eu ter colocado somente o exemplo de customização de template de uma view, navegando dentro da pasta src/templates do projeto podemos ver os templates usados para criar classes de domínio, controladores, filtros, testes, etc. Logo, templates não são apenas para scaffold.

Mas pessoal, lembre do conceito do scaffold. A idéia é te entregar algo resoável para trabalharmos no mais importante. Então a menos que seja um CRUD muito sem vergonha é bom que você substitua o código gerado (mesmo customizado via template) pelo teu, que provavelmente vai ser melhor que o gerado.

Categories: Grails, Groovy Tags: ,

Palestra Grails Javaneiros 2008

December 8th, 2010 No comments

Então, sei que já faz mais de 2 anos (mais precisamente em 08/11/2008), mas como acabei não publicando o conteúdo dessa palestra na época, estou agora, impulsionado pelos desenvolvimento do JMeetings fazendo isso. Além disso tem um outro motivo. Esses tempos meu HD deu pau e quase perdi um monte de palestras antigas que por algum motivo não publiquei. Então estou aproveitando o fim de ano para limpar meus rascunhos e ir postando essas palestras antigas :)

De lá para cá obviamente muitas coisas mudaram, como por exemplo a utilização do servidor tomcat em vez do jetty como padão. Além disso logo depois da apresentação (três dias depois) a SpringSource comprou o G2One, que mantinha a linguagem Groovy e o Grails. E menos de um ano depois disso a SpringSource foi adiquirida pela VMWare.

Estarei postando assim que possível um vídeo mostrando como é simples desenvolver usando Grails. Ou pelo menos tão simples como poderia ser desenvolver em outras plataformas como RoR.

Categories: Grails, Groovy Tags:

Será que o Java 7/8 faz falta mesmo?

December 2nd, 2010 8 comments

Antes de mais nada sei que um ou outro pode querer me xingar sem nem ler o post. Esse post não serve para dizer que eu não quero que o Java evolua, que saiam novas versões, etc. Óbvio que isso é importante e por isso não vou nem discutir isso. Mas o ponto é: tem motivos para eu ficar choramingando lamentando a falta de recursos da linguagem, sem olhar para o que é mais importante, que é a plataforma? Ficar pensando porque essas mudanças nunca saem em vez de usar o que já temos hoje? A minha resposta pessoal é não, e o intuito deste post é explicar o porquê.

E porque eu coloquei Java 7 e 8 numa tacada só? Bom, talvez você não tenha acompanhado a novela da estratégia da Oracle para o Java 7. Resumindo, em vez de fazer um Java 7 pela metade “agora” ou um Java 7 completo para sabe-se lá quando, optou-se por fazer um “Java 7″ em duas fazes. Uma primeira que será o 7 e não vai ter muitas das coisas complicadas de implementar, e a segunda será o 8 e aí sim vai ter tudo que era esperado para o 7. Bem melhor do que demorar tudo que já demorou até hoje e ainda fazer “de qualquer jeito”.

Mas o artigo não é sobre Java 7/8 (vou tratar como uma única versão mesmo), e sim sobre a gente precisar ou não desesperadamente da nova versão da linguagem, e não das evoluções na plataforma. O que vou mostrar é que praticamente tudo que esperamos da próxima versão do Java (linguagem) já temos pronto em outras duas das principais linguagens para a JVM: Groovy e Scala.

Para esse artigo não ficar muito longo vou assumir que você está familiarizado com o fato da plataforma Java ser mais do que a linguagem Java. Mas já deixo na gaveta um post sobre a plataforma para deixar isso mais claro depois.

Além disso meu objetivo aqui não é escrever um post que explique em detalhes cada uma das funcionalidades citadas em cada uma das linguagens, e sim mostrar rapidamente cada coisa. Sendo assim reservo-me o direito de citar referências externas em vez de criar código de exemplo próprio :D



Geral

Para as próximas versões do Java existe o projeto Coin que reúne diversas melhorias para as próximas versões da linguagem. Algumas dessas mudanças estão contempladas nos tópicos abaixo.

Mas o fato é que o objetivo do Java não é ser uma linguagem como Groovy ou Ruby que tem diversas facilidades como elvis operator, safe navigation, reflection super facilitado, invocação do método equals usando == entre dezenas de outras coisas.

Talvez o mais parecido com Java seja Scala. Não em sintaxe, pois nesse quisito Groovy é quase igual Java. Agora o que Scala tem de mais parecido é o fato de ser estaticamente tipada, mas com um sintaxe simplificada, com alto poder inferência de tipos, sem ponto e vírgula obrigatório e que você na maioria das vezes nem percebe que não está usando uma linguagem dita mais “flexível” ou “dinâmica”.

Na minha opinião a maioria não quer especificar tipos, mas isso não quer dizer que necessariamente quer uma linguagem sem tipos. Nos artigos que escrevi de Scala dá pra ver bem dessas facilidades.



Closures

Um dos principais pontos, ou pelo menos o “mais famoso” ponto do Java 7 é o suporte à closures. Suporte que esse que durante a novela Java 7 já foi cortado, reintroduzido “nas coxas” e por fim vai estar completo no Java 8. Mas e se olharmos para Groovy e Scala? O suporte à closures está lá.

Java 8

Referência

#int() fortyTwo = #()(42);
#int(int) doubler = #(int x)(x + x);
#int(int,int) multiplier = #(int x, int y)(x * y);

assert fortyTwo() == 42;
assert doubler(fortyTwo()) == 84;
assert multiplier(3, fortyTwo()) == 126;

Update: o Paulo Silveira, há um bom tempo já, postou um material bem bacana em português sobre isso no blog da Caelum.

Groovy

Referência

def printSum = { a, b -> print a+b }
printSum( 5, 7 )                       //prints "12"

Referência

def c = { arg1, arg2-> println "${arg1} ${arg2}" }
def d = c.curry("foo")
d("bar")

Referência

def x = 2

// define closure and assign it to variable 'c'
def c = { numberToSquare -> numberToSquare * numberToSquare }

// using 'c' as the identifer for the closure, make a call on that closure
def y = c(x)       // shorthand form for applying closure, y will equal 4
def z = c.call(x)  // longhand form, z will equal 4

Scala

Esse código de exemplo eu postei aqui (to meio sem criatividade hoje :D ). Nele temos um método chamado percorreLista e uma closure chamada imprimeElemento, que é passada como parâmetro para o primeiro.

object Main extends Application {
    def percorreLista[T](lista:List[T], funcao:(T) => Unit)= {
    for (elemento <- lista) {
      funcao(elemento)
    }
  }

  val imprimeElemento = (elemento: Any) => {
    println(elemento)
  }

  percorreLista(List(2,3,4,5), imprimeElemento)
}



Automatic Resource Management (ARM)

Uma coisa chata de fazer em Java é trabalhar com IO. Vamos pegar como exemplo…

Java 6

Referências um e dois

Aqui temos que tratar as possíveis exceções todas na mão.

Lendo um arquivo

try
{
    String teamMember;
    FileReader fr = new FileReader("arquivo.txt");
    BufferedReader br = new BufferedReader(fr);
    while ((teamMember = br.readLine()) != null)
    {
        System.out.println(teamMember);
    }
}
catch (FileNotFoundException e)
{
    e.printStackTrace();
}
catch (IOException e)
{
    e.printStackTrace();
}
finally
{
    if (br!= null)
    {
        br.close();
        if (fr != null)
        {
            fr.close();
        }
    }
}

Agora um código JDBC

String connectionURL = "jdbc:mysql://localhost:3306/myDB";
Connection connection = null;
Statement st = null;
ResultSet rs = null;
try
{
    connection = DriverManager.getConnection(connectionURL, "root", "admin");
    st = connection.createStatement();
    rs = st.executeQuery("Select * from EMPLOYEE_SALARIES");
    while (rs.next())
    {
        System.out.println("EMPLOYEE_NAME/EMPLOYEE_SALARY");
        System.out.println(rs.getString(1) + "/" + rs.getString(2));
    }
}
catch (ClassNotFoundException ex)
{
    ex.printStackTrace();
}
catch (SQLException ex)
{
    ex.printStackTrace();
}
catch (InstantiationException ex)
{
    ex.printStackTrace();
}
catch (IllegalAccessException ex)
{
    ex.printStackTrace();
}
finally
{
    try
    {
        if (!rs.isClosed())
        {
            rs.close();
        }
        if (!st.isClosed())
        {
            st.close();
        }
        if (!connection.isClosed())
        {
            connection.close();
        }
    }
    catch (SQLException ex)
    {
        ex.printStackTrace();
    }
}
Java 8

Com o ARM ficaria bem mais simples.

Lendo um arquivo

String teamMember = null;
try (BufferedReader br = new BufferedReader(new FileReader("arquivo.txt")))
{
    while ((teamMember = br.readLine()) != null)
    {
        System.out.println(teamMember);
    }
}

Código JDBC com ARM

String connectionURL = "jdbc:mysql://localhost:3306/myDB";
try
(
    Connection connection = DriverManager.getConnection(connectionURL, "root", "admin");
    Statement st = connection.createStatement();
    ResultSet rs = st.executeQuery("Select * from EMPLOYEE_SALARIES");
)
{
    while (rs.next())
    {
        System.out.println("EMPLOYEE_NAME/EMPLOYEE_SALARY");
        System.out.println(rs.getString(1) + "/" + rs.getString(2));
    }
}
Groovy

Em Groovy fazer isso é extremamente simples

Lendo um arquivo

Referência

println new File('arquivo.txt').text

Lendo uma tabela com Groovy

Referência

import groovy.sql.Sql
sql = Sql.newInstance("jdbc:jtds:sqlserver://serverName/dbName-CLASS;domain=domainName", "username",
                     "password", "net.sourceforge.jtds.jdbc.Driver")
sql.eachRow("select * from tableName", { println it.id + " -- ${it.firstName} --"} );
Scala

Referências um e dois

Em Scala também é simples.

def using[T <: { def close() }]
    (resource: T)
    (block: T => Unit)
{
  try {
    block(resource)
  } finally {
    if (resource != null) resource.close()
  }
}

using(new BufferedReader(new FileReader("file.txt"))) { r =>
  var count = 0
  while (r.readLine != null) count += 1
  println(count)
}

Referência

val lines = for { file <- managed(new java.io.File("some.txt"))
       stream <- new java.io.FileInputStream(file)
       line <- makeLineIterator(stream) } yield line



Switch com String?

Sinceramente eu acho essa parte até meio brincadeira, pois é muito barulho para uma funcionalidade muito simples. Essa mudança faz parte do projeto Coin.

Java 7

A funcionalidade é simples, a única diferença é que agora suporta String.

Groovy

O suporte do Groovy é bem mais rico que o suporte à Strings. Claro que pode ser mais complicado num primeiro momento, cheio de possibilidades, mas para mim é só um comportamento mais completo, com tudo que se espera.

Referência

def x = 1.23
def result = ""

switch ( x ) {
    case "foo":
        result = "found foo"
        // lets fall through

    case "bar":
        result += "bar"

    case [4, 5, 6, 'inList']:
        result = "list"
        break

    case 12..30:
        result = "range"
        break

    case Integer:
        result = "integer"
        break

    case Number:
        result = "number"
        break

    default:
        result = "default"
}

assert result == "number"

E podemos ainda sobrescrever o método isCase(valor), que por default chama equals(valor)

class Estado{
    String uf
    def isCase(valor){
        uf == valor
    }
}

def estado = new Estado(uf: "MS")

switch ( estado ) {
    case "MS":
        println "Mato Grosso do Sul"
        break

    case "SP":
        println "São Paulo"
        break

    default:
        println "Sei lá..."
}
Scala

Scala assim como Groovy tem um mecanismo bem interessante que pode ser usado num switch, o Pattern Matching. Como isso vale um artigo a parte, recomendo a leitura desse post. Mas para exemplificar vou colocar um exemplo:

def checkPrime(number:Int) = number match {
  case 1 => true
  case 2 => true
  case 3 => true
  case 5 => true
  case 7 => true

  case _ => false
}

Comparando pelo tipo

var obj = performOperation()
var cast:Color = obj match {
  case x:Color => x
  case _ => null
}

Em Java o equivalente seria

Object obj = performOperation();
Color cast = null;
if (obj instanceof Color) {
    cast = (Color) obj;
}

E em Scala temos ainda as case classes, que são tipos especiais de classes, que dentro outras coisas não precisamos de new para instanciá-las e podem ser usadas direto com pattern matching.

Referência

case class Number(value:Int)

def checkPrime(n:Number) = n match {
  case Number(1) => true
  case Number(2) => true
  case Number(3) => true
  case Number(5) => true
  case Number(7) => true
  case Number(_) => false
}

checkPrime(Number(12))

Outro exemplo

class Color(val red:Int, val green:Int, val blue:Int)

case class Red(r:Int) extends Color(r, 0, 0)
case class Green(g:Int) extends Color(0, g, 0)
case class Blue(b:Int) extends Color(0, 0, b)

def printColor(c:Color) = c match {
  case Red(v) => println("Red: " + v)
  case Green(v) => println("Green: " + v)
  case Blue(v) => println("Blue: " + v)

  case col:Color => {
    print("R: " + col.red + ", ")
    print("G: " + col.green + ", ")
    println("B: " + col.blue)
  }

  case null => println("Invalid color")
}

printColor(Red(100))
printColor(Blue(220))

printColor(new Color(100, 200, 50))
printColor(null)

Saída

Red: 100
Blue: 220
R: 100, G: 200, B: 50
Invalid color



Properties

Suporte à properties é bem esperado mas já foi praticamente “limado” da lista de features que vão entrar nas próximas versões de Java. Na verdade o suporte à properties é mais do que vamos ver aqui. Um suporte completo permitiria a gente ter type safe criteria na JPA2 sem necessidade de geração de código. Mas com certeza esse suportezinho simples, à la Groovy, já ajuda bastante.

Java

Sem suporte. Getters and Setters

public class Pessoa{
  private String nome;
  private String endereco;
  private String nome;

  //getters e setterss gerados pela IDE.
  //Vocês já devem estar enjoados de ver, então vou economizar espaço e nem colocar esse código aqui
Groovy

Em Groovy fazer isso é extremamente simples. O compilador gera altomaticamente os getters e setters para atributos não privados. Depois de compilado fica igualzinho um “bean” java, atributos privados e métodos de acesso públicos.

class Pessoa{
  String nome
  String endereco

  //para essa propriedade não teremos getters e setters gerados
  private Integer idade

  //aqui fica igual os getters e setters gerados para nome e endereco.
  //Só lembrando que a tipagem em groovy é opcional
  def Integer getIdade(){ idade }
  def void setIdade(Integer idade){ this.idade = idade }
}
Scala

Em Scala também é simples.

class Pessoa(var nome:String,
             var localidade:String,
             private var _idade:Int) {
  //assim como no exemplo em Groovy, quando marcamos um atributo como privado os métodos de acesso não são gerados
  def idade = _idade
  def idade_=(idade:Int) = _idade = idade
}

val pessoa = new Pessoa("Gilliard", "Campo Grande/MS", 26)
pessoa.nome = "Gilliard Cordeiro"
pessoa.idade = 27
println( pessoa.localidade )



Performance

Tem mais um monte de coisas que eu poderia comentar mas essas são as primeiras que me ocorreram. Mas para finalizar, pelo menos por enquanto, vamos falar de performance.

Não encontrei nenhum benchmark atualizado para linkar aqui, mas no geral Java e Scala estão bem próximos em performance, com mais vantagem para o Java e em alguns casos Scala na frente. Já o Groovy é meio patinho feio nessa comparação entre as três linguagens, mas ainda assim – com base em benchmarks um pouco antigos – ele ainda é mais performático que PHP por exemplo. E eu nunca ouvi ninguém dizer que deixaria de usar PHP por causa de performance, mas quando se fala de Groovy esse assunto sempre surge.

Sobre Java e Scala nem vale muito a pena falar, mas os benchmarks envolvendo Groovy tem que ser encarados com cautela. Primeiro porque se o benchmark for meio antigo é bem possível que de lá pra cá a performance já melhorou bastante. Até porque quanto mais lento, mais espaço para melhorar um ambiente de execução tem. O segundo ponto é que em todos os comparativos sempre vejo código 100% linguagem A contra código 100% linguagem B. Já na prática dificilmente a gente vai ter uma aplicação com código 100% Groovy. Vamos pegar como exemplo uma aplicação feita em Grails. Minha bola de cristal diz que no total de código da aplicação (bibliotecas incluídas) nem 30% é Groovy.

Para complicar ainda mais a conta, você já viu o tamanho da StackTrace quando executamos nosso código num servidor de aplicações? O tanto de AOP, interceptors e outros serviços rodando ao mesmo tempo com certeza vai levar mais tempo do que a diferença entre a execução de duas linguagens diferentes. Por fim a maioria dos problemas de performance não é culpa do que se usa, e sim de como se usa uma ferramenta.

Update: Só mais uma “coisinha” sobre performance do Groovy. Existe também o Groovy++, que compila estaticamente partes do código Groovy (depende do que você anotar com @Typed) aumentando assim a performance consideravelmente pelo que li, mas não conheço na prática. Quando eu já tiver mexido com isso eu posto, mas até lá vocês podem ver alguma coisa aqui, aqui e aqui.



(Minha) Conclusão

Terminado então esse post, acho que ficou claro que, pelo menos para mim, sentimos falta de um ou outro recurso da linguagem mas, ou praticamente não vamos usar a função quando ela sair (pois não faz falta de verdade hoje), ou não conhecemos as outras opções que a plataforma, e não a linguagem Java nos dá. Pois se conhecermos as opções e realmente quisermos usar essas funcionalidades, não faz muito sentido não usar o que se tem.

Como trabalhar com ViewScope e Page

November 23rd, 2010 7 comments

Uma coisa que não é muito intuitiva é a forma como o ViewScope do JSF e o scope Page do Seam funcionam. Como estamos acostumados com o escopo request, que termina quando a próxima view é renderizada, tendemos a pensar que esses escopos funcionam da mesma forma. Mas na verdade o escopo morre no momento que uma nova view é setada. O problema é que depois que isso acontece ainda temos toda a fase 6 do jsf.

Para entendermos melhor o funcionamento, vamos considerar como exemplo uma tela de listagem de produtos (produtoLista.xhtml) onde selecionamos um produto e este é exibido em outra view, que mostra os detalhes desse produto (produtoForm.xhtml). Nessa aplicação vou usar o mesmo managed bean com @ViewScope para a listagem e para a tela do produto.

Usando o escopo view, quando clicamos num h:command(Button | Link) que tem dentro um f:setPropertyActionListener temos a impressão que o jsf não colocou o produto selecionado no target, no caso o produtoController.produto. Na verdade ele fez isso sim, mas assim que mudou da view de listagem para a de produto o produtoController, que continha o produto selecionado foi descartado. Então um novo produtoController é instanciado, e esse obviamente não conhece o produto selecionado. O funcionamento é simples, só não é intuitivo (vou fazer essa afirmação várias vezes que é pra ficar no subconsciente hehehe).

Na minha opinião, um bom comportamento padrão seria como o @ViewConversationScoped. Mas como ninguém liga para a minha opinião, o jeito é usarmos os parâmetros de url para segurar esses valores. Pra variar já escrevi muito, então vamos ver na prática como fazer isso.

Na classe Produto vou simplesmente ignorar os getters e setters, Groovy like =)

Entidade Produto

@Entity
public class Produto {
 
	@Id @GeneratedValue
	private Integer id;
	private String nome;
	private String descricao;
 
        @Override
	public String toString() {
		return "Produto [descricao=" + descricao + ", id=" + id + ", nome=" + nome + "]";
	}
}

O Controlador

@ManagedBean
@ViewScope
public class ProdutoController {
 
	private Produto produto;
	private List<Produto> produtos;
 
	//método init serve só para vermos em que momento o bean é destruído
	@PostConstruct
	public void init(){
		System.out.println("ProdutoController.init()");
		atribuirEstadoInicial();
	}
 
	/**
	 * Deixa o bean em um estado inicial válido tanto para edição quanto para listagens
	 */
	private void atribuirEstadoInicial()
	{
		System.out.println("ProdutoController.atribuirEstadoInicial()");
		//serve para deixar o bean em um estado onde pode acontecer uma nova edição
		produto = new Produto();
		//limpa a listagem previamente carregada pois ela não contém um elemento novo ou contém um recém excluído
		produtos = null;
	}
 
	public void salvar()
	{
		System.out.println("ProdutoController.salvar()");
 
		JpaUtil.getEntityManager().getTransaction().begin();
		JpaUtil.getEntityManager().merge(produto);
		JpaUtil.getEntityManager().getTransaction().commit();
 
		atribuirEstadoInicial();
	}
 
	public Produto getProduto() {
		return produto;
	}
 
	public void setProduto(Produto produto) {
		System.out.println("ProdutoController.setProduto(): " + produto);
		this.produto = produto;
	}
 
	@SuppressWarnings("unchecked")
	public List<Produto> getProdutos() {
		if (produtos == null) {
			produtos = JpaUtil.getEntityManager().createQuery("select p from Produto p").getResultList();
		}
		return produtos;
	}
 
	public void setProdutos(List<Produto> produtos) {
		this.produtos = produtos;
	}
}

E o converter

@FacesConverter(forClass=Produto.class)
public class ProdutoConverter implements Converter {
 
	@Override
	public Object getAsObject(FacesContext context, UIComponent component, String string) {
		System.out.println("ProdutoConverter.getAsObject(): " + string);
		if(string == null || string.isEmpty()){
			return null;
		}
		return JpaUtil.getEntityManager().find(Produto.class, Integer.valueOf(string));
	}
 
	@Override
	public String getAsString(FacesContext context, UIComponent component, Object object) {
		Produto produto = (Produto) object;
		System.out.println("ProdutoConverter.getAsString(): " + produto);
		if(produto == null || produto.getId() == null){
			return null;
		}
		return String.valueOf(produto.getId());
	}
}

Na verdade, até aqui não tem muita novidade. No resto também não vai ter novidade :) mas vamos lá.

A listagem de produtos:

...
<h:dataTable value="#{produtoController.produtos}" var="produto">
	<h:column>
		<f:facet name="header">ID</f:facet>
		#{produto.id}
	</h:column>
	<h:column>
		<f:facet name="header">Nome</f:facet>
		#{produto.nome}
	</h:column>
	<h:column>
		<f:facet name="header">Descrição</f:facet>
		#{produto.descricao}
	</h:column>
	<h:column>
		<f:facet name="header">Ações</f:facet>
		<h:link value="editar 1" outcome="produtoForm">
			<f:param name="id" value="#{produto.id}"/>
		</h:link>
		<h:commandLink value="editar 2" action="produtoForm?faces-redirect=true&amp;includeViewParams=true">
			<f:setPropertyActionListener value="#{produto}" target="#{produtoController.produto}"/>
		</h:commandLink>
	</h:column>
</h:dataTable>
...

E o form de produto:

...
<f:view>
	<f:metadata>
		<f:viewParam name="id" value="#{produtoController.produto}" />
	</f:metadata>
	<h:head>
		<title>Detalhes do Produto</title>
	</h:head>
	<h:body>
		<h:form>
			<h:panelGrid columns="2">
				Nome: <h:inputText value="#{produtoController.produto.nome}" />
				Descrição: <h:inputText value="#{produtoController.produto.descricao}" />
				<h:commandButton action="#{produtoController.salvar}" value="Salvar" />
			</h:panelGrid>
		</h:form>
	</h:body>
</f:view>
...

Por fim, vamos analisar o log do click nos links “editar 1″ e “editar 2″

link “editar 1″

ProdutoController.init()
ProdutoController.atribuirEstadoInicial()
ProdutoConverter.getAsObject(): 1
ProdutoController.setProduto(): Produto [descricao=Fermento em Pó, id=1, nome=Fermento]
ProdutoConverter.getAsString(): Produto [descricao=Fermento em Pó, id=1, nome=Fermento]

link “editar 2″

ProdutoController.setProduto(): Produto [descricao=Fermento em Pó, id=1, nome=Fermento]
ProdutoConverter.getAsString(): Produto [descricao=Fermento em Pó, id=1, nome=Fermento]
ProdutoController.init()
ProdutoController.atribuirEstadoInicial()
ProdutoConverter.getAsObject(): 1
ProdutoController.setProduto(): Produto [descricao=Fermento em Pó, id=1, nome=Fermento]
ProdutoConverter.getAsString(): Produto [descricao=Fermento em Pó, id=1, nome=Fermento]




Beleza, agora sim tem código pra caramba… boa parte dele aliás bem parecido com o desse post. No meio disso tudo o que temos que prestar atenção é nos dois botões editar da produtoLista.xhtml. O link “editar 1″ é exatamente igual ao apresentado no post que acabei de citar. O valor é passado por GET e o converter do viewParam faz o trabalho de nos deixar trabalhar sempre OO.

Agora vamos ver o link “editar 2″. Nesse exemplo a gente tem um post para uma view que usa um ManagedBean com escopo @ViewScope para uma outra view cujo MB é o mesmo, mas isso é um detalhe.

Na primeira linha temos o f:setPropertyActionListener trabalhando e chamando o set da propriedade, e na segunda linha vimos o converter gerando o texto (nesse caso id) que irá representar esse objeto na url da próxima view, pois deixamos o includeViewParams=true. Note que em momento algum passamos a propriedade que vai representar o produto na url como fizemos no “editar 1″. Quem vai fazer isso é o conversor.

Depois, entre as linhas 2 e 3 a view é trocada e o MB é perdido, mas como a url agora já tem o valor a ser mantido, fica igual o exemplo anterior. A única coisa que pode parecer é que teremos buscas desnecessárias ao banco. Mas como você vai estar usando algo mais esperto do que buscar no braço, a JPA já vai estar com esse objeto no cache de primeiro nível – pois estou usando o padrão OpenEntityManagerInView – e não haverá nenhum overhead por causa dessa outra forma de fazer. E isso é muito importante, apesar de termos um converter no meio, e do POST em vez de GET rodar o restore view do jsf, o objeto selecionado não será em momento algum trazido mais de uma vez no banco pois o EntityManager está com ele no cache (para isso não precisa de configuração nenhuma). Como estamos com o bean em escopo view, também não será buscado novamente a lista do banco. Então a única perda real nesse caso é não termos a url montada já na tela de listagem – o que pode nem ser uma perda. De fato todo o “overhead” dessa abordagem resume-se a chamadas de métodos locais como getters. Então provavelmente se sua aplicação ficar lenta aqui, o problema é outro.

Novamente o que incomoda é a falta de intuitividade dessa abordagem. Mas o funcionamento é simples. Só temos que lembrar que nessa abordagem do “editar 2″ só vai funcionar se tivermos o includeViewParams ativo, seja no link ou na regra de navegação do faces-config.xml. Sem isso o JSF não se preocupa em incluir na próxima view os parâmetros de url.



Importante! (update)



Apesar da abordagem do link “editar 1″, que usa GET ser a forma mais bacana de se trabalhar, e inclusive é a “novidade” do JSF 2, a abordagem do “editar 2″ tem se mostrado mais segura. Isso porque até a versão atual do JSF (2.1) a remoção do bean no escopo view não ocorre da forma esperada quando usamos GET para sair da página, porém quando usamos POST (jeitão que o JSF já está bem acostumado) a coisa rola corretamente.

Agora caso você queria usar um escopo que dure mais que uma página como comentado pelo Rodrigo a melhor solução na minha opinião é usar conversação. Solução que inclusive permite trocar de páginas usando GET sem o problema do escopo view, que desse modo não remove o bean, pois na conversação, se você não matar, o timeout mata.
Uma forma simples de usar é iniciar a conversação quando abrimos a view. Para isso podemos fazer de várias formas, mas a mais simples é usando o seam-faces:

Código da view

 
<f:metadata>
   <s:viewAction action="#{meuBean.init}" if="#{conversation.transient}" />
</f:metadata>

Código do Bean

@Named
@ConversationScoped
public class MeuBean{
    @Begin 
    public void init(){
    }
}

Ou

Código do Bean (alternativo)

@Named
@ConversationScoped
public class MeuBean{
 
    @In
    Conversation conversation
 
    public void init() {
        conversation.begin();
    }
}

Mas não estou dizendo para criar conversação e largar, tem que matar ela. Só estou falando que se for pra largar pra trás (coisa feia :( ) é melhor fazer com conversação do que com view ou session.

E ainda outra forma de usar um escopo view em mais de uma página é usar o @ViewAccessScope do apache CODI (citado também pelo João). Ele funciona como o “bom e velho” Keep Alive (anotação ao tag), e em vez de matar o bean na troca de página, ele espera o fim do response, e se o bem não for usado, aí sim é removido. O única problema é que a configuração do apache CODI, principalmente quando já estamos rodando o seam-faces, é um pouquinho mais charope. Mas funciona.



Concluindo…



Nada do que mostrei aqui é novo ou difícil. Mas resolvi escrever pois em uma semana tive três dúvidas iguais aqui no blog sobre esse assunto. E nos cursos de Seam (escopo Page) e JSF 2 que ministro vejo que esse assunto demora para ser digerido também. Então espero que esse post tenha sido útil para minimizar essas dúvidas. Usar esse recurso do JSF 2 (ou Seam) é simples, mas se te incomodar muito, ou se você quiser usar uma conversação em uma única view (@ViewScope não segura o EntityManager aberto e com isso não evita LazyinitializationException), lembre-se que JEE6 define extensões portáveis. Então uma boa coisa é procurar coisas como o escopo que eu citei no início do post.

Sei que o pessoal do Java é meio purista, as vezes torce o nariz para o que não é especificado, mas se ganha muito procurando a solução para o seu problema em um projeto opensource bacana em vez de passar raiva e esperar até sair a próxima versão de alguma especificação, o que obviamente vai demorar mais do que uma novidade nascida direto da comunidade (apache, jboss.org, etc). Mas isso é assunto para um próximo post.

Desenvolvendo uma aplicação Desktop com Weld – Final

May 25th, 2010 2 comments

Depois da parte 1 e parte 2, chegamos à parte final do nosso exemplo. Disponibilizei o código e vou mostrar mais algumas coisas.
Só uma observação sobre o código: é o mesmo utilizado na apresentação de 14 de Novembro de 2009, quando ainda não tínhamos as mesmas versões de hoje. Mas o exemplo é totalmente funcional. Acabei deixando fixa a versão no pom.xml, então para testar a última versão você vai ter que alterá-lo.

Agora voltando ao post…


Alternatives


Alternatives são a forma de trocarmos um objeto por outro. É parecido com um decorator, mas em vez de incrementar funcionalidades, um alternative serve para substituir um bean.

No nosso exemplo, temos o seguinte objeto como alternativa ao nosso CaixaEletronico:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Alternative
public class CaixaEletronicoSubstituto extends CaixaEletronico{
 
 
	public void depositar(float valor)
	{
		System.out.println("@@@@@@ CaixaEletronicoMock.depositar():" + valor);
	}
	public void sacar(float valor)
	{
		System.out.println("@@@@@@ CaixaEletronicoMock.sacar():" + valor);
	}
}


E para habilitar temos que mudar novamente nosso META-INF/beans.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
 
	<interceptors>
		<class>br.com.jugms.weldse.intercept.ContaInterceptor</class>
	</interceptors>
 
	<decorators>
		<class>br.com.jugms.weldse.intercept.CaixaDecorator</class>
	</decorators>
 
	<alternatives>
		<class>br.com.jugms.weldse.model.CaixaEletronicoSubstituto</class>
	</alternatives>
 
</beans>


Agora se executarmos nosso código a saída sera:

hello
CaixaDecorator.sacar()
@@@@@@ CaixaEletronicoMock.sacar():200.0
ContaInterceptor.protege(antes) >> getSaldo
ContaInterceptor.protege(depois) >> getSaldo
2000.0
CaixaDecorator.depositar()
@@@@@@ CaixaEletronicoMock.depositar():300.0
ContaInterceptor.protege(antes) >> getSaldo
ContaInterceptor.protege(depois) >> getSaldo
2000.0


Como podemos ver, nosso decorator decora o alternative e o interceptor continua funcionando normalmente.
Podemos também declarar uma anotação própria e anotá-la como alternative. Mas em vez de tentar explicar, vou mostrar um outro exemplo que servirá como base para este.


Estereótipos próprios


Podemos criar nossos próprios estereótipos, e dessa forma não só reduzir a quantidade de anotações em cima dos nossos beans, mas também deixar a leitura das anotações mais condizentes com nosso linguajar do dia a dia.

Vamos pegar como exemplo o nosso objeto ContaBancaria:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package br.com.jugms.weldse.model;
 
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import javax.inject.Named;
 
import br.com.jugms.weldse.intercept.Seguro;
 
@ApplicationScoped
@Seguro
public class ContaBancaria {
	//o código já foi visto antes
}


Já vimos este código e o corpo da classe não tem relação com o que vamos ver agora. Agora o mais importante é observarmos as anotações. Temos duas, mas poderíamos ter mais. Para deixar isso mais limpo e com mais significado no meu negócio, poderia criar um estereótipo próprio como o seguinte:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package br.com.jugms.weldse.model;
 
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
 
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Stereotype;
 
import br.com.jugms.weldse.intercept.Seguro;
 
@ApplicationScoped
@Seguro
@Stereotype
@Target(TYPE)
@Retention(RUNTIME)
public @interface JavaneirosBean {
}


E depois mudar nosso ContaBancaria deixando ele assim:

1
2
3
4
5
6
7
8
9
10
package br.com.jugms.weldse.model;
 
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import javax.inject.Named;
 
@JavaneirosBean
public class ContaBancaria {
	//o código já foi visto antes
}


Com isso o resultado será o mesmo de antes. Nosso objeto ContaBancaria é @Seguro e @ApplicationScoped. Além disso se depois quisermos dizer que nosso @JavaneirosBean é também um @Alternative ou qualquer outro estereótipo pré-existente ou mesmo um outro estereótipo customizado, basta anotar nossa anotação @JavaneirosBean com a anotação desejada.

Pode parecer estranho usarmos anotações de anotações, mas para exemplificar, se a JPA suportasse isso eu poderia ter uma anotação @Entidade que seria uma @Entity (JPA) e ao mesmo tempo @Named (CDI) e assim poder utilizá-la diretamente nas minhas view JSF com uma única anotação.


Conceitos gerais


Eu disse que ao final do exemplo iria entrar mais na parte teórica de como a CDI trata mais extamente a injeção de dependência, seleção de candidatos etc. Porém por questão de organização, vou deixar isso em um post separado.

Desenvolvendo uma aplicação Desktop com Weld – Parte 2

May 25th, 2010 5 comments

Continuando então no nosso exemplo, vamos comentar mais sobre a “parte prática” do exemplo e depois eu comento mais sobre os tipos de injeção que a CDI faz. E só pra contextualizar, quando eu disse “ejeção” no último post, não quer dizer que a CDI tem um @Out como o Seam, e sim que ele tem uma forma de produzir objetos para um determinado escopo assim como um @Factory do Seam ou Spring.


Interceptors


No final do ultimo post vimos que nossa ContaBancaria estava anotada com @Seguro. Essa é uma anotação da nossa aplicação:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package br.com.jugms.weldse.intercept;
 
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
 
import javax.interceptor.InterceptorBinding;
 
@InterceptorBinding
@Target( { METHOD, TYPE })
@Retention(RUNTIME)
public @interface Seguro {
}


No meio de um monte de anotações percebemos a que define nossa anotação como uma @InterceptorBinding. Agora basta anotar quem queremos interceptar com a mesma anotação usada na definição do interceptor (@Seguro) para “bindar” interceptador e interceptado.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package br.com.jugms.weldse.intercept;
 
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
 
 
@Interceptor @Seguro
public class ContaInterceptor {
 
	@AroundInvoke
	public Object protege(InvocationContext context) throws Exception
	{
		System.out.println("ContaInterceptor.protege(antes) >> " + context.getMethod().getName());
 
		Object object = context.proceed();
 
		System.out.println("ContaInterceptor.protege(depois) >> " + context.getMethod().getName());
 
		return object;
	}
}


Esse é o nosso interceptador. Olhando o código quase não preciso explicar nada. Aqui coloquei Conta no meio do nome porque é uma aplicação de exemplo, mas na prática poderia ter um uso mais genérico. Esse código tem o jeitão de um interceptador comum. O método “protege” está anotado com @AroundInvoke, e isso me dá o poder de envolver, e com isso até mesmo trocar a implementação original do objeto interceptado. É bem parecido com AOP. Mas a regra do tio Ben se aplica aqui também: “Grandes poderes trazem grandes responsabilidades”! Então isso não serve para ficar adicionando regra de negócio na aplicação, senão depois você não vai achar mais nada no sistema e vai falar que viu isso aqui. A idéia de um interceptor é prover funcionalidades ortogonais para o sistema, assim como AOP. Então se você não gosta de AOP ou interceptor é provavelmente porque está colocando código no local errado.

Nesse exemplo usei para prover segurança, protegendo meus objetos marcados com @Seguro. Mas minha implementação simplesmente mostra no console que o nosso método foi chamado.

Além de interceptors, temos decorators, que veremos daqui a pouco. Mas para já ter uma idéia da diferença, o interceptor geralmente é para requisitos não funcionais, enquanto os decorators adicionam funcionalidades em objetos do nosso sistema. Mas o foco aqui não é padrões de projeto, então para saber mais basta dar uma pesquisada. O que importa para o contexto desse post é que tanto interceptors quanto decorators não estão habilitados por padrão. Para colocá-los para rodar precisamos “ligá-los” no arquivo /META-INF/beans.xml. Abaixo vemos como fica nosso arquivo com esse interceptor ligado.


1
2
3
4
5
6
7
8
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
 
	<interceptors>
		<class>br.com.jugms.weldse.intercept.ContaInterceptor</class>
	</interceptors>
 
</beans>


Eu sei que sou repetitivo, mas mais uma vez eu digo: não precisamos habilitar beans injetáveis ou gerenciáveis quando usamos CDI. Basta que os objetos estejam em um bean package para tudo acontecer, e um bean package é um módulo (jar, projeto, etc) que tenha um arquivo /META-INF/beans.xml vazio (só com cabeçalho). Pronto, só precisa disso. As exceções são para interceptors, decorators e alternatives (esse ultimo falo depois). Esses sim precisam ser habilitados.

Agora que já temos tudos os arquivos, executando a aplicação temos a seguinte saída:

hello
ContaInterceptor.protege(antes) >> recebeMovimento
ContaInterceptor.protege(depois) >> recebeMovimento
ContaInterceptor.protege(antes) >> getSaldo
ContaInterceptor.protege(depois) >> getSaldo
1800.0
ContaInterceptor.protege(antes) >> recebeMovimento
ContaInterceptor.protege(depois) >> recebeMovimento
ContaInterceptor.protege(antes) >> getSaldo
ContaInterceptor.protege(depois) >> getSaldo
2100.0

Como estamos usando @AroundInvoke, colocamos uma mensagem antes e uma depois de cada método.


Decorators


Agora que vimos o interceptor, fica mais fácil entendermos o decorator. Vamos ao código

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
package br.com.jugms.weldse.intercept;
 
import javax.decorator.Decorator;
import javax.decorator.Delegate;
import javax.inject.Inject;
 
import br.com.jugms.weldse.model.CaixaEletronico;
 
@Decorator
public class CaixaDecorator extends CaixaEletronico {
 
	@Inject
	@Delegate
	private CaixaEletronico delegate;
 
	@Override
	public void sacar(float valor) {
 
		System.out.println("CaixaDecorator.sacar()");
 
		delegate.sacar(valor);
	}
 
	@Override
	public void depositar(float valor) {
 
		System.out.println("CaixaDecorator.depositar()");
 
		delegate.depositar(valor);
	}
}


Um decorator precisa ter condições de substituir o objeto original (extender, implementar mesma interface). Além disso temos o objeto original que é injetado através da anotação @Delegate juntamente com a @Inject. Aqui também podemos utilizar diversos estereótipos para “selecionar” o objeto a ser decorado. Como os demais exemplos dessa nossa aplicação, esse decorator é extremamente complexo ao printar no console que ele foi executado :D .

Agora basta adicionarmos ele no beans.xml:

1
2
3
4
5
6
7
8
9
10
11
12
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
 
	<interceptors>
		<class>br.com.jugms.weldse.intercept.ContaInterceptor</class>
	</interceptors>
 
	<decorators>
		<class>br.com.jugms.weldse.intercept.CaixaDecorator</class>
	</decorators>
 
</beans>


E agora a saída no console é a seguinte:

hello
CaixaDecorator.sacar()
ContaInterceptor.protege(antes) >> recebeMovimento
ContaInterceptor.protege(depois) >> recebeMovimento
ContaInterceptor.protege(antes) >> getSaldo
ContaInterceptor.protege(depois) >> getSaldo
1800.0
CaixaDecorator.depositar()
ContaInterceptor.protege(antes) >> recebeMovimento
ContaInterceptor.protege(depois) >> recebeMovimento
ContaInterceptor.protege(antes) >> getSaldo
ContaInterceptor.protege(depois) >> getSaldo
2100.0


Apesar da saída no console mostrar nosso decorator antes do nosso interceptor, isso é porque estamos decorando e interceptando coisas diferentes, pois decorators são chamados depois dos interceptors.

Em seguida posto como funcionam os alternatives e o que faltou colocar nesses dois primeiros posts.

Desenvolvendo uma aplicação Desktop com Weld – Parte 1

March 17th, 2010 13 comments

Como havia comentado, em Novembro do ano passado eu apresentei uma palestra no Javaneiros, evento anual sobre Java organizado pelo JUGMS. Os slides eu já disponibilizei no post anterior, e agora vou de fato colocar aqui no blog o que mostrei na palestra, pois slides sem explicação não serve para muita coisa né?

Como acho que um post só vai ficar muito grande, vou publicar o conteúdo em partes.


Intradução


Primeiramente eu gostaria de explicar, para quem não está familiarizado, o que é a CDI. Hoje em dia está mais difundido que na época da palestra, mas até para manter a coerência com os slides vou comentar rapidamente alguma coisa. Se seu objetivo é ver código, pode pular esse tópico.

A CDI, ou “Context and Dependency Injection for the Java EE platform” é uma especificação que define uma forma padrão de trabalharmos com DI em aplicações Java. Pelo nome já vemos que ela é voltada para Java EE, mas está seguindo os mesmos passos da JPA, que foi introduzida no Java EE 5 mas é usada sem problemas em aplicações SE.

Talvez a coisa que valha mais a pena comentar antes de entrar na prática é o monte de nomes envolvidos nessa JSR. O primeiro “nome” é a própria identificação da JSR: “JSR-299″. Acho que até quem nunca foi ligado em número de JSR já ouviu falar dessa tal de 299. Para resumir a história, ela já se chamou WebBeans antes de se chamar CDI, e até hoje muita coisa na internet, principalmente as discussões iniciais você vai encontrar esse nome. Além disso tem a implementação de referência dela, que também já chamou WebBeans e agora é Weld. Ou seja, de WebBeans não sobrou nada.

Outra coisa que veio dar uma cofundida nesses nomes foi a JSR-330, “Dependency Injection for Java“, que nada mais é do que a extração das anotações da CDI para uma especificação menor, tornando assim mais fácil que frameworks como Spring e Guice utilizem anotações padrão mesmo não sendo uma implementação da CDI. Isso gerou muita polêmica também, pois em tese a CDI já deveria ser mínima e não faz sentido alguém usar as anotações dela sem ter o mesmo comportamento. Mas isso já é outra história.


Ainda antes de começar…


A CDI sofreu muita influência do Seam e Guice, além é claro de não ser difícil de compreender para quem trabalha com Spring, pois no fim das contas os conceitos são os mesmos.

Uma premissa muito forte da CDI é a injeção de dependência “type safe“, o que já a diferencia bastante do Seam que trabalha muito com Strings. Nesse ponto já fica mais perto para quem trabalha com Spring do que quem tá acostumado com Seam.

Outra coisa que temos que ter em mente é que diferentemente do Spring ou Seam onde uma classe simples não é considerada uma candidata à injeção de dependência a menos que seja marcada (via xml ou anotação), na CDI se a classe está dentro de um bean package então ela automaticamente é elegível. Mas isso nós vamos ver na aplicação de exemplo.

Outra coisa que temos que ter em mente é que o objetivo da CDI não é substituir o Seam. O Seam tem um mundo de coisas como facilitadores para e-mail, pdf, bpm, segurança, jsf, etc. A CDI define apenas o core o que será o core do Seam 3, ou seja, a parte da DI, interceptors e mais algumas coisas. Então não vamos comparar coisas tão diferentes com comentários tipo “O Seam é muito melhor que o Weld. O Weld não faz nada, o Seam faz um monte de coisas”.

A primeira coisa que precisamos saber para trabalhar com a CDI é o que é um bean package. Na prática um bean package vai ser um jar (ou pode estar explodido mesmo) onde temos um arquivo META-INF/beans.xml. Se a CDI encontrar esse cara, ela escaneia tudo e considera todas classes dentro desse “módulo” como beans injetáveis. É análogo ao seam.properties, mas no caso do Seam ainda precisa ter a anotação ou estar no components.xml para a classe ser considerada.

Outra coisa que vamos ver no exemplo é que o mesmo se trata de uma aplicação SE de console. Aí nos perguntamos: mas no nome da especificação não fala que é para Java EE? Pois é, mas a especificação define também a criação de extensões portáveis. Isso quer dizer que você pode fazer uma extensão da CDI e rodar em qualquer implementação dela. Pois bem, o Weld tem uma extensão que permite que rodemos tudo isso num ambiente SE, porém com limitações, claro.


Agora vamos à pratica


O nosso exemplo é bem simples, e consiste em realizarmos um saque e um depósito em uma conta bancária via um caixa eletrônico. Para simplificar o exemplo, temos apenas uma conta e um caixa eletrônico.

Nossa aplicação inicia com a classe MovimentacaoControl.

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
package br.com.jugms.weldse.control;
 
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
 
import org.jboss.weld.environment.se.events.ContainerInitialized;
 
import br.com.jugms.weldse.model.CaixaEletronico;
import br.com.jugms.weldse.model.ContaBancaria;
 
/**
* @author http://gilliard.eti.br
*/
@ApplicationScoped
public class MovimentacaoControl{
 
    @Inject private ContaBancaria contaBancaria;
    @Inject private CaixaEletronico caixaEletronico;
 
    public void executar(@Observes ContainerInitialized init) {
 
        System.out.println("hello");
 
        caixaEletronico.sacar(200.0f);
 
        System.out.println(contaBancaria.getSaldo());
 
        caixaEletronico.depositar(300.0f);
 
        System.out.println(contaBancaria.getSaldo());
 
    }
}

Como podemos ver essa classe possui a anotação @ApplicationScoped, mas essa anotação não serve para habilitar o gerenciamento dos objetos dessa classe, pois como eu disse anteriormente, isso é automático já que estamos em um bean package.

Essa classe também não possui um método main. Para executar o exemplo executamos a classe org.jboss.weld.environment.se.StartMain, e essa sim possui um método main, que inicializa todo o framework e quando termina de inicializar o container ele avisa lançando um evento do tipo org.jboss.weld.environment.se.events.ContainerInitialized. Para iniciarmos nossa aplicação, basta escutarmos esse evento como é feito no método “executar“. Deu para perceber que escutar um evento é algo muito complicado :) . Nesse caso não faremos nada com o objeto init que é o que representa o evento, mas no decorrer do exemplo veremos como isso funciona.

No nosso exemplo, nós realizamos um saque de 200, depois olhamos o saldo, depositamos 300 e olhamos o saldo de novo. Parece bobo (e não vou negar isso), mas é útil para testarmos os diferentes escopos. Pois quando mudamos os scopos para stateless (default, sem a anotação @ApplicationScoped) percebemos que a conta sempre volta a ter o valor inicial.

Nesse exemplo estou trabalhando com classes, mas para trabalhar com interfaces o procedimento é o mesmo, ao ver que você pediu para injetar um objeto tipado por uma determinada interface a CDI vai procurar por uma implementação da mesma, e encontrando injeta. Agora se ela encontrar nenhuma ou mais de uma, aí será lançada uma excessão. “Poxa, mas e se eu tiver mais de uma implementação no bean package”? Palma, palma, palma, não priemos cânico, vamos chegar a ver isso.


Manipulação de eventos


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package br.com.jugms.weldse.model;
 
import javax.enterprise.event.Event;
import javax.inject.Inject;
 
/**
* @author http://gilliard.eti.br
*/
public class CaixaEletronico {
 
    @Inject private Event<Movimentacao> eventMovimentacao;
 
    public void depositar(float valor)
    {
        eventMovimentacao.fire(new Movimentacao(valor));
    }
    public void sacar(float valor)
    {
        eventMovimentacao.fire(new Movimentacao(-1 * valor));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package br.com.jugms.weldse.model;
 
/**
* @author http://gilliard.eti.br
*/
public class Movimentacao {
 
    private float valor;
 
    public Movimentacao(float valor) {
        this.valor = valor;
    }
 
    public float getValor() {
        return valor;
    }
}

Como podemos ver, a classe CaixaEletronico não tem nenhuma anotação para “ativá-la” como injetável, e ainda assim ela é injetada no nosso objeto da classe MovimentacaoControl. Podemos ver também que nossa classe CaixaEletronico não debita diretamente da nossa conta (que nesse exemplo simplista só existe uma). O CaixaEletronico envia uma mensagem que é representada pela classe Movimentacao. Para disparar esse evento nosso CaixaEletronico pede para que seja injetado nele um ‘cara’ que sabe disparar eventos do tipo Movimentacao. E então no depósito ele dispara uma movimentação com valor positivo e no saque uma com valor negativo. Bem simples.

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
package br.com.jugms.weldse.model;
 
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import javax.inject.Named;
 
import br.com.jugms.weldse.intercept.Seguro;
 
/**
* @author http://gilliard.eti.br
*/
@ApplicationScoped
@Seguro
public class ContaBancaria {
 
    @Inject @Named
    private float saldoInicial = 1000.0f;
 
    public void recebeMovimento(@Observes Movimentacao movimentacao)
    {
        saldoInicial += movimentacao.getValor();
    }
 
    public float getSaldo() {
        return saldoInicial;
    }
 
}

Agora na classe ContaBancaria vemos como consumir um evento. Na verdade já tinhamos visto na classe MovimentacaoControl, mas agora vamos de fato utilizar o objeto do evento. Como na MovimentacaoControl, para escutarmos um evento basta termos um método com um parâmetro do tipo do objeto do evento anotado com @Observes. Além disso podemos ver o escopo desse objeto, que é de aplicação. Comente esse escopo e você vai perceber que serão criadas várias contas bancárias, e dessa forma vamos debitar em uma e creditar na outra.

Podemos ver também a injeção de um ‘cara’ do tipo float, mas além de @Inject usamos um @Named para isso. O @Named é um qualificador. Nós também podemos, em vários casos vamos “precisar”, criar nosso próprios qualificadores. Um qualificador serve para especificar melhor qual candidato à injeção queremos que seja escolhido. Seria como uma cláusula “where” na pesquisa por injetáveis. No caso desse float, obviamente algém vai ter que colocá-lo no contexto para que possamos recuperá-lo, e podemos ver isso na classe Produtores.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package br.com.jugms.weldse.model;
 
import javax.enterprise.inject.Produces;
import javax.inject.Named;
 
/**
* @author http://gilliard.eti.br
*/
public class Produtores {
 
    @Produces @Named
    public float getSaldoInicial()
    {
        return 2000.0f;
    }
}

Essa classe agrega todos os métodos produtores da nossa aplicação (que por ser extremamente complexa tem exatamente um :) ). Um método produtor (anotado com @Produces) é o oposto do consumidor (@Inject), logo em vez de pegar, ele joga um obejto no contexto, usando como qualificador o @Named que permite darmos um nome ao componente. Só temos que tomar cuidado para não usarmos demais isso e acabarmos tendo tudo ligado via Strings. Nesse exemplo, o nome usado para registrar esse componente é “saldoInicial”, pois segue o padrão java bean.

Podemos ver também que que na classe ContaBancaria deixei um saldo inicial de 1000, e depois mandei injetar nessa propriedade o valor de um componente do tipo float chamado “saldoInicial” que está com valor 2000. Na prática ficará valendo o 2000, pois o 1000 só vai valer antes que as injeções sejam executadas.

Nos próximos posts eu faço uma sessão específica para comentar as possibilidade de injeção e “ejeção” que a CDI provê. E também vou explicar o funcionamento dos interceptadores como podemos ver através da anotação @Seguro da classe ContaBancaria, e também como especificarmos qual implementação de uma interface vamos querer que seja selecionada. Como dá para perceber esse assunto vai longe. Então até o próximo post.

Apresentacao sobre CDI (JSR-299) no Javaneiros2009

February 25th, 2010 1 comment

Estou a um bom tempo sem postar, mas nesse tempo fiz bastante coisa que acabei não postando aqui. Uma delas foi uma palestra no Javaneiros2009, falando sobre a JSR-299.

Ainda vou postar aqui o exemplo, mas como pretendo explicar cada parte, e isso vai levar mais tempo, já vou postando os slides até para tirar a poeira do blog.

Update:
Desenvolvendo uma aplicação Desktop com Weld – Parte 1
Desenvolvendo uma aplicação Desktop com Weld – Parte 2
Desenvolvendo uma aplicação Desktop com Weld – Final

Mojarra (JSF RI) 2.0 disponível

October 19th, 2009 6 comments

Depois de uma boa espera, está disponível a versão final do JSF 2.0.

Nos demais post deste blog você pode ver alguns exemplos de novas funcionalidades e baixar projetos com JSF 2 (não a versão final) rodando.

Como faz algum tempo que eu venho postando sobre isso, alguns exemplos podem ter pequenas diferenças do que está agora na versão final, mas nada que prejudique o entendimento, pois apesar da implementação estar saindo agora, a especificação já está pronta há algum tempo.

Tem vários assuntos que não tive tempo de escrever um post, mas hoje em dia já não está difícil encontrar exemplos de JSF 2 na internet.

Outros links:

https://javaserverfaces.dev.java.net/
https://javaserverfaces.dev.java.net/nonav/rlnotes/2.0.0/index.html
https://javaserverfaces.dev.java.net/maven2

Boa diversão :D

Categories: JavaEE, JSF Tags: , ,

URLs amigáveis no JSF 2.0

May 27th, 2009 9 comments

Hoje vou falar de mais uma novidade do JSF 2, cuja falta era motivo de muita reclamação: URLs amigávies, bookmarking, método GET e outros nomes que podemos dar. O suporte a essa nova funcionalidade é dado por dois pares de componentes, de um lado h:button e h:link e do outro f:metadata e f:viewParam.

Os componentes h:button e h:link servem para originar as ações jsf assim como os componentes h:command{Button|Link}, porém usando GET em vez de POST. Esses componentes possuem um atributo chamado outcome, que representa a regra de navegação do JSF, assim como seria colocar uma String diretamente na action do h:command{Button|Link}. Para passar parâmetros colocamos a tag f:param dentro do h:link ou h:button.

Como exemplo vamos ver uma aplicaçãozinha que tem uma tela de listagem e outra de visualização de Pessoas. A seguir um trecho da página de listagem de pessoas, listarPessoas.xhtml:

<h:form>
	<h:dataTable value="#{pessoaController.pessoas}" var="pessoa">
		<h:column>
			#{pessoa.nome}
		</h:column>
		<h:column>
			<h:link outcome="/verPessoa" value="Editar">
				<f:param name="pessoa" value="#{pessoa.nome}"/>
			</h:link>
		</h:column>
	</h:dataTable>
</h:form>

Nesse exemplo, no outcome eu já estou usando o esquema novo de navegação comentado no post passado. O link acima vai gerar uma url parecida com http://localhost:8080/exemplojsf2/verPessoa.jsf?pessoa=fulano_0.

Agora do lado da página que recebe a requisição temos os componentes f:metadata e f:viewParam. A primeira é apenas uma tag que engloba as f:viewParam. Já as tags f:viewParam se comportam de forma muito parecida com um h:inputText, podemos dizer que praticamente a única diferença é no input a gente digita em um formulário, enquanto na f:viewParam escrevemos na URL.

A tag f:viewParam possue os seguintes atributos: converter, converterMessage, required, requiredMessage, validator, validatorMessage, value, valueChangeListener, maxlength e for (este último voltado para o novo esquema de component composition do Facelets). Como podemos ver, é praticamente um h:inputText.

Vamos ver então um trecho do código da página que recebe a requisição, verPessoa.xhtml:

<f:view>
	<f:metadata>
	    	<f:viewParam name="pessoa" value="#{pessoaController.pessoaSelecionada}" />
   	</f:metadata>
<h:head>
    <title>Ver Pessoa</title>
</h:head>
<h:body>
	<h:form>
		Nome: #{pessoaController.pessoaSelecionada.nome}
	</h:form>
</h:body>
</f:view>

Não precisei colocar um converter no f:viewParam pois configurei um converter “forClass” como veremos mais a frente.

Agora a nossa classe

public class Pessoa {
 
	private String nome;
 
	public Pessoa() {
	}
	public Pessoa(String nome) {
		this.nome = nome;
	}
 
	//getter e setter suprimido
}

Isso mesmo, a classe é complexa desse jeito  :D

Como o intuito é só um exemplo, eu nem me preocupei com banco de dados ou algum mecanismo mais interessante, apenas criei um conversor para mostrar que o novo esquema não permite apenas o uso de Strings. Segue o código do conversor:

@FacesConverter(forClass=Pessoa.class)
public class PessoaConverter implements Converter {
 
	@Override
	public Object getAsObject(FacesContext context, UIComponent component, String string) {
 
		return new Pessoa(string);
	}
 
	@Override
	public String getAsString(FacesContext context, UIComponent component, Object object) {
 
		return ((Pessoa)object).getNome();
	}
 
}

E agora nosso managed bean que recebe não uma String, e sim objetos do nosso domínio. Eu falo isso o tempo todo pois preciso deixar isso bem claro, senão eu fico doido de ver uma aplicação usando JSF passando String e Integer de um lado pro outro :(

Mas tudo bem, deixando o desabafo pra lá vamos ao código:

@ManagedBean(name="pessoaController")
@RequestScoped
public class PessoaController {
 
	private Pessoa pessoaSelecionada;
	private List<pessoa> pessoas;
 
	@PostConstruct
	public void init()
	{
		pessoas = new ArrayList<pessoa>();
		for (int i = 0; i < 10; i++) {
			pessoas.add(new Pessoa("fulano_" + i));
		}
	}
 
	//getters e setters suprimidos
}

Acredito que por hoje seja suficiente. Vou ver se em breve escrevo algo mais específico sobre facelets.