As coisas estúpidas feitas quando se trabalha com programação

Java, Java EE, Programação, Uncategorized No Comments

Este post foi traduzido e alterado deste link:
http://blog.cherouvim.com/the-stupidest-things-ive-done-in-my-programming-job/

Observação: Não fui eu quem escreveu este post. Apenas fiz a tradução e alteração pois achei muito interessante.

As coisas estúpidas feitas quando se trabalho com programação

  1. ORM
    • Estupidez
      Construir um framework próprio de ORM.
    • Consequências
      O projeto vira uma bagunça depois de 2 anos de manutenção com gambiarras para passar pelo meu framework ORM desenvolvido e chamar comandos SQL’s customizados.
    • O que deveria ter sido feito?
      Usar hibernate, iBATIS, Cayenne ou outros parecidos.
  2. EAV
    • Estupidez
      Usar uma modelagem de banco de dados do tipo Entity-Attribute-Value model.
    • Consequências
      Solução não escalável e impossibilidade de rodar queries úteis no banco de dados.
    • O que deveria ter sido feito?
      Usar uma modelagem de banco de dados normalizada.
  3. Acesso ao Banco de dados
    • Estupidez
      Sincronizar o acesso ao banco de dados com uma conexão compartilhada.
    • Consequências
      Escalabilidade zero. Tempos de respostas muito lentos quando mais de 10 usuários estavam utilizando a aplicação.
    • O que deveria ter sido feito?
      Não fazer isto e utilizar um pool de conexões como c3p0 e usar uma “nova” (reutilizada) conexão retornada do pool para cada ciclo de request / response.
  4. IDE
    • Estupidez
      Evitar aprender e utilizar uma IDE.
    • Consequências
      Falta de habilidade de compilar, testar e fazer o deploy da aplicação rapidamente e / ou geralmente fazer qualquer coisa útil.
    • O que deveria ter sido feito?
      Familiarizar-me com uma IDE. Ex.: NetBeans, eclipse, etc.
  5. Transações
    • Estupidez
      Não usá-las.
    • Consequências
      Corromper os dados da aplicação.
    • O que deveria ter sido feito?
      Usar transações do banco de dados. No MySQL usar InnoDB.
  6. Prepared Statements
    • Estupidez
      Usar Statements, concatenação de strings e escapar caracteres para montar queries “seguras”.
    • Consequências
      Possibilidade de SQL Injections na aplicação. É possível logar utilizando por exemplo “or 1=1;delete from users;” e alterar o estado do banco de dados de uma forma muito desagradável.
    • O que deveria ter sido feito?
      Usar Prepared Statements que montam e escapam corretamente as queries dependendo do driver JDBC utilizado.
  7. Lógica de Negócios
    • Estupidez
      Fazer isto nos templates (JSP).
    • Consequências
      Aplicação de manutenção difícil.
    • O que deveria ter sido feito?
      Fazer isto utilizando o padrão MVC com servlets ou com um Front Controller. Melhor se utilizar um framework MVC open source como Struts, Spring MVC, etc.

Freemarker + Sitemesh + Spring

Java EE No Comments

Depois de muito tempo consegui fazer minha aplicação utilizando Freemarker (template engine) + Sitemesh (web page layout framework) + Spring (IOC, etc) funcionar.

Basicamente, basta configurar no web.xml o filtro do Sitemesh (com.opensymphony.sitemesh.webapp.SiteMeshFilter), a servlet do Freemarker que vem com o Sitemesh (com.opensymphony.module.sitemesh.freemarker.FreemarkerDecoratorServlet) e a servlet do Spring MVC (org.springframework.web.servlet.DispatcherServlet).

O filtro do Sitemesh é aplicado a todas as requisições, a servlet do freemarker às urls *.ftl e a servlet do Spring às urls *.html.

O problema que eu tive foi que os templates do Freemarker e do Sitemesh não eram encontrados. No web.xml, um dos parâmetros iniciais da servlet do Freemarker é o diretório dos templates (TemplatePath). Configurei este parâmetro para “/WEB-INF/templates/”. E no arquivo decorators.xml configurei que os templates do Sitemesh ficavam no diretório “/decorators”. O que acontecia é que o diretório dos templates do Sitemesh deveriam ficar dentro do diretório dos templates do Freemarker. Logo a estrutura de diretórios é dessa maneira: /WEB-INF/templates/decorators.

