Meu Primeiro Projeto Web com Futurepages2 - Parte 1

Neste Tutorial iremos criar um pequeno projeto Web com Futurepages2 para cadastro de Alunos. A seguir temos os requisitos do sistema:

  • A aplicação possuirá as seguintes telas:
    • A tela inicial do sistema, onde mostrará dois links: Listar os Alunos e Criar Novo Aluno ;
    • A tela de listagem de alunos ordenados pelo nome;
    • Tela de criação de novo aluno .
  • A aplicação contemplará as seguintes críticas :
    • Aluno possuirá os campos nome completo e matrícula;
    • O sistema não permitirá a inserção de alunos com matrícula já existente;
    • O sistema não permitirá criação de alunos com nome não especificado;
    • O sistema não permitirá criação de alunos com matrícula não especificada.
  • Quanto ao template :
    • Cabeçalho e rodapé constantes em todas as telas.

Necessário

  • Conhecimentos de Programação Web, Orientação a Objeto com Java e teoria de Banco de Dados;
  • Realizar o passo-a-passo do tópico a seguir;
  • Seguir o padrão dos nomes das pastas, pacotes e arquivos especificados neste tutorial.

Passo 1 - Construindo a Primeira Tela

De posse de todos os arquivos descritos anteriormente e com o Tomcat devidamente instalado, siga os seguintes passos

Configure o espaçamento da IDE

Como possuímos padronização e liberdade como principais valores no futurepages, os projetos podem rodar em qualquer IDE que ofereça suporte a JEE (assim tendo liberdade de escolha). Este tutorial é desenvolvido baseado no NetBeans para que não percamos a padronização de nosso código e para que seja possível numa mesma equipe existir usuários de IDEs diferentes é necessário que padronizemos a configuração dos espaçamentos do código-fonte da IDE.

Se você é usuário do NetBeans, configure como ilustrado na imagem, caso contrário procure fazer o equivalente em sua IDE.

Vá em Ferramentas → Opções → Editor → Formatando

Crie um Novo Projeto Web

  • Vá em Arquivo → Novo Projeto → Categorias: Java Web → Projetos: Aplicação Web → Siga em frente:nb_projeto_web.jpg
  • Defina o nome do projeto como alunoonline, escolha o local onde ficará, mantenha marcado somente “Configurar como Projeto Principal” e siga em frente;
  • Não marque nenhum framework e pressione “Finalizar”;

