Desenvolvimento Java para Web
Osvaldo Pinali Doederlein
Visionnaire
Introdução à Plataforma
J2EE 1.4
Agenda
Containers
Padrões JCP
Servidores de Aplicação
J2EE e Containers
http://java.sun.com/j2ee/
Container: Conceito fundamental do J2EE
Aplicação executa num “casulo” que assume ou
facilita diversas responsabilidades
Administração / Ciclo de vida
Conectividade (SGBD, Messaging, HTTP, SOAP etc.)
Funcionalidades: Persistência, Segurança...
APIs
O Container também implementa APIs J2EE
APIs J2SE: stand-alone (bibliotecas simples)
APIs J2EE: maior dependência de serviços
XML-RPC: exige server HTTP
JMS: exige middleware de mensagens
Connector: exige bridges com outros sistemas
EJB: exige servidor RMI/IIOP
APIs do J2EE 1.4: Diversos
JavaMail
Envio e recepção de e-mail por POP, IMAP, SMTP
JavaBeans Activation Framework
Ativação de funcionalidades via tipos MIME
JNDI (Java Naming and Directory)
LDAP, NDS, DNS, NIS, CosNaming; serviços J2EE
JAAS (Autenthication & Authorization Service)
Autorização e Autenticação; PAM
APIs do J2EE 1.4: Middleware
JMS (Java Message Service)
Comunicação assíncrona robusta
JTA (Java Transaction API)
Transações de alto nível
JCA (J2EE Connector Architecture)
Integração com sistemas não-J2EE
JDBC (Java Database Connectivity)
J2SE + DataSources, transações XA
APIs do J2EE 1.4: Web
Servlets
Programação Web dinâmica
JSP (Java Sever Pages)
Idem, mais visual
JSTL (Java Standard Template Library)
Idem, mais estruturado
Alternativas/Complementos: Struts, Spring...
Futuro (J2SE 5.0): JSF (JavaServerFaces)
APIs do J2EE 1.4:
XML e Web Services
JAXP (Java API for XML Processing)
DOM, SAX, XSLT
SAAJ (SOAP with Attachments API for Java)
Web Services: SOAP (modelo documento)
JAX-RPC (Java API for XML-based RPC)
Web Services: SOAP (modelo RPC), WSDL
JAXR (Java API for XML Registries)
Web Services: UDDI / ebXML
APIs do J2EE 1.4: EJB
EJB (Enterprise Java Beans)
Componentes Distribuídos; Session; RMI/IIOP
EJB BMP/CMP/CMR
Entity; Persistência Automática (mapeamento O/R)
EJB MDB (Message-Driven Beans)
Facilidade de alto nível para JMS e Web Services
Transações, Segurança
Facilidades declarativas
Padrões do JCP
J2EE 1.4 (JSR-151 + 15 JSRs, jcp.org)
Padroniza quase tudo
APIs
Schemas, Deployment
Comportamentos (ciclos de vida, etc.)
Não cobre:
Ferramentas, Metodologias
QoS
Integração (ex.: suporte a SGBDs e middlewares)
http://java.sun.com/reference/blueprints/
Tomcat 5.5
Apache + Sun; RI de JSRs de Servlet, JSP, etc.
Container Web do JBoss, Sun AppServer, …
Conectores para Apache e IIS
http://jakarta.apache.org/tomcat/
JBoss 4.0
JBoss Group
Open Source & Comercial
Arquitetura: Microkernel (JMX), AOP
http://www.jboss.com/products/jbossas/
Design Patterns para J2EE
Agenda
Design Patterns
Singleton
Façade
Factory
DAO
MVC
Design Patterns
Design Patterns (GoF, 1995)
Pattern = design reutilizável; problema+solução
Modelos; ex.: Intenção, Motivação, Aplicabilidade,
Estrutura, Implementação, Implicações, Categoria...
Instanciável, mas não reusável, como código
Linguagem de Patterns: Coleção de patterns
relacionados, com modelo comum
http://java.sun.com/blueprints/patterns/
Singleton: Motivação
Garantir que uma classe tenha instância única
Sem abrir mão do suporte à herança (como static)
Implementação robusta de entidades que
precisam ser únicas; ex: configuração
Alternativa OO às variáveis globais
Facilita a inicialização “lazy”
Singleton: Estrutura
Singleton
-singletonInstance
...
«constructor»
-Singleton( )
«misc»
+getInstance( )
...
class Configuracao {
private static Configuracao singleton;
private final Properties props;
private Configuracao (Properties props) { this.props = props; }
public static Configuracao getInstance () {
if (singleton == null) {
properties props = // ... Lê properties de um arquivo
singleton = new Configuracao(props);
}
return singleton;
}
public getProperties () { return props; }
}
Properties props = Configuracao.getInstance().getProperties();
Façade: Motivação
Interface única e simples para um sistema
Simplifica API, reduz curva de aprendizado
Reduz dependências e acoplamento
Se o modelo interno do sistema evoluir, basta mudar
a implementação da Façade; clientes são preservados
Conduz a modelos em camadas (layers)
Ex: Cliente (JSP) Façade (Session Bean) Negócio
Façade: Estrutura
Cliente
1
Usa
*
Façade
Façade (Session / J2EE)
public class SistemaPagamentos {
public void alteraSalario (String matricula, double salario) {
Funcionario func = CadastroFuncs.getInstance().find(matricula);
if (func != null && func.isAtivo())
func.setSalario(salario);
}
// ... Outros métodos da Façade
}
// Cliente não precisa conhecer Funcionario, CadastroFuncs, etc.
SistemaPagamentos sp = // ...
sp.alteraSalario("93764622", 4350.00);
Factory: Motivação
Cria objetos sem saber suas classes concretas
Encapsula decisões sobre a criação de objeto
Melhoram OO (construtores não são polimórficos!)
Variante: Factory Method
Útil com herança, delega criação à subclasse
Evita instanceof para criar objetos relacionados
Variante: Abstract Factory
Suporta um conjunto de entidades relacionadas
Suporta implementações "plugáveis", ex.: JDBC, XML
Factory Method: Estrutura
public interface Cliente {
public Conta abreConta ();
}
public class ClienteSimples implements Cliente {
public Conta abreConta () {
return new ContaComum(this);
}
public class ClientePremier implements Cliente {
public Conta abreConta () {
return new ContaEspecial(this);
}
Cliente cli = // ... obtém da database ou outra fonte
Conta ct = cli.abreConta(); // cria conta do tipo correto
Abstract Factory: Estrutura
// Utiliza fábrica abstrata de engine XML (Xerces, Crimson, etc.)
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setValidating(true);
spf.setNamespaceAware(true);
// Utiliza fábrica abstrata de parser (com ou sem validação, etc.)
SAXParser parser = spf.newSAXParser();
XMLReader reader = parser.getXMLReader();
reader.setContentHandler(this);
reader.parse(config);
DAO: Motivação
Desacoplar o acesso a dados dos clientes
Ocultar a complexidade do acesso a dados
Permitir alternância fácil e dinâmica da
implementação de persistência
Não é necessário com ferramentas ORM (como
Hibernate), mas facilita migração JDBCORM
DAO: Estrutura
public class Venda { /* nenhum acesso a dados */ }
public interface VendaDao {
public Venda find (String id) throws SQLException;
}
public class VendaDaoJDBC implements VendaDao {
private DataSource ds;
public Venda find (String id) throws SQLException {
try {
Connection c = ds.getConnection();
Statement stmt = c.createStatement();
ResultSet rs = stmt.executeQuery("SELECT…");
if (rs.next()) return new Venda(id, rs.getString("produto"), …);
} finally { if (c != null) try { c.close(); } catch (SQLException e){} }
return null; // Objeto não encontrado
}
MVC-2: Motivação
Desacoplar responsabilidades de uma GUI:
Model: Camada de Implementação das Regras de
Negócio
View: Layout dos dados (ex.: telas HTML)
Controller: Comportamento da GUI (ex.: consultas,
atualizações, tratamento de erros, defaults)
Permite variar/evoluir cada um separadamente
View editável por não-programadores
Back-end pode variar sem afetar GUI
MVC-2: Estrutura
Requisição
Controller
Envio de Dados
Seleção do Visualizador
Resposta
View
Resposta do
Envio de dados
Model
<html:form action="/LoginSubmit.do">
<table>
<tr><td>Login:</td>
<td><html:text property="login" size="20"/></td>
</tr>
<tr><td>Senha:</td>
<td><html:password property="password" size="20"/></td>
</tr>
<tr><td><html:submit>OK</html:submit></td></tr>
</table>
</html:form>
<html:messages id="error" property="invalid">
<script>alert('<bean:write name="error" />');</script>
</html:messages>
<html:javascript formName="LoginForm"/>
View
public class LoginAction extends DispatchAction {
Controller
public ActionForward login (ActionMapping map,
ActionForm form, HttpServletRequest req,
HttpServletResponse resp) throws Exception {
audit(request, PortalAudit.ACCESS);
if (isFirstLogin(request))
return mapping.findForward("firstLogin");
else
return mapping.findForward("loginOk");
}
private boolean isFirstLogin (HttpServletRequest req) {...}
// ... Outras ações ...
}
Logging
Agenda
Log4J
java.util.logging
Log4J
Biblioteca do projeto Apache Jakarta
Substitui System.out.println()...
Níveis de log; ativação dinâmica sem mudar código
Opções de output (console, arquivo, rede, DB etc.)
Formatação, Extensibilidade
Logs detalhados, de qualidade; fácil e eficiente
Para diagnóstico de problemas (debug ou produção)
Para auditoria, etc.
http://logging.apache.org/log4j/
Log4J: Exemplo
Logger logger = Logger.getLogger("SIST");
logger.debug("x=“ + x);
logger.log(Level.DEBUG, "x=“ + x);
if (logger.isDebugEnabled()) // Se não estiver habilitado...
logger.debug(a+b+c+d+e+f); // ...evita custo de gerar dados
Log4J: log4j.properties
log4j.logger.SIST = INFO, APP_SIST
log4j.appender.APP_SIST = org.apache.log4j.RollingFileAppender
log4j.appender.APP_SIST.File = ../logs/SIST.log
log4j.appender.APP_SIST.MaxFileSize = 10MB
log4j.appender.APP_SIST.MaxBackupIndex = 10
log4j.appender.APP_SIST.layout = org.apache.log4j.PatternLayout
log4j.appender.APP_SIST.layout.ConversionPattern =
[%p] %d{HH:mm:ss} - %m%n
Log4J: log4j.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
<logger name=“SIST"> <level value="INFO"/> </logger>
<appender name="APP_SIST" class="org.apache.log4j.RollingFileAppender">
<param name="File" value="../logs/SIST.log"/>
<param name="MaxFileSize" value="10MB"/>
<param name="MaxBackupIndex" value="10"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%p] %d{HH:mm:ss} - %m%n"/>
</layout>
</appender>
</log4j:configuration>
java.util.logging
API padrão do J2SE 1.4+ (JSR-47)
Semelhante à Log4J, mas não igual/compatível
Menos poderosa; Mais leve, simples, eficiente
Log4J ou java.util.logging?
Log4J não é o padrão, mas ainda é mais popular
java.util.logging melhor para componentes reusáveis
(não impõe dependência da Log4J para projetos)
A Jakarta Commons Logging só piora as coisas
http://java.sun.com/j2se/1.5.0/docs/guide/logging/
java.util.logging: Exemplo
Logger logger = Logger.getLogger("SIST");
logger.fine("x=“ + x);
logger.log(Level.FINE, "x=“ + x);
if (logger.isLoggable(Level.FINE)) // Se não estiver habilitado...
logger.fine(a+b+c+d+e+f);
// ...evita custo de gerar dados
java.util.logging:
logging.properties
handlers= java.util.logging.FileHandler
SIST.level = INFO
java.util.logging.FileHandler.pattern = ../logs/SIST%u.log
java.util.logging.FileHandler.limit = 10000000
java.util.logging.FileHandler.formatter =
java.util.logging.SimpleFormatter
JDBC
Agenda
JDBC API
J2EE/DataSources
A API JDBC
Baseada na ODBC (Microsoft; Open Group CLI)
Microsoft: novas APIs (OleDB, ADO, RDO, RDS...)
JDBC: evolução compatível + frameworks O/R
Características
Baixo nível: conexões, statements, cursores, SQL
Leve e eficiente
Infra-estrutura para todas as outras soluções
http://java.sun.com/products/jdbc/
JDBC: Connection
Representa sessão com o SGBD, e transação
Alto custo de criação. Usar pools!
Default: auto-commit, não usar!!
Connection conn = null;
try {
conn = DriverManager.getConnection(url, user, password);
// Utiliza a conexão...
} catch (SQLException e) {
if (conn != null) try { conn.close(); } catch (SQLException e) {}
}
JDBC: Statement e ResultSet
Statement: Query, Update, ou DDL
Prefira PreparedStatement sempre!
ResultSet: Cursor (resultado de Statement)
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT id,nome FROM FUNC");
while (rs.next()) {
System.out.println(rs.getInt(1));
System.out.println(rs.getString("nome"));
}
stmt.close();
JDBC: PreparedStatement
Estende Statement, mais poderoso e eficiente
Binding (?), reduz recompilação e formatação de SQL
Cache no cliente (driver JDBC)
Também é mais seguro (evita "injeção de SQL")
PreparedStatement stmt = conn.prepareStatement(
"SELECT id,nome FROM FUNC WHERE salario >= ?");
stmt.setDouble(1, 3000);
ResultSet rs = stmt.executeQuery();
...
stmt.close();
JDBC: CallableStatement
Invocação de stored procedures / functions
“Anti-OO”, mas reduz tráfego com SGBD
CallableStatement stmt = conn.prepareCall(
“{call CALCULA_SALARIO (?,?)}");
stmt.setInt(1, idFuncionario);
stmt.registerOutParameter(2, Types.DOUBLE);
stmt.executeUpdate();
double salario = stmt.getDouble(2);
stmt.close();
Gerenciamento de Recursos
Invocação dos métodos close()
Connection.close(): fecha todos Statements e blobs
Statement.close(): fecha ResultSet corrente
Statemet.execute*(): também fecha ResultSet anterior
ResultSet.close(): não fecha seus blobs!
Regra: fechar só o objeto “raiz local” do método
Ex.: Se um método recebe a Connection como
parâmetro, basta fechar os Statement criados
Evite dividir responsabilidades (um método cria,
outro faz o close()) bugs, bugs, bugs!!
Opções: Connection
TRANSACTION_READ_UNCOMMITTED
Permite non-repeatable/phantom reads
TRANSACTION_REPEATABLE_READ
Permite phantom reads
TRANSACTION_SERIALIZABLE
Isolamento ACID total
DESEMPENHO
TRANSACTION_READ_COMMITTED
CONFIABILIDADE
Permite dirty/non-repeatable/phantom reads
Opções: ResultSet
FETCH_FORWARD/REVERSE/UNKNOWN
Direção da leitura (hints para o driver)
TYPE_FORWARD_ONLY/
SCROLL_SENSITIVE/SCROLL_SENSITIVE
Leitura randômica ou aleatória
DESEMPENHO
Somente leitura, ou cursor atualizável
PRODUTIVIDADE
CONCUR_READ_ONLY/UPDATABLE
DataSource
Abstração sobre obtenção de conexões
J2EE, mas também pode ser usada sem container
Suporte a JNDI, pools, transações distribuídas
Configuração no container
Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("jdbc/Sistema");
...
Connection conn = ds.getConnection();
Testes Unitários
Agenda
Testes Unitários
JUnit
Testes Unitários
TDD (Test-Driven Development), XP
1 funcionalidade = 1 teste
Funcionalidade: método, classe, caso de uso, cenário
Escrever teste antes do código a testar
Objetivos: melhorar qualidade e produtividade
Planejar componentes, do ponto de vista do cliente
Proteção contra bugs de regressão
Otimizações, refactoring... "fearless programming"
http://www.extremeprogramming.org/rules/unittests.html
JUnit
Framework para testes unitários em Java
Kent Beck (XP) + Erich Gamma (Patterns, Eclipse)
Simples
Extensível
Exemplos públicos para examinar...
J2SE/TCK (45.000 testes)
Eclipse (feito com o JUnit)
http://www.junit.org/
public class TestaRaizes extends TestCase {
public void testOk () {
double[] x = Raizes.calcula(3, 4, 1); // 3x2+4x+1=0
assertTrue(x[0] == -3 && x[1] == -9);
System.out.println(x[0] + "," + x[1]);
}
public void testDeltaNegativo () {
try {
double[] x = Raizes.calcula(3, 0, 1); // 3x2=-1
fail();
}
catch (IllegalArgumentException e) {}
}
}
public class Raizes {
public static double[] calcula (double a, double b, double c) {
double delta = Math.sqrt(b*b-4*a*c);
if (Double.isNaN(delta)) throw new
IllegalArgumentException("Delta negativo");
double x1 = (-b+delta)/2*a;
double x2 = (-b-delta)/2*a;
return new double[]{x1,x2};
}
}
Servlets
Agenda
Servlet
Inicialização
Filtros
Redirecionamento
Sessões
Cookies
Servlet
Servlet = Classe Java que responde a HTTP
Exige um conector HTTP (webserver)
Servlet recebe um request
Conector cuida de headers, TCP/IP, etc.
Base para todo stack Java Web
JSP, JSTL, JSF, Struts, Velocity, Spring, etc.
http://java.sun.com/products/servlet/
public class AloMundo extends HttpServlet {
public void doGet (HttpServletRequest req,
HttpServletResponse resp) throws IOException, ServletException {
resp.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>Alô Mundo</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Alô, Mundo!</h1>");
out.println("</body>");
out.println("</html>");
}
}
Web.xml:
<servlet>
<servlet-name>AloMundo</servlet-name>
<servlet-class>AloMundo</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AloMundo</servlet-name>
<url-pattern>/alo</url-pattern>
</servlet-mapping>
</web-app>
Inicialização e Shutdown
Servlet.init(), Servlet.destroy()
init() na primeira invocação, ou load-on-startup=true
ServletContextListener
Mecanismo apropriado para ciclo de vida da
aplicação como um todo
public class MeuListener implements ServletContextListener{
public void contextInitialized (ServletContextEvent sce) {
System.out.println("inicializando...");
}
public void contextDestroyed (ServletContextEvent sce) {
System.out.println("terminando...");
}
}
Web.xml:
<listener>
<listener-class>TesteListener</listener-class>
</listener>
Filtros
Interceptação de requests
Verificação de desempenho
Logging automatizado
Controle de segurança, transações, etc.
Transformações, ex.: via XSLT
Implementação de frameworks dinâmicos, ex.: Struts
Implementação do próprio container
public class PSSFilter implements Filter {
// Omitidos: init(), destroy()
public void doFilter (ServletRequest req, ServletResponse resp,
FilterChain chain) throws ServletException, IOException {
try { chain.doFilter(req, resp); }
finally { Catalog.closeConnection(); }
}
}
Web.xml:
<filter>
<filter-name>PSSFilter</filter-name>
<filter-class>com.visionnaire.PSS.servlet.PSSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>PSSFilter</filter-name><url-pattern>*.jsp</url-pattern>
</filter-mapping>
Forward e Include
Forward: Desvio para outra URL
request.getRequestDispatcher("/Header.jsp")
.forward(request, response);
Include: Cópia do output de uma URL
request.getRequestDispatcher("/OK.jsp")
.include(request, response);
Contexto, Sessões e Cookies
ServletContext: Representa a web-app
MIME, dispatchers, resources, servlets, atributos, log
HttpSession: Permite armazenar informações
sobre um cliente, através de várias páginas
ID, atributos, criação, último acesso, timeout
Cookies ou URL rewriting
Cookie: Pacote de dados armazenável no browser
Utiliza headers do HTTP
Pode ser restrito pelo cliente (número, tamanho)
JSP
Agenda
JSP
Taglibs
Expression Language
JSTL
JSP
Servlet: Java com HTML, JSP: HTML com Java
Semelhante a ASP, PHP, etc.
JSP gera uma Servlet
Compilado pelo container, em demanda ou deploy
Acesso a toda a API de Servlets
Diretrizes de página
Scriptlets
Ações
JSP: Sintaxe
Comentário
Expressão
Scriptlet
Declaração
Diretiva include
Diretiva page
<%-- comentário -->
<%= expressão %>
<% statement %>
<%! declaração %>
<%@include file="url-absoluta">
<%@page atrib="valor" %>
language="java", import="pkg"
errorPage="url", extends="classe"
info="msg", contentType="MIME"
buffer="kb|none", isErrorPage,
session, autoflush, isThreadSafe
JSP: Ações
Ação include
Ação useBean
Ação getProperty
Ação forward
Ação plugin
<jsp:include page="url">
<jsp:useBean atrib="valor"/>
id="nome" scope="page|request|
session|application" type="class"
class="class" beanName="class"
<jsp:getProperty
name="name" value="valor"/>
<jsp:forward page="url"/>
<jsp:plugin atrib="valor"/>
JSP: Variáveis predefinidas
As mesmas que estariam disponíveis na Servlet
HttpServletRequest request
HttpServletResponse response
HttpSession session
PrintWriter out
<h2>Seu host é: <%= request.getRemoteHost() %></h2>
<%@page language="java"%>
<%@page import="java.util.Date"/>
<jsp:include page="Titulo.html"/>
<jsp:include page="Navegacao.jsp"/>
<html><head/>
<body>
<p>
Alo, mundo! Agora são <%= new Date() %>
</p>
</body>
</html>
Taglibs
Declaração e Uso (JSP)
<%@taglib prefix="opd" uri="/tags/osvaldo" %>
<tt:alo name="Osvaldo"> ... </tt:alo>
Configuração (web.xml)
<jsp-config>
<taglib>
<taglib-uri>/tags/osvaldo</taglib-uri>
<taglib-location>/WEB-INF/tags/osvaldo.tld</taglib-location>
</taglib>
</jsp-config>
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE taglib PUBLIC
"-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
"http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<taglib>
<tlibversion>1.0.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>Taglib Demo</shortname>
<tag>
<name>alo</name>
<tagclass>tags.AloTag</tagclass>
<bodycontent>empty</bodycontent>
<attribute>
<name>nome</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
</tag>
</taglib>
public class AloTag extends BodyTagSupport {
private String nome;
public void setNome (String nome) {
this.nome = nome;
}
public int doEndTag () throws JspException {
try {
pageContext.getOut().write("Alo, " + nome);
}
catch (IOException e) {}
return SKIP_BODY;
}
}
EL (Expression Language)
Parâmetros de tags; reduz uso de scriptlets
Sintaxe:
${var} (= var)
${var.x.y} (= var.getX().getY())
Operadores extra; números e strings
div, mod, and, or, not, eq, ne, lt, gt, le, ge, empty
Ex.: ${(10 mod 4 == 5) and empty x}
${(10 % 4 == 5) && x == null}
EL (Expression Language)
Variáveis predefinidas
pageContext (servletContext, session, request, response)
param, paramValues
header, headerValues
cookie, initParam
pageScope, requestScope, sessionScope, applicationScope
Sintaxe [ ] para acesso a variáveis Map (get())
${header["host"]}
${pageScope.departamentos[nomeDepto]}
JSTL (JSP Standard TagLib)
JSR-52: Biblioteca padrão de tags para JSP
Core: Iteração, Condições, Output…
FMT: Formatação, Internacionalização
SQL: Acesso a dados via JDBC
XML: Parsing, Xpath, XSLT
Functions: Manipulação de strings
<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html> <head><title>Demo de JSTL</title></head>
<body>
<sql:transaction dataSource=“jdbc/MinhaDatabase">
<sql:query var=“produtos">
SELECT * FROM PRODUTO
</sql:query>
</sql:transaction>
<h2>Componentes de Hardware</h2>
<table border="1">
<c:forEach var="row" items="${produtos.rowsByIndex}">
<tr>
<c:forEach var="col" items="${row}">
<td><c:out value="${col}"/></td>
</c:forEach>
</tr>
</c:forEach>
</table>
</body></html>
JSTL: Core
<c:out value="texto“/>
<c:set var="nome" value="valor"
scope="page|request|session|application"/>
<c:remove var="nome" scope="page|request|session|application"/>
<c:catch var="nome"> ... </c:catch>
<c:if test="condição" var,scope> ... </c:if>
<c:choose>
<c:when test="condição">...</c:when>
<c:otherwise>...</c:otherwise>
</c:choose>
JSTL: Core
<c:forEach var="nome" items="coleção"
varStatus="status" begin="n" end="n" step="n">
<c:forTokens var="nome" items="string" delims="sep"
varStatus="status" begin="n" end="n" step="n">
<c:import url="url" charEncoding="encoding"
context="contexto" varReader="nome" var,scope>
<c:param name="nome" value="valor"/>
<c:url value="url" context="contexto" var,scope/>
<c:redirect url="url" context="contexto" >
<c:param name="nome" value="valor"/>
JSTL: Formatação
<fmt:setTimeZone value="timeZone"/>
<fmt:timeZone var,scope> corpo </fmt:timeZone>
<fmt:formatNumber value="valor" currencyCode="code"
currencySymbol="symbol" type="number|currency|percent" var,scope
groupingUsed="bool" pattern="pattern" maxIntegerDigits="n"
minIntegerDigits="n" maxFractionDigits="n" minFractionDigits="n">
<fmt:parseNumber value="valor" parseLocale="locale"
type="number|currency|percent" integerOnly="bool" var,scope/>
JSTL: Formatação
<fmt:formatDate value="data" type="{time|date|both}"
dateStyle="{default|short|medium|long|full}"
timeStyle="{default|short|medium|long|full}"
pattern="pattern" timeZone="zona" var,scope/>
<fmt:parseDate value="data" type="{time|date|both}"
dateStyle="{default|short|medium|long|full}"
timeStyle="{default|short|medium|long|full}" var,scope
pattern="pattern" timeZone="zona" parseLocale="locale"/>
JSTL: SQL
<sql:query sql="query" var="var" dataSource="nome"
maxRows="max" startRow="start" var,scope>
<sql:param value="valor"/>
</sql:query>
<sql:update sql="update" dataSource="nome" var,scope>
params </sql:update>
<sql:dateParam value="valor"
type="timestamp|time|date"/>
JSTL: SQL
<sql:transaction dataSource="nome"
isolation="read_committed|read_uncommitted|repeatable_
read|serializable"> ... </sql:tx>
<sql:setDataSource dataSource="nome" url="url"
driver="classe" user="nome" password="pwd" var,scope/>
Mais taglibs...
JSTL
XML (<x:...>)
Internacionalização (<fmt:...>)
Funções EL definidas pelo usuário
JSR-128: JESI, JSP TagLib for Edge Side Includes
JSR-267: JSP TagLib for Web Services
Deployment
Agenda
Pacotes jar, war, ear
Deploy em servidores J2EE
Pacotes jar, war, ear
JAR: J2SE
/ Classes, Resources
/META-INF/ Manifest.MF
WAR: J2EE
/ Páginas (e subdiretórios)
/WEB-INF Configurações, TLDs
/WEB-INF/classes Classes
/WEB-INF/lib JARs
/WEB-INF/tags tagfiles
EAR: J2EE (EJB)
Deploy em servidores J2EE
"Hot deploy", copiar WAR/EAR para diretório
Tomcat: /webapps
JBoss: server/config/deploy
DataSources: deploy de .XML
Outros (JBoss):
RAR (Resource Adapter)
SAR (Service)
Deploy "explodido" (opcional / debug)
Ferramentas GUI de Deploy: overrides
Struts
Agenda
Model (forms)
View
Controller (actions)
Configuração
Taglibs
Tiles
Validação
Requisição
Controller
Envio de Dados
Seleção do Visualizador
Resposta
View
Resposta do
Envio de dados
Model
View
Página JSP "amarrada" à Struts
<html:form action> Action (Controller)
Campos Form (View)
Controller
Herda Action
Predefinidas: Tiles, Download, Forward, Include...
Implementar execute()
Uma ou mais actions: request.getParameter("action")
Dispara alguma ação de negócio
Determina próxima página (forward)
DispatchAction: implementar métodos similares ao
execute(), mas com nome igual à da ação
Configuração
WEB-INF/struts-config.xml
Mapeamento MVC, page flow
WEB-INF/web.xml
Configuração padrão da Struts, ex.: ActionServlet
WEB-INF/: Outros arquivos da Struts
WEB-INF/lib: Binários da Struts
WEB-INF/.struts-config.mex: Layout visual
para o struts-config.xml (MyEclipse)
public class ContatoForm extends ActionForm {
private String nome="", sobrenome="", email="", fone="", warning="";
public String getNome () { return nome; }
public void setNome (String nome) { this.nome = nome; }
// Omitido: outros getters/setters
public String valida () {
if (nome == null || nome.length() == 0) return "nome";
if (sobrenome == null || sobrenome.length() == 0) return
"sobrenome";
if (email == null || email.length() == 0 ||
email.indexOf('@') == -1) return "email";
if (fone == null || fone.length() == 0) return "fone";
return null;
}
}
public class MessageBean {
private String message = "";
public String getMessage () {
return message;
}
public void setMessage (String message) {
this.message = message;
}
}
public class AssinaAction extends Action {
public ActionForward execute (ActionMapping map,
ActionForm aForm, HttpServletRequest req,
HttpServletResponse resp) throws Exception {
ContatoForm form = (ContatoForm)aForm;
String campoRuim = form.valida();
if (campoRuim != null) {
warn(req, campoRuim);
return map.findForward("valor-errado");
}
else return map.findForward("sucesso");
}
protected void warn (HttpServletRequest req, String msg) {
MessageBean msgBean = new MessageBean();
msgBean.setMessage(msg);
req.setAttribute("messageBean", msgBean);
}
}
assina.jsp:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<html><head><title>Assinatura</title></head>
<body><h1 align="center">Assinatura</h1>
Digite seus dados de contato para receber nossa Newsletter!<p/>
<center>
<html:form action="/assina">
<table>
<tr><td>Nome:</td><td><html:text property="nome"/></td></tr>
<tr><td>Sobrenome:</td><td><html:text property="sobrenome"/></td></tr>
<tr><td>Email:</td><td><html:text property="email"/></td></tr>
<tr><td>Fone:</td><td><html:text property="fone"/></td></tr>
</table>
<html:submit value="Assine!"/>
</html:form>
</center>
</body></html>
confirma.jsp: (Usando taglib Struts Beans – NÃO PROGRAMAR ASSIM!!!)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
<html><head><title>Confirmação</title></head>
<body><center><h1>Confirmação</h1>
Muito obrigado, prepare seu Inbox para nosso spam!
<table>
<tr><td>Nome:</td>
<td><bean:write name="contatoForm" property="nome"/></td></tr>
<tr><td>Sobrenome:</td>
<td><bean:write name="contatoForm" property="sobrenome"/></td></tr>
<tr><td>Email:</td>
<td><bean:write name="contatoForm" property="email"/></td></tr>
<tr><td>Fone:</td>
<td><bean:write name="contatoForm" property="fone"/></td></tr>
</table>
</center>
</body></html>
confirma.jsp: (Usando taglib JSTL Core – FAZER DESSA MANEIRA!!!)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html><head><title>Confirmação</title></head>
<body><center><h1>Confirmação</h1>
Muito obrigado, prepare seu Inbox para nosso spam!
<table>
<tr><td>Nome:</td>
<td><c:out value="${contatoForm.nome}"/></td></tr>
<tr><td>Sobrenome:</td>
<td><c:out value="${contatoForm.sobrenome}"/></td></tr>
<tr><td>Email:</td>
<td><c:out value="${contatoForm.email}"/></td></tr>
<tr><td>Fone:</td>
<td><c:out value="${contatoForm.fone}"/></td></tr>
</table>
</center>
</body></html>
valor-errado.jsp:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head><title>Valor faltando ou incorreto!</title></head>
<body><center>
<h2>Valor faltando ou incorreto: <c:out value="${messageBean.message}"/>!</h2>
Por favor, <a href="assina.jsp">Tente novamente</a>.
</center>
</body></html>
assina2.jsp:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
<html><head><title>Assinatura</title></head>
<body><h1 ALIGN="CENTER">Assinatura</h1>
Digite seus dados de contato para receber nossa Newsletter!<p/><center>
<html:form action="/assina2">
<c:if test="${not empty contatoForm.warning}">
Campo faltando ou inválido:
<bean:write name="contatoForm" property="warning" filter="false"/>
</c:if>
<table>
<tr><td>Nome:</td><td><html:text property="nome"/></td></tr>
<tr><td>Sobrenome:</td><td><html:text property="sobrenome"/></td></tr>
<tr><td>Email:</td><td><html:text property="email"/></td></tr>
<tr><td>Fone:</td><td><html:text property="fone"/></td></tr>
</table>
<html:submit value="Assine!"/>
</html:form></center></body></html>
struts-config.xml:
<struts-config>
<form-beans>
<form-bean name="contatoForm" type="ContatoForm"/>
</form-beans>
<global-forwards>
<forward name="sucesso" path="/confirma.jsp"/>
</global-forwards>
<action-mappings>
<action path="/assina" type="AssinaAction" name="contatoForm" scope="request">
<forward name="valor-errado" path="/valor-errado.jsp"/>
</action>
<action path="/assina2" type="AssinaAction2" name="contatoForm"
scope="request">
<forward name="valor-errado" path="/assina2.jsp"/>
</action>
</action-mappings>
</struts-config>
Struts Taglibs
Struts inclui várias taglibs:
Bean, Logic (maior parte obsoleta; usar JSTL)
HTML: Gera elementos, útil para forms
http://struts.apache.org/userGuide/struts-html.html
Nested: Relacionamentos entre tags (pai/filho)
Utilities: Diversos
Tiles
Tiles
Taglib da Struts
Permite compor páginas complexas, juntando
fragmentos (tiles) reutilizáveis
Como <jsp:include>, mas mais flexível e estruturado
Facilidade de template
Separa estrutura, layout e componentes
Permite recombinar estes elementos facilmente
http://struts.apache.org/userGuide/dev_tiles.html
Tiles: Preparando
Configuração do struts-config.xml:
(copiar do struts-blank.war)
<controller processorClass=
"org.apache.struts.tiles.TilesRequestProcessor"/>
<plug-in className="org.apache.struts.tiles.TilesPlugin">
<set-property property="definitions-config"
value="/WEB-INF/tiles-defs.xml"/>
<set-property property="moduleAware" value="true"/>
</plug-in>
Tiles: Planejando
Comece planejando seu layout "master"...
Cabeçalho
Menu
Corpo
Rodapé
Tiles: Planejando
Defina estrutura de página/layout/fragmentos
página1
página2
página3
layout
Menu
Cabeçalho
Rodapé
Corpo
Tiles: Implementando Página
(instancia/parametriza o layout)
pagina1.jsp:
<%@taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %>
<tiles:insert page="/layout.jsp" flush="true">
<tiles:put name="titulo" value="Página 1"/>
<tiles:put name="cabecalho" value="/cabecalho.html"/>
<tiles:put name="menu" value="/menu.jsp"/>
<tiles:put name="rodape" value="/rodape.jsp"/>
<tiles:put name="corpo" value="/corpo1.jsp"/>
</tiles:insert>
Tiles: Implementando Layout
layout.jsp:
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %>
<html><head>
<title><tiles:getAsString name="titulo"/></title></head>
<body><table width="100%">
<tr><td colspan="2"><tiles:insert attribute="cabecalho"/></td></tr>
<tr><td width="120"><tiles:insert attribute="menu"/></td>
<td><tiles:insert attribute="corpo"/></td></tr>
<tr><td colspan="2"><tiles:insert attribute="rodape"/></td></tr>
</ table></body></html>
Tiles: Implementando os Tiles
JSPs, HTMLs ou imagens, normais
Exemplo: cabecalho.html
<a href="http://www.companhia.com">
<img src="/BannerEsq.gif" align="left" border="0">
</a>
<img src="/BannerDir.gif" align="right" border="0">
Tiles: Usando Definitions
Especificação declarativa das páginas
Suporta herança de páginas
JSPs de página reduzidas 1 insert:
<%@taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %>
<tiles:insert definition="pagina2.page" flush="true"/>
Ainda mais facilidade para sites dinâmicos
JSP pode decidir entre várias definitions
Tiles-defs.xml:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd">
<tiles-definitions>
<definition name=".layoutBase" path="/layout.jsp">
<put name="titulo" value=""/>
<put name="cabecalho" value="cabecalho.html"/>
<put name="menu" value="menu.jsp"/>
<put name="rodape" value="rodape.jsp"/>
<put name="corpo" value=""/>
</definition>
<definition name="pagina1.page" extends=".layoutBase" >
<put name="titulo" value="Página 2"/>
<put name="corpo" value="corpo2.jsp"/>
</definition>
</tiles-definitions>
pagina1.jsp
layout.jsp
(titulo="Página 1")
cabecalho.html
menu.jsp
corpo1.jsp
rodape.jsp
pagina2.jsp
layout.jsp
(titulo="Página 2")
pagina3.jsp
layout.jsp
(titulo="Página 3")
cabecalho.html
cabecalho.html
rodape.jsp
menu.jsp
menu.jsp
corpo2.jsp
corpo3.jsp
rodape.jsp
Validação
Opções de validação
Sem Validator: ad-hoc
Validação Manual (ou semi-automática)
Validação Automática
Código de validação, onde?
Form (e/ou JavaScript): Só regras encapsuláveis no
bean; ex.: formato de string; valores mín/máx
Action: Acesso à lógica de negócio, database, etc.
http://struts.apache.org/userGuide/dev_validator.html
Validação Manual
Implementar ActionForm.validate()
public ActionErrors validate (
ActionMapping map, HttpServletRequest req) {
ActionErrors ae = super.validate(map, req);
if (ae == null) ae = new ActionErrors();
if (...) ae.add("nome", new ActionMessage("contatoForm.nome"));
if (...) ae.add("email", new ActionMessage("contatoForm.email"));
... return ae;
}
Detecte todos os erros, não só o primeiro!
struts-config.xml:
<action path="/assina" type="AssinaAction"
name="contatoForm" scope="request"
input="/assina.jsp" validate="true">
</action>
<message-resources
parameter="MessageResources" null="false"/>
WEB-INF/classes/MessageResources.properties:
contatoForm.nome=Nome não pode ser vazio
contatoForm.email=Email deve ter formato "conta@host"
assina.jsp:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<html><head><title>Assinatura</title></head>
<body><h1 align="center">Assinatura</h1>
Digite seus dados de contato para receber nossa Newsletter!<p/>
<center>
<html:form action="/assina">
<table>
<html:errors/>
<tr><td>Nome:</td><td><html:text property="nome"/></td></tr>
<tr><td>Sobrenome:</td><td><html:text property="sobrenome"/></td></tr>
<tr><td>Email:</td><td><html:text property="email"/></td></tr>
<tr><td>Fone:</td><td><html:text property="fone"/></td></tr>
</table>
<html:submit value="Assine!"/>
</html:form>
</center>
</body></html>
public class AssinaAction extends Action {
public ActionForward execute (ActionMapping map,
ActionForm aForm, HttpServletRequest req,
HttpServletResponse resp) throws Exception {
// Se chegou até aqui, é porque o Struts já
// executou a validação com sucesso!
return map.findForward("sucesso");
}
}
Validação Automática
Validação declarada em arquivo XML
Validação Server-Side (Validator), obrigatória
Mais segura, não contornável pelo usuário
Validação Client-Side (JavaScript), opcional
Mais eficiente, evita request ao servidor e refresh
struts-config.xml:
<plug-in
className="org.apache.struts.validator.ValidatorPlugIn">
<set-property property="pathnames"
value="/WEB-INF/validator-rules.xml, /WEB-INF/validation.xml"/>
</plug-in>
validator-rules.xml, validation.xml: extraia do struts-blank.war
WEB-INF/classes/MessageResources.properties:
# Mensagens-padrão (copiar do comentário no validator-rules.xml):
errors.required={0} é obrigatório.
errors.minlength={0} não pode ter menos que {1} caracteres.
...
validation.xml:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE form-validation PUBLIC
"-//Apache Software Foundation//DTD Commons Validator Rules Configuration
1.1.3//EN" "http://jakarta.apache.org/commons/dtds/validator_1_1_3.dtd">
<form-validation>
<formset>
<form name="contatoForm">
<field property="nome" depends="required"><arg key="contatoForm.nome"/></field>
<field property="sobrenome" depends="required">
<arg key="contatoForm.sobrenome"/> </field>
<field property="email" depends="required,mask">
<arg key="contatoForm.email"/>
<var>
<var-name>mask</var-name>
<var-value>^([\w]+)(.[\w]+)*@([\w]+)(.[\w]{2,3}){1,2}$</var-value>
</var>
</field>
<field property="fone" depends="required"> <arg key="contatoForm.fone"/> </field>
</form>
</form-validation>
assina.jsp:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<html><head><title>Assinatura</title></head>
<body><h1 align="center">Assinatura</h1>
<p align="center">Digite seus dados de contato para receber nossa Newsletter!</p>
<center>
<html:form action="/assina" onsubmit="return validateContatoForm(this);">
<table>
<tr><td>Nome:</td><td><html:text property="nome"/>
<html:errors property="nome"/></td></tr>
<tr><td>Sobrenome:</td><td><html:text property="sobrenome"/>
<html:errors property="sobrenome"/> </td></tr>
<tr><td>Email:</td><td><html:text property="email"/>
<html:errors property="email"/> </td></tr>
<tr><td>Fone:</td><td><html:text property="fone"/>
<html:errors property="fone"/> </td></tr>
</table>
<html:submit value="Assine!"/>
</html:form><html:javascript formName="contatoForm"/></center></body></html>
public class ContatoForm extends ValidatorForm {
private String nome="";
private String sobrenome="";
private String email="";
private String fone="";
public String getNome () { return nome; }
public void setNome (String nome) { this.nome = nome; }
public String getSobrenome () { return sobrenome; }
public void setSobrenome (String sn) {this.sobrenome = sn;}
public String getEMail () { return email; }
public void setEMail (String email) { this.email = email; }
public String getFone () { return fone; }
public void setFone (String nome) { this.fone = fone; }
} // Nenhum código de validação!
Validação no Servidor
Validação no Cliente
Validação: Capacidades
Validações predefinidas:
required, requiredif, validwhen, minlength, maxlength
mask, creditCard, email, url
integer, float, double, long, short, byte, date
intRange, longRange, floatRange, doubleRange
Definições globais (constantes)
Validadores plugáveis
Validação Híbrida (Manual + Automática)