Segue abaixo o link com a aplicação de exemplo desta integração:
http://www.fnbrandao.com.br/blog/files/freemarker.tar.gz

PostBack no JSF

Java EE, Programação 2 Comments

Um grande problema com o JSF é que na especificação 1.1 não existe algum método para saber se a página está sendo executada no postback (quando você reenvia os dados para ela mesma). Mas existe o seguinte workaround:

package web.jsf.vh;

import java.io.IOException;
import java.util.Locale;
import java.util.Map;

import javax.faces.FacesException;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;

public class PostBackViewHandler extends ViewHandler {

  protected ViewHandler baseViewHandler;

  public CustomViewHandler(ViewHandler viewHandler) {
    super();
    this.baseViewHandler = viewHandler;
  }

  public Locale calculateLocale(FacesContext facesContext) {
    return baseViewHandler.calculateLocale(facesContext);
  }

  public String calculateRenderKitId(FacesContext facesContext) {
    return baseViewHandler.calculateRenderKitId(facesContext);
  }

  public UIViewRoot createView(FacesContext facesContext, String arg1) {
    setPostback(facesContext, false);
    return baseViewHandler.createView(facesContext, arg1);
  }

  public String getActionURL(FacesContext facesContext, String arg1) {
    return baseViewHandler.getActionURL(facesContext, arg1);
  }

  public String getResourceURL(FacesContext facesContext, String arg1) {
    return baseViewHandler.getResourceURL(facesContext, arg1);
  }

  public void renderView(FacesContext facesContext, UIViewRoot arg1) throws IOException, FacesException {
    baseViewHandler.renderView(facesContext, arg1);

  }

  public UIViewRoot restoreView(FacesContext facesContext, String arg1) {
    setPostback(facesContext, true);
    return baseViewHandler.restoreView(facesContext, arg1);
  }

  public void writeState(FacesContext facesContext) throws IOException {
    baseViewHandler.writeState(facesContext);
  }

