Observer em um Resources do vraptor

Pesoal,

Tenho alguns métodos em um controller que estão fazendo mais de uma coisa e estou querendo refatorá-los para um observer. É um problema bem parecido com a classe GeradorDeNotaFiscal desse post:http://blog.caelum.com.br/tdd-e-sua-influencia-no-acoplamento-e-coesao/

No caso esse controller seria observado e os observadores seriam recebidos no construtor:

public controller(Observer1 o1, Observer2 o2) {
  o1 = new Observer1(this);
  o2 = new Observer2(this);
}

No controller também teriam os métodos para adicionar observer e notificação.

Tem outra forma de implementar esse observer no controller ou dessa forma está ok?

como vc pensa em fazer com que requisições sejam transformadas em observers?

Os observers só iriam salvar ou atualizar alguns dados no banco.

    public controller(Observer1 o1, Observer2 o2, Repository r1, Repository r2) {  
      o1 = new Observer1(this, r1);  
      o2 = new Observer2(this, r2);  
    }  

pode dar um exemplo mais concreto?

Imagina um controller parecido com essa classe GeradorDeNotaFiscal:


public class GeradorDeNotaFiscal {
       private final Sap sap;
       private final EnviadorDeEmails enviador;
 
       public GeradorDeNotaFiscal(Sap sap, EnviadorDeEmails enviador) {
               this.sap = sap;
               this.enviador = enviador;
       }
 
       private NotaFiscal geraNotaFiscalAPartirDa(Fatura fatura) {
          
       }
 
       public void gera(Fatura fatura) {
               NotaFiscal nf = geraNotaFiscalAPartirDa(fatura);
 
               sap.armazena(nf);
               enviador.envia(nf);
       }
}

Agora sap e EnviadorDeEmails seriam Observers:

public class GeradorDeNotaFiscalController {
       private final Sap sap;
       private final EnviadorDeEmails enviador;
 
       public GeradorDeNotaFiscalController(Sap sap, EnviadorDeEmails enviador) {
               this.sap = new Sap(this);
               this.enviador = new EnviadorDeEmails(this);
       }
 
       private NotaFiscal geraNotaFiscalAPartirDa(Fatura fatura) {
          
       }

       public void notifica(Nota Fiscal) {

       }
 
       public void gera(Fatura fatura) {
               NotaFiscal nf = geraNotaFiscalAPartirDa(fatura);
 
               notifica(nf);
       }
}

se vc recebe o Sap e o EnviadorDeEmails como dependência, vc não deveria dar new neles:

public class GeradorDeNotaFiscalController {  
       private final Sap sap;  
       private final EnviadorDeEmails enviador;  
  
       public GeradorDeNotaFiscalController(Sap sap, EnviadorDeEmails enviador) {  
               this.sap = sap;  
               this.enviador = enviador;  
       }
}

esse controller é um @Resource?

É porque em Sap eu teria que receber o controller no construtor.
Algo assim:

public Sap(GeradorDeNotaFiscalController controller){
		controller.adicionarObservador(this);
	}
 

aí que está, vc não precisa do adicionaObservador, já que vc tá recebendo o Sap no construtor do controller…

o ideal na verdade é vc criar uma interface:

public interface NotaFiscalObserver /*extends Observer?*/ {
   //...
}

e fazer com que o Sap e o EnviadorDeEmails implementem essa interface. Eles também devem ser anotados com @Component.

Daí vc pode receber no controller uma List:

@Resource
public class GeradorDeNotaFiscalController {    
       
       public GeradorDeNotaFiscalController(List<NotaFiscalObserver> observers) {    
            this.observers = observers;
       }
       public void notifica(Fatura fatura) {
           for (NotaFiscalObserver obs : this.observers) {
               obs.notifica(fatura);
           }
       }
}  

ou algo do tipo. Assim, se vc precisar adicionar outro observer é só criar uma classe anotada com @Component que implementa NotaFiscalObserver, não precisa modificar o controller.

Lembrando: o Sap e o EnviadorDeEmails não precisam receber o controller!

[quote=Lucas Cavalcanti]aí que está, vc não precisa do adicionaObservador, já que vc tá recebendo o Sap no construtor do controller…

o ideal na verdade é vc criar uma interface:

public interface NotaFiscalObserver /*extends Observer?*/ {
   //...
}

e fazer com que o Sap e o EnviadorDeEmails implementem essa interface. Eles também devem ser anotados com @Component.

Daí vc pode receber no controller uma List:

[/quote]

Curiosidade

       public GeradorDeNotaFiscalController(List<NotaFiscalObserver> observers) {    

observers vai receber todos as classes que implementam NotaFiscalObserver ?

exato!

isso só funciona automaticamente se vc usa o Spring como DI (se vc está com os jars do spring no WEB-INF/lib), pro Guice tem que fazer uma configuraçãozinha e pro Pico eu não tenho certeza, mas é possível que funcione também.

Isto é muito legal !
poderia ver o q tem de fazer no Guice, atualmente estou usando apenas ele.

[]s

no guice vc precisa configurar essa List<…> explicitamente…

tem um jeito relativamente fácil de fazer isso, mas tá protected no VRaptor…

se vc não se importar de usar snapshots, posso gerar um pra vc com essa alteração

eu gostaria sim, se não for incomodo, com o snapshot não posso colocar em produção, mas poderei testar internamente.

[]

Testa aí

Apareceu um erro estranho

2011-03-28 22:51:11.861:INFO::jetty-7.3.0.v20110203
2011-03-28 22:51:13.554:WARN::Failed startup of context o.m.j.p.JettyWebAppContext{/,file:/E:/trabalho/workspace/aws2/src/main/webapp/},file:/E:/trabalho/workspace/aws2/src/main/webapp/
java.lang.IllegalStateException: Duplicate fragment name: @@-NAMELESS-@@0 for jar:file:/E:/trabalho/repositorio/br/com/caelum/vraptor/3.3.2-SNAPSHOT/vraptor-3.3.2-SNAPSHOT.jar!/META-INF/web-fragment.xml and jar:file:/E:/trabalho/workspace/aws2/src/main/webapp/WEB-INF/lib/vraptor-3.3.1.jar!/META-INF/web-fragment.xml
	at org.eclipse.jetty.webapp.MetaData.addFragment(MetaData.java:244)
	at org.eclipse.jetty.webapp.FragmentConfiguration.findWebFragments(FragmentConfiguration.java:72)
...

Usando eclipse+maven+jetty7

Arranquei o fragment e voltou a funcionar…

mas não sei se teve a ver a atualização ou algumas mexidas que eu tinha feito no pom

o vraptor inclui um fragment do servlet 3.0, e vc está com dois jars do vraptor, por isso o erro de name duplicado.

Obrigado o maven estava com um lixo … mas agora testando surgiu este erro, com a versão 3.3.1 funciona com a 3.3.2-SNAPSHOT ele aparece …

2011-03-30 15:17:20.997:WARN::Error for /
java.lang.NoClassDefFoundError: br/com/caelum/vraptor/vraptor2/Info
	at br.com.caelum.vraptor.util.StringUtils.lowercaseFirst(StringUtils.java:35)

usa esse snapshot:
https://oss.sonatype.org/content/repositories/snapshots/br/com/caelum/vraptor/3.3.2-SNAPSHOT/vraptor-3.3.2-20110329.205836-1.jar

Agora funcionou !

Obrigado