[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!

Comparação Mentawai x Seam  XML
Forum Index -> F.A.Q.
Author Message
saoj



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

Esse artigo tem como intuito analisar a mesma aplicação web implementada por dois frameworks bastante diferentes: Mentawai e Seam.

A implementação com o Seam pode ser analisada e baixada aqui:
http://www.infoq.com/articles/jboss-seam

A implementação com o Mentawai pode ser baixada aqui: (Utilizamos o banco de dados embarcado H2 de forma que vc não precisa se preocupar em criar/configurar o seu próprio banco de dados para esse exemplo)
http://www.mentaframework.org/files/Menta_versus_Seam.zip

Alguns pontos que vc deve observar em relação a implementação com o Mentawai:

Foi difícil configurar um pool de conexões? Duas linhas de código e temos um pool de conexões configurado sem qualquer enrolação.

Repare como a arquitetura da aplicação com o Mentawai fica bem dividida e organizada. Temos nossas actions, entities, repositórios (ou DAOs), implementações para os nossos repositórios (ou DAOs) e nossos serviços onde as regras do negócio serão utilizadas.

Repare que vc utilizou Inversão de Controle de uma maneira bem simples e fácil, sem precisar de Spring ou outro framework. No ApplicationManager estamos dizendo que o nosso UserRepository será do tipo H2UserRepository. Se amanhã quizermos utilizar um OracleUserRepository, tudo que temos que fazer é mudar a configuração de IoC (1 linha).

Repare que vc utilizou Auto-Wiring de uma maneira bem simples e fácil, sem precisar de Spring ou outro framework. No ApplicationManager estamos dizendo que tudo que precisar de uma Connection irá receber automaticamente uma Connection. No nosso exemplo o H2UserRepository vai receber essa connection via auto-wiring.

Repare que as configurações da sua aplicação ficam centralizadas em um único lugar e que a linguagem utilizada na configuração é o Java (poderia ser outras linguagens também).

Repare que para algumas configurações bem burras e estáticas podemos utilizar também o arquivo properties default chamado AppManager.properties que deve estar no diretório WEB-INF. (mais info sobre isso aqui)

Repare como foi fácil persistir o objeto User no banco de dados utilizando o MentaBean. Repare que a sua entidade e o seu modelo de negócios não receberam qualquer tipo de anotação, em outras palavras, estão totalmente desacopladas do framework web.

Repare que vc não precisou escrever uma linha de SQL para persistir o seu objeto User no banco de dados e que se amanhã vc quiser mudar o seu mecanismo de persistencia para Hibernate, IBatis, JPA, Arquivos Texto, Lista em Memória, etc. tudo que vc tem que fazer é implementar uma nova versão do UserRepository e reconfigurar o seu IoC.

Repare como a sua action UserAction é bem simples e como vc pode validar campos, obter objetos (Pojos) populados com os dados do seu formulário, retornar facilmente vários resultados, criar várias inner actions, etc.

Repare que na camada de apresentação o Mentawai te oferece diversas tags simples e poderosas como mtw:outError, mtw:list, mtw:loop, mtw:isEmpty, etc. Nada te impede de usar JSTL ou EL, mas as tags do Mentawai são bem mais simples, poderosas e com menos verbosidade pois se encaixam perfeitamente ao framework, enquanto as JSTL são genéricas para todos os frameworks. (Migre o hello.jsp para JSTL e analise por si mesmo!)

Se amanhã eu quiser colocar a minha aplicação num ambiente distribuído (pouco provável), tudo que eu tenho que fazer é transformar os meus services em web services. (recomendamos xFire para isso) Partir do pressuposto que toda aplicação web vai ser integrada com EJB como o Seam faz não é legal. Dá para entender porque ele faz isso: JBoss

Estrutura da Aplicação e Classes:



User.java
Code:
 package org.helloworld.entity;
 
 import java.io.Serializable;
 
 public class User implements Serializable {
    
    private int id;
    
    private String name;
    
    public User() { }
    
    public User(int id, String name) {
       this.id = id;
       this.name = name;
    }
    
    public User(int id) {
       this.id = id;
    }
    
    public int getId() { return id; }
    
    public String getName() { return name; }
    
    public void setId(int id) { this.id = id; }
    
    public void setName(String name) { this.name = name; }
 }
 


UserRepository.java
Code:
 package org.helloworld.repository;
 
 import java.util.Collection;
 
 import org.helloworld.entity.User;
 
 public interface UserRepository {
    
    public User getById(int id);
    
    public Collection<User> getUsers();
    
    public void add(User user);
    
 }
 


H2UserRepository.java
Code:
 package org.helloworld.repository.h2;
 
 import java.sql.Connection;
 import java.util.Collection;
 
 import org.helloworld.entity.User;
 import org.helloworld.repository.UserRepository;
 import org.mentawai.bean.BeanSession;
 import org.mentawai.bean.jdbc.H2BeanSession;
 
 public class H2UserRepository implements UserRepository {
    
    private BeanSession session; // mentabean...
    
    // Injeção vai acontecer aqui... (Auto-Wiring)
    public void setConn(Connection conn) {
       
       this.session = new H2BeanSession(conn);
    }
    
    public User getById(int id) {
       
       User u = new User(id);
       
       try {
       
          if (session.load(u)) {
             
             return u;
          }
          
          return null;
          
       } catch(Exception e) {
          
          throw new RuntimeException(e);
       }
    }
    
    public void add(User user) {
       
       try {
          
          session.insert(user);
          
       } catch(Exception e) {
          
          throw new RuntimeException(e);
       }
    }
    
    public Collection<User> getUsers() {
       
       User u = new User(); // no arguments for WHERE clause... (empty user)
       
       try {
       
          return session.loadList(u);
          
       } catch(Exception e) {
          
          throw new RuntimeException(e);
       }
    }
 }
 


UserService.java
Code:
 package org.helloworld.service;
 
 import java.util.Collection;
 
 import org.helloworld.entity.User;
 import org.helloworld.repository.UserRepository;
 
 public class UserService {
    
    // Injeção vai acontecer aqui (IoC)
    private UserRepository userRepo;
    
    public UserService() { }
    
    public void add(User user) {
       
       userRepo.add(user);
    }
    
    public Collection<User> getUsers() {
       
       return userRepo.getUsers();
    }
 }
 


UserAction.java
Code:
 package org.helloworld.action;
 
 import java.util.Collection;
 
 import org.helloworld.entity.User;
 import org.helloworld.service.UserService;
 import org.mentawai.core.BaseAction;
 import org.mentawai.filter.ModelDriven;
 
 public class UserAction extends BaseAction implements ModelDriven {
    
    private UserService userService = new UserService();
    
    public Object getModel() {
       
       return userService;
    }
    
    public String add() {
       
       if (isPost()) { // POST
          
          String name = input.getStringValue("name");
          
          if (name == null || name.trim().equals("")) {
             
             addError("Por favor digite um nome!");
             
             return ERROR;
          }
          
          User u = input.getObject(User.class);
          
          userService.add(u);
          
          return SUCCESS;
          
       } else { // GET
          
          return JSP;
       }
    }
    
    public String list() {
       
       Collection<User> users = userService.getUsers();
       
       output.setValue("users", users);
       
       return SUCCESS;
    }
 }
 


ApplicationManager.java
Code:
 package org.helloworld;
 
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.util.Properties;
 
 import org.helloworld.action.UserAction;
 import org.helloworld.entity.User;
 import org.helloworld.repository.h2.H2UserRepository;
 import org.mentawai.bean.DBTypes;
 import org.mentawai.core.ActionConfig;
 import org.mentawai.core.Context;
 import org.mentawai.db.C3P0ConnectionHandler;
 import org.mentawai.db.ConnectionHandler;
 import org.mentawai.filter.ConnectionFilter;
 import org.mentawai.filter.DIFilter;
 import org.mentawai.filter.InjectionFilter;
 import org.mentawai.filter.IoCFilter;
 
 public class ApplicationManager extends org.mentawai.core.ApplicationManager {
    
    private ConnectionHandler connHandler;
    
    @Override
    public void init(Context application) {
       
       Properties prop = getProperties();
       
       String driver = prop.getProperty("driver");
       String url = prop.getProperty("url");
       String user = prop.getProperty("user");
       String pass = prop.getProperty("pass");
       
       // Configuração do pool de conexões (C3P0)
       connHandler = new C3P0ConnectionHandler(driver, url, user, pass);
       
       // Cria a tabela, se ela não existe...
       createTable(connHandler);
       
       // Satck de filtros
       filter(new ConnectionFilter(connHandler)); // pool de conexões...
       filter(new IoCFilter()); // ioc container...
       filter(new DIFilter());  // auto-wiring...
       filter(new InjectionFilter()); // action/model injection...
       
       // IoC
       ioc("userRepo", H2UserRepository.class);
       
       // Auto-wiring
       di("conn", Connection.class);
       
    }
    
    @Override
    public void loadBeans() {
       
       // ORM mapping...
       bean(User.class, "Users")
          .pk("id",      DBTypes.AUTOINCREMENT)
          .field("name", DBTypes.STRING);
    }
    
    @Override
    public void loadActions() {
       
       // configure list inner action...
       ActionConfig listAction = action(UserAction.class, "list")
          .on(SUCCESS,   fwd("/hello.jsp"));
       
       // configure add inner action...
       // (depois de adicionar queremos ir para listar)
       action(UserAction.class, "add")
          .on(JSP,       chain(listAction))
          .on(SUCCESS,   chain(listAction))
          .on(ERROR,     chain(listAction));
       
    }
    
    protected void createTable(ConnectionHandler connHandler) {
       
       Connection conn = null;
       
       PreparedStatement stmt = null;
       
       try {
          
          conn = connHandler.getConnection();
          
          stmt = conn.prepareStatement("create table if not exists Users(id " +
 "INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100))");
          
          stmt.executeUpdate();
          
       } catch(Exception e) {
          
          System.err.println("Cannot create table: " + e.getMessage());
          
          throw new RuntimeException(e);
          
       } finally {
          
          if (stmt != null) try { stmt.close(); } catch(Exception e) { }
          
          connHandler.release(conn);
       }
    }
    
 }
 


AppManager.properties
Code:
 
 driver = org.h2.Driver
 url = jdbc:h2:~/helloworld
 user = helloworld
 pass = helloworld
 
 


hello.jsp
Code:
 <%@page contentType="text/html;charset=UTF-8" %>
 <%@taglib uri="/WEB-INF/lib/mentawai.jar" prefix="mtw" %>
 
 <html><body>
 
 <h1>Mentawai Hello World</h1>
 
 <h3>Please enter you name:</h3>
 
 <mtw:outError><font color="red"><mtw:out /></font><br /><br /></mtw:outError>
 
 <form action="<mtw:contextPath />/UserAction.add.mtw" method="post">
 <mtw:input name="name" type="text" size="20" /> <br /><br />
 <input type="submit" value="Say Hello" />
 </form>
 
 <h2>The following persons have said "hello" to Mentawai:</h2>
 
 <mtw:list value="users">
    <mtw:isEmpty>
       <h4>Nobody has said so yet!</h4>
    </mtw:isEmpty>
 
    <mtw:loop var="user">
       <mtw:out value="user.name" /><br />
    </mtw:loop>
 </mtw:list>
 
 </body></html>
 


web.xml
Code:
 <?xml version="1.0" encoding="ISO-8859-1"?>
 
 <!DOCTYPE web-app
     PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     "http://java.sun.com/dtd/web-app_2_3.dtd">
 
 <web-app>
 	<display-name>HelloWorld</display-name>
 	<description></description>
 
 	<servlet>
         <servlet-name>Controller</servlet-name>
         <servlet-class>org.mentawai.core.Controller</servlet-class>
         <init-param>
     	    <param-name>applicationManager</param-name>
         	<param-value>org.helloworld.ApplicationManager</param-value>
 	    </init-param>    
 		<load-on-startup>1</load-on-startup>		
     </servlet>
 
     <!-- You must choose an extension to indicate a mentawai action -->
     <servlet-mapping>
         <servlet-name>Controller</servlet-name>
         <url-pattern>*.mtw</url-pattern>
     </servlet-mapping>
     
     <filter>
         <filter-name>DebugFilter</filter-name>
         <filter-class>
             org.mentawai.util.DebugServletFilter
         </filter-class>
     </filter>
     
     <filter-mapping>
         <filter-name>DebugFilter</filter-name>
         <url-pattern>*.jsp</url-pattern>
         <dispatcher>REQUEST</dispatcher> 
         <dispatcher>FORWARD</dispatcher>    
         <dispatcher>INCLUDE</dispatcher>    
         <dispatcher>ERROR</dispatcher>    
     </filter-mapping>
 
 </web-app>
 


Sergio Oliveira

saoj



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

Seguindo os excelentes comentários feitos por Rodrigo Yoshima a respeito da necessidade ou não de um action, estamos listando uma versão modificada da aplicação acima que não faz uso de nenhuma action.

A questão da "action: ter ou não ter" é bastante interessante e vai depender da necessidade de se trabalhar com os detalhes do protocolo web HTTP, por exemplo: cookies, session, headers, ip, post/get, etc. Também vai depender do gosto de cada um e da necessidade de melhor controlar a ligação entre requisição WEB e FACHADA/SERVICO.

Lembramos que menos código não é necessariamente código melhor, mas menos código geralmente pode ser traduzido para menos trabalho em cima do desenvolvedor.

As modificações são as seguintes:

UserAction.java: (sai de cena, pode apagar)

UserService.java:

Code:
 package org.helloworld.service;
 
 // imports
 
 public class UserService {
    
    // Injeção vai acontecer aqui (IoC)
    private UserRepository userRepo;
    
    // Propriedade para exibição...
    private Collection<User> users;
    
    public UserService() { }
    
    public void add(User user) {
       
       userRepo.add(user);
    }
    
    public void list() {
       
       users = userRepo.getUsers();
    }
    
    public Collection<User> getUsers() {
       
       return users;
    }
 }
 


ApplicationManager.java: (mudanças estão comentadas!)

Code:
 package org.helloworld;
 
 // imports...
 
 public class ApplicationManager extends org.mentawai.core.ApplicationManager {
    
    private ConnectionHandler connHandler;
    
    @Override
    public void init(Context application) {
       
       Properties prop = getProperties();
       
       String driver = prop.getProperty("driver");
       String url = prop.getProperty("url");
       String user = prop.getProperty("user");
       String pass = prop.getProperty("pass");
       
       // Configuração do pool de conexões (C3P0)
       connHandler = new C3P0ConnectionHandler(driver, url, user, pass);
       
       // Cria a tabela, se ela não existe...
       createTable(connHandler);
       
       // Stack de filtros
       filter(new ConnectionFilter(connHandler)); // pool de conexões...
       filter(new IoCFilter()); // ioc container...
       filter(new DIFilter());  // auto-wiring...
       filter(new InjectionFilter()); // action/model injection...
       filter(new OutputFilter()); // ==========================> Mudança
       
       // IoC
       ioc("userRepo", H2UserRepository.class);
       
       // Auto-wiring
       di("conn", Connection.class);
       
    }
    
    @Override
    public void loadBeans() {
       
       // ORM mapping...
       bean(User.class, "Users")
          .pk("id",      DBTypes.AUTOINCREMENT)
          .field("name", DBTypes.STRING);
    }
    
    @Override
    public void loadActions() {
       
       // MUDANÇA = POJO Actions
       // ==== Estamos usando POJO Actions aqui =====
       
       // configure list inner action...
       ActionConfig listAction = action(UserService.class, "list")
          .on(SUCCESS,   fwd("/hello.jsp"));
       
       // configure add inner action...
       // (depois de adicionar queremos ir para listar)
       action(UserService.class, "add")
          // Mudança
          // a partir da vs 1.12 não será necessário
          .filter(new VOFilter("user", User.class)) 
          .on(JSP,       chain(listAction))
          .on(SUCCESS,   chain(listAction))
          .on(ERROR,     chain(listAction));
       
    }
    
    protected void createTable(ConnectionHandler connHandler) {
      
        // mesma coisa...
      
    }   
 }
 


As alterações mais importantes são no ApplicationManager que agora está utilizando o OutputFilter para disponibilizar todas as propriedades da action na camada view. (coloca todas as propriedades no output da action).

Repare que estamos utilizando POJO ACTIONS, ou seja, qualquer classe Java poderá se comportar como uma action do Mentawai. Veja que o método add automaticamente vai passar o user que está no input da action como parametro.

Utilizamos o VOFilter para criar o objeto User a partir dos dados do input da action (formulário). A partir da versão 1.12 do Mentawai, esse filtro não será mais necessário nessa situação, pois o Mentawai vai tentar criar um objeto on-the-fly a partir do tipo do parametro e dos dados do input.

Um war com a aplicação modificada pode ser baixada aqui: http://www.mentaframework.org/files/HelloMentawai2.war

Sergio Oliveira

 
Forum Index -> F.A.Q.
Go to:   
Powered by JForum 2.1.6 © JForum Team