  public Map getRequestScope(FacesContext facesContext) {
    return (Map)facesContext.getApplication().createValueBinding(“#{requestScope}”).getValue(facesContext);
  }

  public void setPostback(FacesContext facesContext, boolean value) {
    getRequestScope(facesContext).put(“ispostback”, new Boolean(value));
  }

}

O código acima é um View Handler do JSF, agora basta registrá-lo no “facesconfig.xml”:

<application>
   <view-handler>web.jsf.vh.PostBackViewHandler</view-handler>
</application>

E para saber em sua página se é um postback:

public boolean isPostback() {
    FacesContext facesContext = FacesContext.getCurrentInstance();
    Map requestScope = (Map)facesContext.getApplication().createValueBinding(“#{requestScope}”).getValue(facesContext);
    boolean ispostback = ((Boolean)requestScope.get(“ispostback”)).booleanValue();
    return ispostback;
}

Obs.: Esse workaround só funciona se o estado da view não é salva no servidor.

JSF – Duplicate componentID

Java EE, Programação No Comments

Utilizando o DataTable do RichFaces em uma simples página, após um reload na página sempre era retornado o seguinte erro:

Duplicate component ID '_id0:_dataTable:_id1' found in view.

Segue abaixo o código da página que causava o erro:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:a4j="https://ajax4jsf.dev.java.net/ajax"
	xmlns:rich="http://richfaces.ajax4jsf.org/rich">

	<h:form binding="#{UserConsultPage.component}">
		<rich:dataTable id="dataTable"
			binding="#{UserConsultPage.dataTable}" var="item">
			<rich:column>
				<f:facet name="header">
					<h:outputText value="Name" />
				</f:facet>
				<h:outputText value="#{item.name}" />
			</rich:column>
			<rich:column>
				<f:facet name="header">
					<h:outputText value="Email" />
				</f:facet>
				<h:outputText value="#{item.email}" />
			</rich:column>
		</rich:dataTable>

		<h:commandButton id="btnLoad" actionListener="#{UserConsultPage.load}" />
	</h:form>

</ui:composition>

A solução foi colocar id’s em todos os componentes “outputText”. Segue abaixo a página modificada:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:a4j="https://ajax4jsf.dev.java.net/ajax"
	xmlns:rich="http://richfaces.ajax4jsf.org/rich">

	<h:form binding="#{UserConsultPage.component}">
		<rich:dataTable id="dataTable"
			binding="#{UserConsultPage.dataTable}" var="item">
			<rich:column>
				<f:facet name="header">
					<h:outputText id="headerNameValue" value="Name" />
				</f:facet>
				<h:outputText id="nameValue" value="#{item.name}" />
			</rich:column>
			<rich:column>
				<f:facet name="header">
					<h:outputText id="headerEmailValue" value="Email" />
				</f:facet>
				<h:outputText id="emailValue" value="#{item.email}" />
			</rich:column>
		</rich:dataTable>

		<h:commandButton id="btnLoad" actionListener="#{UserConsultPage.load}" />
	</h:form>

</ui:composition>

JSF + Spring + JPA + Hibernate

Java EE, Programação 1 Comment

Ao tentar fazer o deploy de uma aplicação utilizando JSF, Spring e JPA (com implementação do hibernate) é recebida a seguinte exception:

javax.persistence.PersistenceException: org.hibernate.SessionException: Session is closed!

Para resolver este problema, basta apenas adicionar o seguinte filtro no “web.xml”:

<filter>
	<filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
	<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>

<filter-mapping>
	<filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

Debug no Tomcat 5.5

Java EE, Programação No Comments

Para rodar um debug remoto no tomcat 5.5 no Ubuntu, basta editar o arquivo “/etc/init.d/tomcat5.5″ e colocar a seguinte linha:


-Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n

antes das linhas:


# Define other required variables
CATALINA_PID="/var/run/$NAME.pid"
BOOTSTRAP_CLASS=org.apache.catalina.startup.Bootstrap
JSVC_CLASSPATH="/usr/share/java/commons-daemon.jar:$CATALINA_HOME/bin/bootstrap.jar"

Problema no botão back do navegador

Java EE, Programação No Comments

O botão back do navegador é muito útil ao usuário para ele conseguir navegar entre diferentes páginas e websites. Entretanto, o botão back pode trazer diversos problemas aos sites. Uma maneira de “travar” o botão é desabilitar completamente o cache das páginas que não devem permitir que sejam acessadas através do botão back.
Para se fazer isso no Java EE basta apenas criar um filtro e mapeá-lo para as url’s necessárias.
Segue abaixo o código do filtro:

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

public class ClearCacheFilter implements Filter {

    public void destroy() {
    }

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        httpResponse.setHeader("Expires", "Sat, 1 Jan 1990 12:00:00 GMT");
        httpResponse.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
        httpResponse.addHeader("Cache-Control", "post-check=0, pre-check=0");
        httpResponse.setHeader("Pragma", "no-cache");

        chain.doFilter(request, response);
    }

    public void init(FilterConfig filterConfig) throws ServletException {
    }

}

Basicamente o que o filtro faz é setar as diretivas de cache para o navegador. Primeiro ele seta a data de expiração da página para uma data passada. Depois, o código seta 3 diretivas para desabilitar o cache:

  • no-store: não armazena o conteúdo no cache
  • no-cache: o browser deve enviar o request para o servidor mesmo se ele possuir uma cópia do que será enviado
  • must-revalidate: revalida o cache em outro request, se estiver expirado

Depois, são setadas duas diretivas específicas do Internet Explorer. Estas diretivas indicam que o conteúdo sempre deve ser atualizado. E por último, é desabilitado o cache para o protocolo HTTP/1.0 (já que as outras 3 são específicas do HTTP/1.1).

Por último é preciso configurar o filtro na aplicação web. Segue abaixo o trecho de código referente à configuração para o filtro, bem como para ele ser aplicado a todas as páginas da aplicação:

<filter>
	<filter-name>ClearCacheFilter</filter-name>
	<filter-class>br.com.uol.ps.common.web.ClearCacheFilter</filter-class>
</filter>

<filter-mapping>
	<filter-name>ClearCacheFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>