Archive

Archive for May, 2010

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.