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

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.

7 thoughts on “Desenvolvendo uma aplicação Desktop com Weld – Parte 2

  1. Pingback: Gilliard Cordeiro » Apresentacao sobre CDI (JSR-299) no Javaneiros2009

  2. Pingback: Gilliard Cordeiro » Desenvolvendo uma aplicação Desktop com Weld – Final

  3. Primeiramente gostia de parabeniza-lo pelo ótimo post. Em segundo momento preciso tirar uma dúvida rsrsrs. Estou com problemas para iniciar a aplicação, o teste foi feito no netbeans e a ide não deixa executar sem um método main. E daí outra dúvida, como inicio este “contaner desktop”? grato e aguardo a resposta! :)

  4. Obrigado @Glauco Soares.
    Não sou conhecedor do netbeans, mas no eclipse você pode criar um “Play” arbitrariamente indicando uma Main qualquer. Acredito que no netbeans também tenha essa possibilidade. Essa main, que eu cito no primeiro artigo é que sobe o container e notifica a finalização através de um evento.
    Quando encontrar a opção no netbeans coloca aqui, que assim fica documentado para quem mais tiver essa dúvida.

  5. @Gilliard Cordeiro
    Deu certo meu amigo, o problema foi unicamente má compreensão(na verdade falta de atenção hehehe) chamei no meu main o main da StartMain(quantos main hehehe). Tive que adicionar outras libs além da weld-se que foram : slf4j-log4j12 e a log4j. Ainda tive outro problema pois estava usando a api-jee6 que por algum motivo lançava uma exceção de ClassFormatError na classe UIComponent, retirei do classpath as libs e funcionou. Porém, o mundo é cruel e ainda tive uns pau aqui. O primeiro foi quando adicionei o interceptor no beans.xml lançava uma exceção de deploy pela duplicidade de declaração do Interceptor, retirei e funcionou beleza algumas vezes incluse com ele ativado. Logo após, adicionei o decorator que apresentou o mesmo erro devido o xml, retirei e não funcionou nada. O estranho e que parou de funcionar o interceptor. Tentanto algumas vezes com xml/sem xml consegui rodar o interceptor. Só que ele executa quando quer :S … Uma luuuuuzz!

  6. @Glauco Soares
    Não consegui reproduzir esse mesmo comportamento. Talvez seja alguma versão diferente, com bugs diferentes…
    Quando você executa você limpa e recompila tudo? Será que essa inconstância não pode ser barbeiragem da IDE, que por algum motivo não atualizou algum artefato?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>