====== 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; * Baixar todas as bibliotecas necessárias (JARs) em [[http://futurepages.org/download/lib.zip|futurepages2-lib]]; * Baixar o futurepages: [[http://futurepages.org/download/jar/|Arquivo JAR (versão estável)]]; * Baixar e Instalar o NetBeans IDE 6.8 RC2 ([[http://www.netbeans.org/downloads/index.html|Página de Download - Baixe a Opção "Java Web e EE"]] ); * Instalar o [[http://futurepages.org/download/apache-tomcat-5.5.28.zip|Tomcat 5.5.28]] no NetBeans IDE 6.5+ ([[:quickstart:instalando_tomcat55_no_netbeans|Ver Tutorial]]); * 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 {{ :quickstart:spacing.png | }} ==== Crie um Novo Projeto Web ==== * Vá em Arquivo → Novo Projeto → Categorias: **Java Web** → Projetos: **Aplicação Web** → Siga em frente:{{ :quickstart: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; * Escolha o Servidor [[:quickstart:instalando_tomcat55_no_netbeans|Tomcat 5.5 que você configurou]], mantenha as demais opções como padrão e siga adiante; * 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** ; {{ :quickstart:nb_propriedades_codigo.jpg }} * Se quiser utilizar Charset UTF-8, siga as instruções [[http://futurepages.org/forum/viewtopic.php?f=5&t=37|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. Aluno On Line 10 Controller org.futurepages.core.control.Controller applicationManager org.futurepages.core.ApplicationManager TemplateServlet org.futurepages.core.template.JspTemplateServlet TemplateManager org.futurepages.core.template.TemplateManager Application Listener org.futurepages.core.ApplicationListener org.futurepages.core.session.SessionListener Controller / Controller *.fpg TemplateServlet *.page \\ 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. \\ == Configuração do Template == Crie em SRC **conf/app-template.xml** com o seguinte conteúdo: \\ 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"%> Aluno Online QuickStart Application

\\ **template/cabecalho.jsp** : <%@ taglib uri="futurepagesApp" prefix="fpg"%>
Aluno Online QuickStart Application
\\ **template/rodape.jsp** :
(c) 2008-2010 - Workset - Todos os Direitos Reservados
\\ 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"%>



Novo Aluno

Listar Alunos

\\ Altere o arquivo /index.jsp para que ele redirecione para sua ação inicial: \\ \\ 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 [[:futurepages2:parametros|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: {{ :quickstart: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: {{ :quickstart:taglib_origem.jpg }} * Em seguida, cole no local indicado a seguir. {{ :quickstart: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: \\ Inclua a tag Resource mostrada a seguir dentro da tag Context. Seu arquivo deverá ficar como mostrado a seguir: \\ 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: \\ 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: {{ :quickstart: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 [[http://workset.dyndns.org:81/svn/dev/futurepages2/trunk/src/org/futurepages/core/install/Installer.java|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: \\ == 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 [[:futurepages2:actions|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 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"%>



/Aluno.fpg?type=create">Novo Aluno

/Aluno.fpg?type=explore">Listar Alunos

\\ 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"%>

Novo Aluno



/Aluno.create.fpg"> Nome:

Matrícula:


\\ 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: * [[http://nomeDoHost|http://nomeDoHost]]/modules/escola/Aluno.create.fpg 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" %>

Listagem de Alunos



/Aluno.fpg?type=create">Novo Aluno

Nenhum aluno cadastrado.
ID NOME MATRÍCULA
${aluno.id} ${aluno.nomeCompleto} ${aluno.matricula}


\\ 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 **** 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 **[[:futurepages2:modulemanager|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: {{ :quickstart:novo-aluno-peq.png |}} \\ \\ {{ :quickstart:listagem-alunos.png |}} ===== 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. ( [[http://futurepages.org/download/jar/|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"%>

Novo Aluno




/Aluno.create.fpg"> Nome:

Matrícula:


\\ 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: {{ :quickstart:validacao-com-erro.png |}} * **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 * Se você tiver dúvidas de como fazer um projeto usando os repositórios do SVN, pode usar esse tutorial como ajuda: [[quickstart:criando_projeto_a_partir_do_svn]] * Ou então, você pode continuar o tutorial em [[quickstart:meu_primeiro_projeto_web_com_futurepages2_-_parte_2|Meu Primeiro Projeto com Futurepages2 - Parte 2]]