[Logo] Mentawai Forum - Mentawai Web Framework
  [Search] Search   [Recent Topics] Recent Topics   [Members]  Member Listing   [Groups] Back to home page 
[Register] Register / 
[Login] Login 

Forum Read-Only! Check the new site and forum here!

Conexão Elegante (:  XML
Forum Index -> Comentários Gerais
Author Message
Lucas Teixeira


[Avatar]

Joined: 09/06/2005 18:56:48
Messages: 9
Location: Londrina - PR
Offline

E ai pessoal,
Antes de tudo, parabéns pelo projeto, está simplesmente d+!

Minha dúvida é a seguinte. Vi que o Mentawai já me ajuda a fazer meu Connection Pooling com um Filter adequado, correto?
Mas eu vi que nos exemplos, a conexão é sempre resgatada diretamente na Action em questão - a Action onde o filter foi adicionado. Porém, pensando de uma forma um pouco mais habitual, vejo que não conseguiria ter um DAO acessado essa connection, a não ser que passasse ele para meu DAO, o que não considero uma boa alternativa. Teria como buscar a connection gerenciada pelo Filter em outra classe que não fosse minha Action? No caso, o DAO em questão?

Valeu galera, e novamente, parabéns!!!

Lucas Frare de A. Teixeira
lucas.teixeira@gmail.com
[Email]
saoj



Joined: 01/07/2005 09:59:17
Messages: 2846
Location: Rio de Janeiro, RJ
Offline

Tua pergunta é muito boa!

A questão é: Como o seu DAO vai gerenciar conexões ???

Ele sabe abrir conexões, ou alguém tem que passar pra ele essa conexão ???

Caso alguém tenha que passar pra ele a conexão, vc tem algumas opções:

1) A action pega a conexão do input e passa para o DAO.

2) Vc usa o InjectionFilter para injetar a conexão num setConn(Connection conn) da action, que por sua vez injeta no DAO.

3) Vc pode ter um filtro feito por vc para fazer isso.

Agora se o seu DAO tem que "saber" abrir conexões, então vc precisa do org.mentawai.db.ConnectionHandler. Na verdade todos os pools do mentawai implementam essa interface.

Agora que vc vai ter que pensar um pouco, pois eu não tenho a resposta muito clara na minha cabeça.

Quando vc for criar o DAO, que imagino fique no escopo da aplicação (como um singleton, mas sem ser singleton), vc precisar dar um jeito de criar tb o pool e passá-lo para dentro do DAO, de forma que ele fique prontinho para ser usado.

Vc não pode criar várias instancias do pool, não faz sentido. Logo toda vez que vc criar um DAO, vc pega a instancia do pool que está no escopo da aplicação e coloca no DAO.

Algo por aí... Depois me diga o que vc fez para eu aprender tb...



Sergio Oliveira

saoj



Joined: 01/07/2005 09:59:17
Messages: 2846
Location: Rio de Janeiro, RJ
Offline

Acho que veio a idéia para resolver o seu problema.

Tudo que vc tem que fazer é passar o pool de conexões do Mentawai (ConnectionHandler) para o Dao como parametro de inicialização ou parametro de construtor. Assim:

Code:
 
 public class MyDAO {
 
     public void setConnectionHandler(ConnectionHandler ch) {
         ...
     }
 }
 
 DefaultComponent c1 = new DefaultComponent(MyDao.class);
 c1.addProperty("connectionHandler", myPool);
 
 


Entretanto isso tem um problema chato: Vc está atrelando o seu DAO a API do Mentawai, no caso ao connection handler. O ideal seria atrelar o seu DAO apenas a Connection.

Uma outra opção seria criar um outro componente para gerar as conexões. Isso foi uma idéia do Rubem e acho que resolve o problema.

Quando vc adiciona um componente como propriedade ou valor de incialização, o método getInstance() é chamado antes de setar ele no objeto.

Então vc criaria uma classe assim:

Code:
 
 public class MyConnectionHandler implements Component {
     private ConnectionHandler connHandler = null;
 
     // estou com preguiça de programar...
     // mas a função:
 
     public Object getInstance() {
         return connHandler.getConnection();
     }
 
     // retorna a conexão...
 
     // OBS: O Dao nao vai poder ter scopo de aplicacao, e sim de requisicao.
 }
 


Problema: Quem vai retornar a conexão para o pool ????????????????????

Acho que a solução mais fácil é um filtro mesmo para injetar no DAO a cada requisição.

Como vc pode ver sua pergunta foi muito boa. Ainda não tenho uma resposta final.

Sergio Oliveira

Lucas Teixeira


[Avatar]

Joined: 09/06/2005 18:56:48
Messages: 9
Location: Londrina - PR
Offline

Sérgio, também estou pensando em algo. Assim que tiver algo convincente, posto aqui! Obrigado pelas dicas...

Ahhh, a idéia é manter o Dao ligado apenas à conexão, e nunca nem à API do Mentawai, nem a Action... Estou pensando em um Filter implementado por mim que possa gerenciar isto em uma Thread Local....

O que acha? Posto resultados assim que puder considerá-los como 'resultados'.

Heheheh, um abraço!


Lucas Frare de A. Teixeira
lucas.teixeira@gmail.com
[Email]
Lucas Teixeira


[Avatar]

Joined: 09/06/2005 18:56:48
Messages: 9
Location: Londrina - PR
Offline

E ai Sérgio...

Desacoplei totalmente a gerência da Conexão da API do Mentawai e da Action fazendo da seguinte forma...

O Dao pede a conexão para uma classe chamada "ConexaoUtils", que verifica na ThreadLocal se já existe uma conexão em andamento. Caso já exista ele usa esta mesma. Se não existir, ela pede ao ConnectionPool uma conexão limpa, armazena na ThreadLocal (para futuros usos) e retorna para o Dao em questao.
Também criei um ConnectionFilter que verifica a execução da Action, e após ela, fecha a conexão corrente usando a mesma ConexaoUtils (que sabe quem é a conexão corrente)!!!

Desta maneira, acredito que eu possa fazer chains e mais chains, e quantas consultas em quantos Daos eu quiser também, a conxão será sempre a mesma, diminuindo o custo para o banco também!

Assim, quem deve saber sobre a conexão sabe, quem não deve, não mete o bedelho! (:

Aqui os códigos (apenas para teste, precisam ser muito melhorados para usar em produção, e a implementação é um exemplo, de fato).

ConexaoAction
Code:
/*
  * Criado em 29/08/2005
  * Lucas Frare Teixeira
  * <a href="mailto:lucas.teixeira@gmail.com">lucas.teixeira@gmail.com</a>
  */
 package action;
 
 import org.mentawai.core.ActionException;
 import org.mentawai.core.BaseAction;
 
 import dao.NomesDao;
 
 public class ConexaoAction extends BaseAction {
 
     public String execute() throws ActionException {
         
         NomesDao nomesDao = new NomesDao();
         String nome = null;
         
         try {
             nome = nomesDao.getRandomName();
             output.setValue("nome", nome);
             return SUCCESS;
         } catch (Exception e) {
             e.printStackTrace();
             return ERROR;
         }
     }
 
 }


NomesDao
Code:
/*
  * Criado em 29/08/2005
  * Lucas Frare Teixeira
  * <a href="mailto:lucas.teixeira@gmail.com">lucas.teixeira@gmail.com</a>
  */
 package dao;
 
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
 public class NomesDao extends JdbcBaseDao {
     
     public String getRandomName() throws Exception {
         PreparedStatement ps = conn().prepareStatement("SELECT * FROM NOMES;");
         ResultSet rs = ps.executeQuery();
         List l = new ArrayList();
         while (rs.next()) {
             l.add(rs.getString(1));
         }
         if (l.size() > 0) {
             Collections.shuffle(l);
             return (String) l.get(0);
         } else 
             return null;
     }
 
 }


JdbcBaseDao
Code:
/*
  * Criado em 29/08/2005
  * Lucas Frare Teixeira
  * <a href="mailto:lucas.teixeira@gmail.com">lucas.teixeira@gmail.com</a>
  */
 package dao;
 
 import java.sql.Connection;
 
 import util.ConexaoUtils;
 
 public class JdbcBaseDao {
     
     private Connection conn;
     
     protected Connection conn() throws Exception {
         if (conn == null || conn.isClosed()) {
             conn = ConexaoUtils.conexaoCorrente();
         }
         return conn;
     }
 
 }
 


ConexaoUtils
Code:
/*
  * Criado em 29/08/2005
  * Lucas Frare Teixeira
  * <a href="mailto:lucas.teixeira@gmail.com">lucas.teixeira@gmail.com</a>
  */
 package util;
 
 import java.sql.Connection;
 
 public class ConexaoUtils {
     
     private static final ThreadLocal LOCAL = new ThreadLocal();
 
     public static Connection conexaoCorrente() throws Exception{
         Connection con = (Connection) LOCAL.get();
         if (con == null || con.isClosed()) {
             con = ConnectionPool.getConnection();
             LOCAL.set(con);
         }
         return con;
     }
 
     public static void closeConnection() throws Exception{
         Connection con = (Connection) LOCAL.get();
         if (con != null && !con.isClosed()) {
             con.close();
         }
         LOCAL.set(null);
     }
 }


ConnectionPool
Code:
/*
  * Criado em 29/08/2005
  * Lucas Frare Teixeira
  * <a href="mailto:lucas.teixeira@gmail.com">lucas.teixeira@gmail.com</a>
  */
 package util;
 
 import java.sql.Connection;
 import java.sql.DriverManager;
 
 public class ConnectionPool {
     
     public static boolean loaded = false;
     
     public static Connection getConnection() throws Exception {
         //Aqui a idéia é usar um pool como o C3P0, 
         //ou outro framework de IoC para a injeção de um DataSource
         
         if (!loaded) {
             Class.forName("com.mysql.jdbc.Driver");
         }
         
         return DriverManager.getConnection(
                     "jdbc:mysql://localhost/mentawai",
                     "root",
                     ""
                 );
         
     }
 
 }
 


ApplicationManager
Code:
/*
  * Criado em 29/08/2005
  * Lucas Frare Teixeira
  * <a href="mailto:lucas.teixeira@gmail.com">lucas.teixeira@gmail.com</a>
  */
 
 import org.mentawai.core.ActionConfig;
 import org.mentawai.core.Forward;
 
 import filter.ConnectionFilter;
 
 import action.ConexaoAction;
 
 public class ApplicationManager extends org.mentawai.core.ApplicationManager {
     
     public void loadActions() {       
         ActionConfig ac = new ActionConfig("/Conexao", ConexaoAction.class)
                 .addConsequence(ConexaoAction.SUCCESS, new Forward("/ok.vm"))
                 .addConsequence(ConexaoAction.ERROR, new Forward("/erro.vm"));
         ac.addFilter(new ConnectionFilter());
         addActionConfig(ac);
     }
 }


ConnectionFilter
Code:
/*
  * Criado em 29/08/2005
  * Lucas Frare Teixeira
  * <a href="mailto:lucas.teixeira@gmail.com">lucas.teixeira@gmail.com</a>
  */
 package filter;
 
 import org.mentawai.core.ActionException;
 import org.mentawai.core.Filter;
 import org.mentawai.core.FilterException;
 import org.mentawai.core.InvocationChain;
 
 import util.ConexaoUtils;
 
 public class ConnectionFilter implements Filter {
 
     public String filter(InvocationChain invocationChain) throws FilterException, ActionException {
         String r = invocationChain.invoke();
         
         try {
             ConexaoUtils.closeConnection();    
         } catch (Exception e) {
             throw new FilterException(e);
         }
         
         return r;
     }
 
     public void destroy() { }
 }
 

Lucas Frare de A. Teixeira
lucas.teixeira@gmail.com
[Email]
Lucas Teixeira


[Avatar]

Joined: 09/06/2005 18:56:48
Messages: 9
Location: Londrina - PR
Offline

Os arquivos.... (mais fácil)
É um projeto do eclipse, só descompactar e adicionar os JARs do mentawai, do velocity e do mysql-connector.


(:
 Filename menta_conexao.zip [Disk] Download
 Description
 Filesize 10 Kbytes
 Downloaded:  400 time(s)


Lucas Frare de A. Teixeira
lucas.teixeira@gmail.com
[Email]
saoj



Joined: 01/07/2005 09:59:17
Messages: 2846
Location: Rio de Janeiro, RJ
Offline

Fala Lucas...

Mandou muito bem. Eu tenho algumas considerações. Talvez eu esteja falando bobagem, mas aí vai:

Vc está acoplando o seu DAO ao ConexaoUtils. É menos mal do que acoplar ao ConnectionHandler do Mentawai, mas ainda não é o mais desacoplado possível.

O ideal seria que o seu DAO tivesse que receber uma conexão, via IoC (injection), toda a vez que ele for ser utilizado.

Primeira coisa que precisamos definir é:

Seu DAO vai ser instanciado uma vez só e reutilizado por todas as requisições, ou cada requisição terá a sua própria instância de DAO ???

Acho que na maioria dos casos, faz mais sentido reutilizar a mesma instância, mas como aqui estaremos utilizando uma conexão diferente para cada requisição, logo precisamos ter uma instanciao do Dao para cada requisição.

Code:
 DefaultComponent nomesDao = new DefaultComponent(NomesDao.class);
 // ...
 ac.addFilter(new IoCFilter(nomesDao, "nomesDao", IoCFilter.REQUEST));
 


Isso significa que para cada requisição, o IoCFilter vai chamar o método getInstance() do Component, isto é, vai criar uma nova instancia do seu DAO.

O que eu penso é o seguinte:

Depois do IoCFilter vc coloca um ConnectionFilter. Logo agora temos o Dao e a Connection no Input. Tudo que nos resta fazer agora é fazer um filtro maroto que pega essa connection e injeta no Dao.

Code:
 public class ConnToDaoFilter implements Filter {
 
     private String daoName;
     
     public ConnToDaoFilter(String daoName) {
         this.daoName = daoName;
     }
 
     public String filter(InvocationChain chain) throws FilterException, ActionException {
         Action action = chain.getAction();
         Input input = action.getInput();    
         BaseDao dao = (BaseDao) input.getValue(daoName);
         Connection conn = (Connection) input.getValue(ConnectionFilter.KEY);
         
         dao.setConnection(conn);
         chain.invoke();
 }
 
     public void destroy() { }
 }
 


Assim o seu DAO fica limpinho, só acoplado ao Connection e o connection filtro faz todo o trabalho sujo de retornar a conexão.

O que achou ??? Faz sentido ???




Sergio Oliveira

passos


[Avatar]

Joined: 23/07/2005 16:04:09
Messages: 48
Offline

Ai. Essa brincadeira ficou linda!

A programação hoje é uma corrida entre os engenheiros de software que lutam para construir programas maiores e mais à prova de idiotas e o universo tentando produzir idiotas maiores e melhores. Até então, o universo está vencendo (Rick Cook)
saoj



Joined: 01/07/2005 09:59:17
Messages: 2846
Location: Rio de Janeiro, RJ
Offline

Uma observação:

Devido a essa situação, criei a função:

addGlobalFilter(Filter f, boolean last)

pra adicionar um filtro global NO FINAL da cadeia de execução.

É que por default os filtros globais executam antes dos filtros específicos das actions, e esse filtro ConnToDao é um caso onde executar ele por último é melhor.


Sergio Oliveira

Lucas Teixeira


[Avatar]

Joined: 09/06/2005 18:56:48
Messages: 9
Location: Londrina - PR
Offline

Talvez pensando um pouco "a là WW", que tal a criação de Pilhas de Filters?

Tipo....

Code:
 FilterStack filterStack = new FilterStack();
 
 filterStack.addFilter(new FulanoFilter());
 filterStack.addFilter(new CiclanoFilter());
 filterStack.addFilter(new BeltranoFilter());
 
 ActionConfig ac = //ActionConfig.....
 ac.addFilter(filterStack);



Isso é muito útil caso vc queria colocar os mesmos 8634984 filters para várias actions (:
Que tal Sérgio?

Lucas Frare de A. Teixeira
lucas.teixeira@gmail.com
[Email]
saoj



Joined: 01/07/2005 09:59:17
Messages: 2846
Location: Rio de Janeiro, RJ
Offline

É uma boa sim.

Na próxima versão, as funções:

addFilter

e

addGlobalFilter

vão aceitar tb uma java.util.List de filtros.

Isso já está no CVS !!!


Sergio Oliveira

RubemAzenha


[Avatar]
Joined: 30/06/2005 23:12:02
Messages: 472
Location: São Paulo, SP
Offline

Acho melhor adicionar um list do que criar uma coisa que por dentro deve encapsular um list...

Tem alguma vantagem de criar uma classe FilterStack ???


Mentawai Developer
[WWW] [MSN]
Lucas Teixeira


[Avatar]

Joined: 09/06/2005 18:56:48
Messages: 9
Location: Londrina - PR
Offline

RubemAzenha wrote:
Acho melhor adicionar um list do que criar uma coisa que por dentro deve encapsular um list...

Tem alguma vantagem de criar uma classe FilterStack ???
 


Hum... Acredito que não, List talvez seja a melhor coisa mesmo...

Talvez mais pra frente se precisar de algum tratamento mais bruto, aí sim uma classe pra encapsular cairia bem, mas acredito que com o List seria suficiente!

Hum.... Agora tenho minhas dúvidas quanto à hora de sair adicionando isso...

Code:
List filters = new ArrayList();

E agora?
Code:
filters.add(new FulanoFilter());

ou
Code:
filters.add(br.com.lucastex.filters.FulanoFilter.class);

Com o class ficaria melhor né... Ai na hora de serem verificados, o Mentawai poderia gerar as instâncias destes Filters, uma vez que a mesma lista de Filters poderá ser adicionada em diversas Actions diferentes!

(:

Lucas Frare de A. Teixeira
lucas.teixeira@gmail.com
[Email]
saoj



Joined: 01/07/2005 09:59:17
Messages: 2846
Location: Rio de Janeiro, RJ
Offline

Atualmente só suporta instâncias de filtros, e não a classe do filtro.

A questão é que muitas vezes você vai precisar passar parametros para essas instâncias:

Ex:

Filter f = new ConnToDaoFilter("nomesDao");

Nada te impede de compartilhar a mesma instancia de filtro entre várias actions. desde que o filtro seja thread-safe.




Sergio Oliveira

 
Forum Index -> Comentários Gerais
Go to:   
Powered by JForum 2.1.6 © JForum Team