Altere as propriedades padrões do projeto

  • Em Código-Fonte :
    • Selecione o diretório “src\java” e o remova;
    • Clique em “Adicionar pasta…”, selecione “src” e clique em “Abrir”;
    • Fonte/binário: JDK 5 ;
    • Codificação: ISO -8859-1 ; nb_propriedades_codigo.jpg
    • Se quiser utilizar Charset UTF-8, siga as instruções deste link.
  • Em Bibliotecas:
    • Na aba Compilar, clique em adicionar “JAR/Pasta” e escolha todos os arquivos de biblioteca que você baixou (futurepage2.XXX.jar e a respectiva lib (futurepages2-Lib). ATENÇÃO: para nosso tutorial só será necessário adicionar os arquivos existentes na Raiz da desta pasta LIB.
  • Em Executar:
    • Desmarque “Implantar ao Salvar” (não desmarcar este item poderá causar muito transtorno)

Configure sua aplicação para rodar sobre o futurepages2

Configure o arquivo em WEB-INF/web.xml para rodar o futurepages2 na inicialização como mostrado a seguir:

  • Este arquivo é o descritor que deve existir em toda aplicação JEE. Nele devem/podem ser definidos configurações WEB como servlets, listeners, filtros, params, etc do JEE.
<?xml version="1.0" encoding="ISO-8859-1"?>
 
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
	<display-name>Aluno On Line</display-name>
 
        <!-- Tempo padrão da sessão http do usuário -->
	<session-config>
		<session-timeout>10</session-timeout>
	</session-config>
 
        <!-- Servlet Controlador - responsável pelas Actions -->
	<servlet>
		<servlet-name>Controller</servlet-name>
		<servlet-class>org.futurepages.core.control.Controller</servlet-class>
		<init-param>
			<param-name>applicationManager</param-name>
			<param-value>org.futurepages.core.ApplicationManager</param-value>
		</init-param>
	</servlet>
 
        <!-- Servlet responsável pelos Templates -->
        <servlet>
		<servlet-name>TemplateServlet</servlet-name>
		<servlet-class>org.futurepages.core.template.JspTemplateServlet</servlet-class>
		<init-param>
			<param-name>TemplateManager</param-name>
			<param-value>org.futurepages.core.template.TemplateManager</param-value>
		</init-param>
	</servlet>
 
        <!-- Inicializador da aplicação Futurepages -->
	<listener>
		<description>Application Listener</description>
		<listener-class>org.futurepages.core.ApplicationListener</listener-class>
	</listener>
 
        <!-- Gerenciador de eventos na criação e destruição de sessões http de usuário -->
	<listener>
		<listener-class>org.futurepages.core.session.SessionListener</listener-class>
	</listener>
 
        <!-- Mapeamentos de actions -->
	<servlet-mapping>
		<servlet-name>Controller</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
 
	<servlet-mapping>
		<servlet-name>Controller</servlet-name>
		<url-pattern>*.fpg</url-pattern>
	</servlet-mapping>
 
        <!-- Mapeamentos de templates -->
	<servlet-mapping>
		<servlet-name>TemplateServlet</servlet-name>
		<url-pattern>*.page</url-pattern>
	</servlet-mapping>
</web-app>


Note que neste arquivo estão sendo informados que…

  • a aplicação será inicializada pelo Listener do Futurepages (ApplicationListener), onde serão feitas as inicializações e automações do framework.
  • as requisições mapeadas por *.fpg (actions) e *.page (template) serão capturadas pelos Servlets do FuturePages

Adicione os arquivos de Configurações

Parâmetros da Aplicação

Crie em SRC o arquivo conf/app-params.xml com o seguinte conteúdo. A aplicação possui parâmetros padrões, neste arquivo indicaremos algum parâmetro de configuração. Mais a frente veremos alguns destes parâmetros.

<?xml version="1.0" encoding="ISO-8859-1"?>
 
 
<app-params-configuration>
 
   <!-- Em breve alguns parâmetros serão adicionados. -->
 
</app-params-configuration>


Configuração do Template

Crie em SRC conf/app-template.xml com o seguinte conteúdo:

<?xml version="1.0" encoding="ISO-8859-1"?>
 
 
<app-template-configuration>
    <page rule="^.*$" base="template/layout.jsp">
        <block id="cabecalho"   value="template/cabecalho.jsp"/>
        <block id="rodape"      value="template/rodape.jsp"/>
     </page>
</app-template-configuration>


O arquivo app-template.xml é responsável pela configuração de Template da aplicação - gerenciado pelo TemplateManager ;

  • Cada tag page determina como um determinado grupo de páginas chamadas pelo padrão RULE.page se comportarão;
  • O atributo rule da tag page é uma expressão regular cuja regra específica restringe quais arquivos serão montados com aquele arquivo esqueleto (atributo base ) e com seus blocos internos (tags block );
  • As tags block formam os blocos do layout;
  • Se por exemplo eu chamar o arquivo pasta/Arquivo.page , então meu TemplateManager irá varrer o arquivo template/layout.jsp para incluir nos blocos estáticos (cabecalho e rodape ) o conteúdo dos arquivos especificados no arquivo de configuração; além de colocar no bloco dinâmico “body” o conteúdo do arquivo pasta/Arquivo.jsp .

Crie seus arquivos de template na pasta Web:

template/layout.jsp :

<%@ taglib uri="futurepagesApp" prefix="fpg"%>
<%@ page language="java" contentType="text/html; charset=iso-8859-1" pageEncoding="iso-8859-1"%>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
    <head>
        <title>Aluno Online QuickStart Application</title>
    </head>
 
    <body>
        <fpg:block id="cabecalho"/>
        <hr/>
	<fpg:block id="body"/>
        <hr/>
	<fpg:block id="rodape"/>
    </body>
</html>


template/cabecalho.jsp :

<%@ taglib uri="futurepagesApp" prefix="fpg"%>
 
<div style="text-align:center; font-size: 20px">
    <a href="<fpg:contextPath/>/">Aluno Online QuickStart Application</a>
</div>


template/rodape.jsp :

<div style="text-align:center; font-size: 10px">
    (c) 2008-2010 - Workset - Todos os Direitos Reservados
</div>


Neste momento você deve ter notado que a uri “futurepagesApp” não foi reconhecida pela IDE. Mais adiante será mostrado o que deve ser feito para que a taglib seja reconhecida.

Crie a Ação e a Tela Inicial da Aplicação

Alterações do SRC (JAVAs)
  • Exclua o pacote java que o NetBeans criou dentro do Pacote de Códigos-Fonte(SRC).
  • Crie o pacote init e dentro dele a classe Index.java (init.Index) como se segue:
package init;
 
import org.futurepages.actions.FreeAction;
import org.futurepages.util.DateUtil;
 
/**
 * Ação Inicial da Aplicação
 */
public class Index extends FreeAction{
 
    @Override
    public String execute(){
        output.setValue("mensagemInicial","Seja bem-vindo. Data atual: "+DateUtil.viewToday("dd/MM/yyyy"));
        return SUCCESS;
    }
}


* Esta classe é uma FreeAction do Futurepages, pois não precisaremos de usuário logado para acessá-la.

  • Ela mostrará uma saudação inicial e a data atual do servidor na tela de saída da action.
Alterações da WEB (JSPs)
  • Crie na pasta WEB uma nova pasta init e dentro dela crie o arquivo Index.jsp(init/Index.jsp);
  • O arquivo mostrará uma saudação inicial e os links para as outras telas do sistema. As URLs ainda serão alteradas nos próximos passos;
  • Coloque o seguinte conteúdo do arquivo init/Index.jsp:
<%@taglib uri="futurepagesApp" prefix="fpg"%>
 
<div style="text-align:center">
 
    <h1><fpg:out value="mensagemInicial"/></h1>
    <br/>
    <br/>
    <a href="javascript:void(0)">Novo Aluno</a>
    <br/>
    <br/>
    <a href="javascript:void(0)">Listar Alunos</a>
    <br/>
    <br/>
 
</div>


Altere o arquivo /index.jsp para que ele redirecione para sua ação inicial:

<meta http-equiv="refresh" content="0;URL=Index.fpg"/>


Você acabou de criar a ação inicial padrão da aplicação Futurepages.

  • Estes caminhos todos podem ser configurados no arquivo app-params.xml:
    • START_PAGE_NAME=“Index” , INIT_ACTION=“init.Index” e START_CONSEQUENCE=“init/Index.page” são valores default dos parâmetros.
  • Para mais detalhes, acesse a Especificação dos Parâmetros do Futurepages2.
  • Mande Executar o projeto (F6).
  • Se você realizou os passos corretamente e nada de inesperado ocorreu, você visualizará uma tela similar a esta:

tela_inicial.jpg

Reconhecimento da taglib
  • Provavelmente sua IDE não está reconhecendo o path da taglib do futurepages (futurepagesApp) nos arquivos JSP, isto ocorre porque o arquivo de taglib das aplicações futurepages são criadas no momento de deploy. Cada aplicação possui um arquivo gerado a partir do esqueleto inicial (tags nativas) somado às tags dos módulos da aplicação (criadas pelo desenvolvedor). Com este recurso do framework não é necessário ficar alterando o xml no braço a cada nova tag criada por você. Assim você cria suas tags seguindo o padrão e deixa o resto por conta do futurepages. (Caso você seja novo no JSP, pesquise a respeito de taglib para entender melhor este tópico)
  • Como o NetBeans gera uma pasta diferente (build) para a aplicação publicada no servidor. O arquivo da taglib é gerada no contexto desta pasta build. Por isso o NetBeans não reconhece a taglib nos JSPs enquanto estamos desenvolvendo na pasta WEB. Temos que copiar o arquivo da pasta build para web, para que nossa IDE, caso queiramos que a IDE reconheça nossa biblioteca de tags geradas.
  • Copie o arquivo taglib.tld que está no local indicado pela figura abaixo:

taglib_origem.jpg

  • Em seguida, cole no local indicado a seguir.

taglib_destno.jpg

  • Abra o arquivo copiado no destino para que o NetBeans reconheça-o mais rapidamente;
  • Abra os arquivos JSP que fazem referência ao path futurepagesApp para verificar se foi encontrado.

Passo 2 - Criando o Modelo

Configure o acesso ao banco de dados

  • Neste tópico iremos configurar o arquivo do nosso contexto para acessar um DataSource (Pool de Conexões gerenciado pelo Tomcat);
  • No nosso exemplo iremos utilizar o banco de dados MySQL. Portanto certifique-se de que o driver do banco de dados do MySQL (“mysql connector”) está presente na pasta common/lib do Tomcat instalado;
  • Crie o banco de dados com as seguintes configurações:
    • nome alunoonlinebd, charset: latin1 e collation: latin1_swedish_ci
  • Não crie tabelas no banco, deixe seu banco vazio, o Futurepages se encarregará de criar a estrutura do banco.
  • O arquivo META-INF/context.xml em WEB guarda informações e recursos do contexto. Um dos recursos que ele gerencia é o DataSource, que é o Pool de Conexões gerenciado pelo Tomcat.

O seu arquivo context.xml gerado pelo NetBeans provavelmente deverá estar similar a este que se segue:

<?xml version="1.0" encoding="ISO-8859-1"?>
 
<Context antiJARLocking="true" path="/alunoonline"/>


Inclua a tag Resource mostrada a seguir dentro da tag Context. Seu arquivo deverá ficar como mostrado a seguir:

<?xml version="1.0" encoding="ISO-8859-1"?>
 
<Context antiJARLocking="true" path="/alunoonline">
 
    <Resource  
            name="jdbc/alunoOnLineDS"
            type="javax.sql.DataSource"
            driverClassName="com.mysql.jdbc.Driver"
            url="jdbc:mysql://localhost:3306/alunoonlinebd"
            username="root"
            password=""
            auth="Container"
            initialSize="4"
            maxActive="-1"
            validationQuery="Select 1"
            testOnBorrow="true"			   
	/>
 
</Context>


Entendendo os parâmetros do DataSource:

Parâmetros do Banco de Dados
name nome do DataSource
driverClassName Driver do Banco de Dados , MySQL neste exemplo
url url para conexão com o banco
username usuário de acesso ao banco de dados
password senha do usuário do banco.


Configure a camada de persistência (Hibernate)

O Futurepages utiliza o Hibernate Annotations como camada de persistência dos dados. ele que faz a ponte entre aplicação e banco de dados. Para utilizar-se deste recurso em nossa aplicação, deveremos configurar o acesso da aplicação ao banco de dados (DataSource) e especificar algumas outras propriedades do Hibernate. Essa configuração é feita através do arquivo hibernate.properties .

Crie em SRC o arquivo conf/hibernate.properties como mostrado a seguir:

 hibernate.dialect = org.hibernate.dialect.MySQLDialect
 hibernate.show_sql = false
 hibernate.format_sql = false
 hibernate.connection.datasource = java:/comp/env/jdbc/alunoOnLineDS


Observe:

  • Na primeira linha é especificado o nome do dialeto SQL que será utilizado. Nosso caso: org.hibernate.dialect.MySQL Dialect
  • Na última linha ele descreve o endereço do DataSource: java:/comp/env/jdbc/alunoOnLineDS que foi definido anteriormente.
  • Este exemplo define o uso de pool de conexões do Tomcat, que é a forma mais indicada de se configurar a conexão com o banco de dados de uma aplicação com muitos usuários. Porém, alternativamente é possível informar diretamente no arquivo hibernate.properties as informações da conexão do banco de dados:
 hibernate.dialect = org.hibernate.dialect.MySQLDialect
 hibernate.show_sql = false
 hibernate.format_sql = false
 hibernate.connection.driver_class=com.mysql.jdbc.Driver
 hibernate.connection.url=jdbc:mysql://localhost/alunoonlinebd
 hibernate.connection.username=root
 hibernate.connection.password=


A vantagem desta última configuração é a possibilidade de realizar testes no ambiente de desenvolvimento Java com maior facilidade. Você poderá executar classes com método main() e conseguirá acessar a base de dados, já que a configuração do banco não estará no Tomcat. Recomendamos, para o desenvolvimento, que utilize este último.

As desvantagens são o baixo desempenho para aplicações com muitos usuários (péssimo para ambiente em produção) e efeitos colaterais no controle do isolamento de transações (Mysql). Este último problema pode ser resolvido da seguinte maneira:

No Windows, no arquivo my.ini adicione ao seu final, uma nova linha com o seguinte comando, salve-o e reinicie o MySQL:

    transaction-isolation=READ-COMMITTED

No Linux, o arquivo é o my.cnf, localize-o na pasta correta de seu mysql. adicione o comando transaction-isolation=READ-COMMITTED no local indicado a seguir:


(...)

# The MySQL server
[mysqld]

default-storage-engine=InnoDB
transaction-isolation=READ-COMMITTED

user                = nobody
port                = 3306
socket                = /opt/lampp/var/mysql/mysql.sock


(...)

Crie os Beans

Beans são aquelas classes do modelo que refletirão nossas entidades do Banco de Dados. No nosso caso, temos somente uma entidade: Aluno. No código SRC , crie o pacote modules.escola.beans e dentro dele crie a classe Aluno.java que refletirá a nossa entidade Aluno.

O arquivo Aluno.java deverá ter o seguinte conteúdo:

package modules.escola.beans;
 
 
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
 
 
@Entity
@Table(name = "escola_aluno")
public class Aluno implements Serializable {
 
 
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int id;
 
 
	@Column(name = "matricula", length = 10, unique = true, nullable = false)
	private String matricula;
 
 
	private String nomeCompleto;
 
 
	public Aluno() {
	}
 
 
	public int getId() {
		return id;
	}
 
 
	public void setId(int id) {
		this.id = id;
	}
 
 
	public String getMatricula() {
		return matricula;
	}
 
 
	public void setMatricula(String matricula) {
		this.matricula = matricula;
	}
 
 
	public String getNomeCompleto() {
		return nomeCompleto;
	}
 
 
	public void setNomeCompleto(String nomeCompleto) {
		this.nomeCompleto = nomeCompleto;
	}
}


Observações
  • Os pacotes modules.moduleName.beans são os Repositórios de Beans do Futurepages, onde moduleName é a string identificadora do módulo. Este padrão deve ser seguido, pois o Futurepages varre-os para anexá-los ao mapeamento do Hibernate. Será nestes pacotes “beans” onde guardaremos nossas classes que refletem entidades do banco de dados.
  • @Entity : diz que essa será uma classe mapeada como entidade numa base de dados.
  • @Table(name=“escola_aluno”) : Por padrão não precisaríamos dizer o nome da tabela, pois o hibernate associaria com a tabela de nome “aluno”. Porém, para aproveitar-se da modularização e reusabilidade entre os módulos, é interessante seguirmos o padrão “moduloNome_entidadeNome” evitando assim conflitos de nomes repetidos de tabelas, além de ajudar na organização da base de dados.
  • @Id : Determina que o atributo mapeado será a chave primária.
  • @GeneratedValue(strategy=GenerationType.IDENTITY) : Este mapeamento determina que o campo associado ao atributo será auto_increment.
  • @Column(name=“matricula”, length=10, unique=true, nullable=false) : Este mapeamento diz que o atributo será associado à coluna “matricula”, será um índice UNIQUE, NOT NULL e será VARCHAR(10) pois o atributo é do tipo String e length=10.
  • Os campos id e nomeCompleto não possuem o mapeamento @Column , mas automaticamente são associados aos campos da tabela com seus respectivos nomes.

Gere a estrutura (esquema) do Banco de Dados

Neste tópico verificaremos se os mapeamentos que fizemos em nossos beans sairam como esperado, iremos gerar as nossas tabelas no banco de dados já criado e configurado. Para isso, basta adicionarmos um parâmetro de configuração no nosso arquivo app-params.xml e executar a aplicação.

  • SCHEMA_GENERATION_TYPE - Parâmetro para Geração do Esquema do Banco de Dados
    • none : Nada é feito (DEFAULT);
    • update : Atualiza novas tabelas e novos campos criados, mantendo o que existe no esquema;
    • export : Atualização agressiva do esquema, gera todo o esquema desconsiderando e até excluindo o esquema existente;

Deixe seu arquivo app-params.xml como se segue:

<app-params-configuration>
 
    <param name="SCHEMA_GENERATION_TYPE"    value="export"/>
 
</app-params-configuration>


Após modificar o arquivo, execute a aplicação. Você visualizará a sql de criação da tabela no log do Tomcat no momento da inicialização:

INFO: Running hbm2ddl schema export
drop table if exists escola_aluno
create table escola_aluno (id integer not null auto_increment,
                           matricula varchar(10) not null unique,
                           nomeCompleto varchar(255),
                           primary key (id))
15/04/2009 19:28:50 org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: exporting generated schema to database


Após executado, troque o valor export do parâmetro por none e deixe-o assim até que seja necessário criar um novo esquema para a base de dados.

Antes de passar para o próximo item, verifique no SGBD se o esquema foi criado como esperado. Você irá visualizar a tabela escola_aluno com os campos devidamente mapeados. Caso contrário, verifique a possibilidade de ter feito algo errado. A estrutura de sua tabela deverá estar como mostrado a seguir:

mysql_alunoonlinebd.jpg

Inserções Iniciais (Install)

O Futurepages oferece um recurso que possibilita a inserção de várias instâncias (linhas) no banco de dados no momento da inicialização da aplicação. Este recurso é o INSTALL MODE (Modo de Instalação) o qual funciona da seguinte maneira:

  • Cada módulo possui ou não seu pacote install (“modules.moduleName.install”). Na raiz deste pacote, todas as classes que herdarem de org.futurepages.core.install.Installer serão executadas na inicialização da aplicação quando INSTALL_MODE = ON.
Parâmetro INSTALL_MODE em app-params.xml:
  • INSTALL_MODE : Instalação de Recursos (pastas/arquivos) ou instâncias na base de dados (linhas em tabelas) - instalações presentes no pacote install * off : Desligado (DEFAULT );
    • on : Ligado.
Ligue o "INSTALL MODE"

Altere o arquivo app-params.xml, deixando-o assim:

<?xml version="1.0" encoding="ISO-8859-1"?>
 
<app-params-configuration>
 
    <param name="SCHEMA_GENERATION_TYPE"    value="none"/>
    <param name="INSTALL_MODE"              value="on"/>
 
</app-params-configuration>


Criando o Installer

Vamos criar alguns alunos de exemplo no nosso projeto, vamos criar nosso instalador e criar as instalações de alguns alunos.

Crie em modules.escola.install a classe AlunosInstaller com o seguinte código-fonte:

package modules.escola.install;
 
import org.futurepages.core.install.Installer;
import org.futurepages.core.persistence.Dao;
import modules.escola.beans.Aluno;
 
/**
 * Instala alunos para testes de exemplos
 *
 */
public class AlunosInstaller extends Installer{
 
    public void execute(){
        Dao.save(new Aluno("03N1047000", "Maria da Silva"));
        Dao.save(new Aluno("02N1047451", "João Fulano Cavalcante Soares"));
        Dao.save(new Aluno("99S5447050", "Antônio Feliciano Gomes"));
        Dao.save(new Aluno("99S5000001", "Sicrano da Silva e Silva"));
        Dao.save(new Aluno("99S8348349", "Maria Antonieta da Costa"));
        Dao.save(new Aluno("93E9394322", "José Pompeu Exemplus da Silva"));
    }
}


Observe:

  • O método execute() é onde ficam as instalações que serão executadas na inicialização da aplicação quando INSTALL_MODE = ON
  • O construtor new Aluno(matricula, nomeCompleto) não existe ainda. Crie-o no seu Bean Aluno:
    public Aluno(String matricula, String nomeCompleto) {
        this.matricula = matricula;
        this.nomeCompleto = nomeCompleto;
    }


Agora temos nosso Instalador devidamente criado e o INSTALL_MODE ligado. Vamos executar nossa aplicação.

No log de inicialização verificaremos a indicação de que o Instalador AlunosInstaller foi executado:

[:install:] BEGIN...
[:install:] module 'escola' installing...
[:install:]>>> installer AlunosInstaller running...
[:install:]>>> installer AlunosInstaller OK.
[:install:] module escola installed.
[:install:] END.


Verifique no banco se os dados estão lá como o esperado. Desligue o INSTALL MODE e continue o seu processo de desenvolvimento.

Passo 3 - Criando o Controle

Crie AlunoActions.java no pacote modules.escola.actions , esta classe refletirá a nossos casos de uso de criação, visualização, edição e deleção de aluno (CRUD). (Neste tutorial só faremos o caso de criação e listagem)

A classe deverá herdar org.futurepages.actions.CrudActions , uma das Actions do Futurepages, a qual possui automatizações para CRUDs.

Esta classe deve possuir um atributo private do tipo modules.escola.beans.Aluno

Lembre-se que todas as suas ações de casos de uso devem herdar uma das actions do futurepages.

Crie a innerAction create()

Crie o método (innerAction) contemplando a criação de um novo aluno, o padrão é create :

package modules.escola.actions;
 
import java.util.List;
import modules.escola.beans.Aluno;
import org.futurepages.actions.CrudActions;
import org.futurepages.core.persistence.Dao;
 
public class AlunoActions extends CrudActions {
 
    private Aluno aluno;
 
    public String create() {
        Dao.saveTransaction(aluno);
        return success("Aluno criado com sucesso");
    }
}


A innerAction create (nas actions, os métodos públicos são chamados de innerAction ) é o método que deverá ser chamado quando o usuário do sistema enviar uma requisição de “salvar novo aluno”. As propriedades do objeto aluno da classe deverão estar preenchidos com os dados do formulário antes do método ser executado. O método ao ser executado, irá inserir o novo aluno na base de dados e retornará uma mensagem de sucesso indicando que o aluno foi criado com sucesso.

Crie o método privado listObjects()

Trata-se do método que realiza a listagem padrão dos beans. Sobrescreva-o na sua action de CRUD. Pois ele deverá ser chamado automaticamente quando a tela de listagem padrão for requisitada. Nos próximos passos entenderemos como isso ocorre.

Adicione em AlunoActions o seguinte método:

    @Override
    protected void listObjects() {
        List<Aluno> alunos = Dao.list(Aluno.class,"","nomeCompleto asc");
        output.setValue("alunos", alunos);
    }


Este método carrega todos os alunos cadastrados na base de dados ordenados pelo nome completo e logo em seguida coloca esta lista no output da action nomeada com a chave “alunos”.

Passo 4 - Criando a Visão

Consideremos “visão” os arquivos JSPs (localizados na pasta WEB), os quais representam as telas do sistema:

Tela de Caso de Uso Arquivo em “WEB/modules/escola/”
Criação de Aluno (Novo Aluno) Aluno.create.jsp
Listagem de Alunos (Explorar com filtros) Aluno.explore.jsp
Edição de Aluno Aluno.update.jsp *
Exclusão de Aluno Aluno.delete.jsp *

* não será apresentado neste tutorial, fica como atividade extra.

Atualize os links para as novas telas

Primeiramente iremos modificar a tela inicial da aplicação para contemplar os links corretos que irão redirecionar o usuário até as telas de cadastro de alunos (Aluno.create.jsp)

Modifique init/Index.jsp

<%@taglib uri="futurepagesApp" prefix="fpg"%>
 
<div style="text-align:center">
    <h1><fpg:out value="mensagemInicial"/></h1>
    <br/>
    <br/>
    <a href="<fpg:modulePath module="escola" />/Aluno.fpg?type=create">Novo Aluno</a>
    <br/>
    <br/>
    <a href="<fpg:modulePath module="escola" />/Aluno.fpg?type=explore">Listar Alunos</a>
    <br/>
    <br/>
</div>


Veja que no arquivo:

  • não foram especificados os nomes dos arquivos *.jsp e nem dos arquivos do template (*.page), pois utilizamos o padrão CRUD para aproveitar as facilidades providas pelo futurepages, no passo seguinte iremos entender como funciona esta requisição utilizando o atributo type
  • foi utilizada a tag modulePath , ela é responsável por retornar o caminho do módulo especificado no parâmetro module . O seu uso é indicado para tornar a aplicação indepedente do nome do contexto da aplicação. No nosso caso esta tag iria retornar /alunoonline/modules/escola
  • não foram incluídos os links da edição e exclusão, pois estes estarão inclusos dentro da tela de listagem.

Crie a tela de Novo Aluno

Crie /modules/escola/Aluno.create.jsp

<%@taglib uri="futurepagesApp" prefix="fpg"%>
 
<div style="text-align:center">
    <h1>Novo Aluno</h1>
    <br/>
    <br/>
 
    <form method="post" action="<fpg:modulePath module="escola" />/Aluno.create.fpg">
        <fpg:bean value="aluno">
            Nome: <input id="nomeCompleto" name="nomeCompleto" value="${aluno.nomeCompleto}" />
			<br/><br/>
            Matrícula: <input id="matricula" name="matricula" value="${aluno.matricula}" />
        </fpg:bean>
        <br/>
        <br/>
        <input type="submit" value="Enviar" />
    </form>
</div>


Note que criamos a tela com o formulário de cadastro do novo aluno, no qual o usuário especificará nome e matrícula, enviando-os via POST para o seguinte endereço:

Nosso objetivo é que AlunoActions.create() seja executada a partir desta requisição, recebendo como entrada o atributo aluno existente na classe AlunoActions preenchido com os campos do formulário, para então persistí-lo na base de dados. Mais a seguir descobriremos como esta “mágica” é feita.

Crie a tela de Listagem de Alunos

Crie /modules/escola/Aluno.explore.jsp como se segue:

<%@taglib uri="futurepagesApp" prefix="fpg" %>
 
<div align="center">
	<h2>Listagem de Alunos</h2>
	<fpg:hasSuccess>
		<fpg:success/>
		<br/>
		<br/>
	</fpg:hasSuccess>
	<a href="<fpg:modulePath module="escola" />/Aluno.fpg?type=create">Novo Aluno</a>
	<br/>
	<br/>
	<fpg:list value="alunos">
		<fpg:isEmpty>
		Nenhum aluno cadastrado.
		</fpg:isEmpty>
		<fpg:isEmpty negate="true">
			<table border="1">
				<tr>
					<td><strong>ID</strong></td>
					<td><strong>NOME</strong></td>
					<td><strong>MATRÍCULA</strong></td>
				</tr>
				<fpg:loop var="aluno">
					<tr>
						<td>${aluno.id}</td>
						<td>${aluno.nomeCompleto}</td>
						<td>${aluno.matricula}</td>
					</tr>
				</fpg:loop>
			</table>
		</fpg:isEmpty>
	</fpg:list>
	<br/>
	<br/>
</div>


Perceba que:

  • Estamos tentando acessar na visão a lista de alunos atribuída à variável de saída (output) com a chave “alunos”. Como vimos no passo anterior, fizemos isto no método listObjects da action AlunoAction, e estamos esperando que este método seja acessado quando chamarmos Aluno.fpg?type=explore;
  • A tag <fpg:isEmpty> verifica se a lista é vazia;
  • existe uma padronização para o nomenclatura dos arquivos de visão, este padrão facilita a manutenção do código.

Passo 5 - Configurando o fluxo entre Controle e Visão

No passo anterior vimos que o usuário clica no link /modules/escola/Aluno.fpg?type=create para abrir o formulário de criação e o segundo envia o mesmo formulário para /modules/escola/Aluno.create.fpg . Algumas ressalvas sobre as requisições, antes de prosseguir:

  • ambas são feitas ao controlador do futurepages (*.fpg) ;
  • ambas são requisições para actions do módulo escola ;
  • são requisições para a mesma action apelidada de “Aluno ” ;
  • a primeira chama o método público execute() da action;
  • a segunda chama o método público create() da action.

Vejamos como AlunoActions foi apelidada de Aluno e como se dão os fluxos entre tela (jsp) e controle (java).

  • Cada módulo possui uma classe em sua raiz chamada de ModuleManager , nele são configuradas as actions, filtros (globais ou não) entre outras coisas;
  • No nosso caso, como ambas as actions chamam actions de escola, devemos criar o ModuleManager do módulo escola: modules.escola.ModuleManager.java
  • Vimos que a classe AlunoActions não possui um método execute() , então Aluno.fpg?type=create irá chamar o método execute() de um de suas super-classes , neste caso, de CrudActions , que possui uma estrutura preparada para CRUDs.
  • Se o tipo da ação é abrir tela de criação (type=create) ou edição (type=update) , o método listDependencies() que você sobrescrever na sua classe será chamada antes de carregar a tela de criação/edição. Este método servirá para chamar listas e valores que sua tela pode depender (exemplo: uma tela de criação de cidade poderia depender de uma lista de estados onde o usuário deveria escolher somente um) .

Crie o ModuleManager

Crie modules.escola.ModuleManager.java com o conteúdo a seguir:

package modules.escola;
 
import modules.escola.actions.AlunoActions;
import modules.escola.beans.Aluno;
import org.futurepages.core.control.AbstractModuleManager;
import org.futurepages.filters.VOFilter;
 
public class ModuleManager extends AbstractModuleManager {
 
    @Override
    public void loadActions() {
 
 
 
        //Configuração da Action com o apelido "Aluno"
        action("Aluno", AlunoActions.class)
            //VOFilter: pega os valores dos campos do formulário da visão e casa-os com os atributos da classe Aluno.class, instancia um objeto
            //deste tipo e insere os valores dos campos do formulário nas respectivas propriedades,
            //o objeto preenchido é atribuido ao campo da action com o nome "aluno".
            .filter(new VOFilter("aluno", Aluno.class))
 
            // se chamar Aluno.fpg?type=create, o método execute será chamado, e a consequência do método deverá
            // mandar o resultado do output (fwIn) para a tela de criação do aluno dentro do módulo (com o template montado) 
            .on(CREATE, fwIn("Aluno.create.page"))
                //se ocorrer sucesso na criação do aluno vai para a tela de listagem dos alunos cadastrados
                .on(SUCCESS, CREATE, fwIn("Aluno.fpg?type=explore"))
 
            //Aluno.fpg?type=explore ocorre o redirecionamento para a tela de listagem dos alunos cadastrados no sistema
            .on(EXPLORE, fwIn("Aluno.explore.page"))
               .on(SUCCESS, EXPLORE, fwIn("Aluno.explore.page")) 
         ;
 
    }
}


Entenda bem os elementos do ModuleManager

Note que dentro do método loadActions() você adiciona as actions , as quais somente serão reconhecidas pelo servlet controlador (*.fpg) se definidas em algum ModuleManager.

O método loadActions() de cada módulo é carregado no controlador no momento de inicialização da aplicação. Este método é composto por o que chamamos de ActionConfig s, estas podem ser métodos como action, filter, on , etc. Podem ser globais ou vinculadas à uma action:

  • action(“Exemplo”, ExemploActions.class): Registro da action para a classe ExemploActions.class com apelido “Exemplo”. O padrão de apelidos para CrudActions é a retirada do nome “Actions”, deixando somente o nome do bean do CRUD;
  • action(Exemplo.class): Neste caso o apelido da action é inferido do nome da classe: “Exemplo”;
  • on(“RESULTADO”, consequence(“exemplo.jsp”)) : Especifica o fluxo da action se o método execute() tiver retornado “RESULTADO” , como (foward ou redirect) e para qual arquivo da visão a aplicação fluirá após este resultado;
  • on(“RESULTADO”, “innerActionX”, consequence(“exemplo.jsp”)) : Especifica o fluxo da action se o método innerActionX() tiver retornado “RESULTADO” , como e para qual arquivo da visão a aplicação fluirá após este resultado;
  • filter(new ExemploFiltro(param1, param2,param3, …)) : Um filtro é algo que será executado antes ou depois da (inner)action ser executada - no nosso exemplo há o uso do filtro VOFilter, um filtro que instancia um objeto do tipo passado por parâmetro, preenche-o com os campos do formulário casando os nomes das propriedades da classe, deixando esse objeto injetado no input da action, tudo isso antes da (inner)action ser executada. Você pode encontrar vários filtros no pacote org.futurepages.filters.*;
  • Existem filtros e consequências globais, ou seja, não vinculadas a alguma action. Não deve-se vincular uma action dentro de outra.

As consequências podem ser do tipo: Forward (fwd) e Redirect (redir) :

  • fwIn: significa foward in , ou seja, a action manda o seu output para a consequência dentro do módulo ;
  • fwd(“moduleId”,”arquivo.ext”) mandaria a consequência para um arquivo de outro módulo;
  • fwd(“arquivo.ext”) mandaria pro arquivo com o endereço completo dele a partir da raiz do host;
  • rdIn(String), redir(String), redir(String,String) são similares ao fwIn e fwd , mas só redirecionam para a página, sem mandar o output da action.

Agora sua aplicação cria e lista alunos, execute-a e faça os testes.

Suas telas devem parecer com as duas telas que se seguem:



Passo 6 - Validando os Dados de Entrada

Foi especificado que este sistema NÃO permitirá:

  • inserção de alunos com matrícula já existente;
  • criação de alunos com nome não especificado;
  • criação de alunos com matrícula não especificada.

Se tentarmos criar um aluno com matrícula já existente, ocorrerá uma exceção (ConstraintViolationException ), pois especificamos no esquema o campo matrícula teria o atributo unique=true na anotação @Column . O sistema de banco de dados não permitirá a inclusão, mas a mensagem não é amigável para o usuário.

Log do Servidor ao tentar-se criar um aluno com uma matrícula existente.

could not insert: [modules.escola.beans.Aluno]
org.hibernate.exception.ConstraintViolationException: could not insert: [modules.escola.beans.Aluno]

...

Caused by: java.sql.SQLException: Duplicate entry '' for key 2


Se tentarmos criar um aluno com o nome completo vazio, o aluno será criado com sucesso, ao contrário do que foi especificado.

Veremos neste passo como fazer para atender aos requisitos de validação de dados de entrada.

Crie o Validador

Validadores são classes que possuem métodos de validação para uma entidade. Se aluno é algo a ser validado, então criaremos um validador para Aluno (AlunoValidator) em modules.escola.validators .

Crie a classe modules.escola.validators.AlunoValidator :

package modules.escola.validators;
 
import modules.escola.beans.Aluno;
import org.futurepages.core.persistence.Dao;
import org.futurepages.core.validation.Validator;
import org.futurepages.util.Is;
 
public class AlunoValidator extends Validator {
 
	public void create(Aluno aluno) {
 
		// Validação do nome
		if (Is.empty(aluno.getNomeCompleto())) {
			error("Preencha o nome do aluno.");
		}
 
		// Validação da matrícula
		if (Is.empty(aluno.getMatricula())) { //verifica se matrícula vazia
			error("Especifique a matrícula do aluno.");
		} else {
			// Caso matrícula não seja vazia, verifica se a matrícula já existe
			if (Dao.uniqueResult(Aluno.class, "matricula='" + aluno.getMatricula() + "'") != null) {
				error("Já existe um aluno com esta matrícula.");
			}
		}
 
	}
}


Caso não encontre a classe Validator do futurepages, é provável que você esteja utilizando uma versão desatualizada do futurepages. Baixe a versão mais nova para que então seja possível compilar sua classe. ( futurepages.jar )

Aplique o validador na action

Primeiro adicione o import no cabeçalho da sua classe

import modules.escola.validators.AlunoValidator;

Faça a seguinte chamada na innerAction create()

    public String create() {
        validate(AlunoValidator.class).create(aluno);  //validação de aluno
        Dao.saveTransaction(aluno);
        return success("Aluno criado com sucesso");
    }


Configure o fluxo do erro

Você irá adicionar a consequência do erro. Ou seja, dirá para onde o output da action deve ir assim que retornar algum erro. Para isso, iremos adicionar a seguinte linha ao ModuleManager como consequência da innerAction create() da action apelidada de “Aluno” .

.on(ERROR,   CREATE, fwIn("Aluno.create.page"))


A linha acima informa ao Manager o seguinte: Quando houver um erro durante a execução do método create() , todo o output da action irá para Aluno.create.page .

Seu ModuleManager ficará assim:

package modules.escola;
 
 
import modules.escola.actions.AlunoActions;
import modules.escola.beans.Aluno;
import org.futurepages.core.control.AbstractModuleManager;
import org.futurepages.filters.VOFilter;
 
 
public class ModuleManager extends AbstractModuleManager {
 
 
    @Override
    public void loadActions() {
 
 
        //Configuração da Action com o apelido "Aluno"
        action("Aluno", AlunoActions.class)
            //VOFilter: pega os valores dos campos do formulário da visão e casa-os com os atributos da classe Aluno.class, instancia um objeto
            //deste tipo e insere os valores dos campos do formulário nas respectivas propriedades,
            //o objeto preenchido é atribuido ao campo da action com o nome "aluno".
            .filter(new VOFilter("aluno", Aluno.class))
 
 
            // se chamar Aluno.fpg?type=create, o método execute será chamado, e a consequência do método deverá
            // mandar o resultado do output (fwIn) para a tela de criação do aluno dentro do módulo (com o template montado) 
            .on(CREATE, fwIn("Aluno.create.page"))
                //se ocorrer sucesso na criação do aluno vai para a tela de listagem dos alunos cadastrados
                .on(SUCCESS, CREATE, fwIn("Aluno.fpg?type=explore"))
                //se ocorrer error na criacao do aluno volta para a tela de criação do aluno
                .on(ERROR,   CREATE, fwIn("Aluno.create.page"))
 
 
            //Aluno.fpg?type=explore ocorre o redirecionamento para a tela de listagem dos alunos cadastrados no sistema
            .on(EXPLORE, fwIn("Aluno.explore.page"))
               .on(SUCCESS, EXPLORE, fwIn("Aluno.explore.page")) 
         ;
 
    }
}


Contemple o retorno do erro na visão

A tag hasError verifica se a tela voltou do erro e error imprime a mensagem de erro.

<%@taglib uri="futurepagesApp" prefix="fpg"%>
 
<div style="text-align:center">
    <h1>Novo Aluno</h1>
    <br/>
    <br/>
    <fpg:hasError>
        <!-- imprime estre pedaço somente se houver erro na página -->
        <div style="color: red; border:solid 1px red">
            <fpg:error/>
        </div>
    </fpg:hasError>
        <br/>
 
    <form method="post" action="<fpg:modulePath module="escola" />/Aluno.create.fpg">
        <fpg:bean value="aluno">
            Nome: <input id="nomeCompleto" name="nomeCompleto" value="${aluno.nomeCompleto}" />
            <br/><br/>
            Matrícula: <input id="matricula" name="matricula" value="${aluno.matricula}" />
        </fpg:bean>
        <br/>
        <br/>
        <input type="submit" value="Enviar" />
    </form>
</div>


Pronto, a validação está concluída. Rode sua aplicação e provoque as telas de erro, passando como entrada dados inválidos.

Você deverá ver uma tela semelhante à que se segue:

  • Dica: Assim como existem as tags hasError e error quando retornamos uma mensagem através do método error(String msg) possuímos também os semelhantes para tratar mensagens de sucesso. Ou seja, as tags hasSuccess e success para o retorno do método success(String msg). Tente como atividade extra criar uma mensagem de sucesso para a criação de um novo aluno.
  • Você pode baixar o código fonte deste tutorial aqui:
Repositório : http://repo.futurepages.org/svn/2/apps/alunoonline/tags/meu_primeiro_projeto_com_futurepages
    Usuário : user
      Senha : fpguser
 
/var/www/html/wiki/data/pages/quickstart/projeto_futurepages_do_zero.txt · Última modificação: 2015/09/03 22:31 por leandro
 
Exceto onde for informado ao contrário, o conteúdo neste wiki está sob a seguinte licença:CC Attribution-Noncommercial-Share Alike 3.0 Unported
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki