Esmeril Programming

Bom… EnJói (Enjoy)

Ríder (header)
Origem (source code)
Ruliúde (hollywood)
Têxtpád (textpad)
Iran máidi (Iron Maiden)
Facête (Facade pattern)
Ricuesti paramíter (request parameter)
Déprékatédi (deprecated)
Privêiti (private)
Urubuntu (Ubuntu)
Avorrad (RAD 7)
WebEsfirra (Websphere)
kapabílitis (capabilities)
Píu Rabo (Pearl Harbor)
Rilísi (Release)
Fáitom (Python)

XhtmlRenderer HTML to PDF

Sem muita enrolação (como de costume aqui no blog), certo dia em um projeto eu precisava montar
um documento no formato de uma carta e precisava fazer isso de forma rápida…
Em mãos eu tinha: POI, iText, jasperReports (com iReport).

Usei uma fração do meu tempo para procurar um projeto que:
-Usaria como entrada um texto;
-Dinamicamente modificasse o texto via java;
-Transformasse a saída no formato pdf;

Por sorte encontrei o http://xhtmlrenderer.dev.java.net

A idéia era pegar um html com certa formatação e obter um pdf resultante desse html modificado via java.
Ex.: “Caro Sr. variavelNomeCliente,
O contrato de número variavelNumeroDoContrato
etc etc etc etc etc etc etc etc etc etc etc etc
etc etc etc etc etc etc etc etc etc etc etc etc
etc etc etc etc etc etc etc etc etc etc etc etc
etc etc etc etc etc etc etc etc etc etc etc etc…”

Eu não queria usar nenhum template .doc para modificar via POI muito menos brincar com formatação no iReport muito menos fazer formatação de texto com iText na mão (apesar de achar isso bacana).

O que você vai precisar ?!
-xhtmlrenderer
-itext
-xml-apis-xerces

O código..
Criei uma classe utilitária para cuidar da renderização.

package util;

import java.io.ByteArrayInputStream;
import java.io.OutputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.xhtmlrenderer.pdf.ITextRenderer;

public class XhtmlRendererUtil {

	public static void build(String sb, OutputStream os) throws Exception {

		DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
		ByteArrayInputStream bais = new ByteArrayInputStream(sb.getBytes());
		Document document = docBuilder.parse(bais);
		ITextRenderer itextRenderer = new ITextRenderer();
		itextRenderer.setDocument(document, null);
		OutputStream outputStream = os;
		itextRenderer.layout();
		itextRenderer.createPDF(outputStream);
		os.close();
	}

}

Abaixo um servlet simples pra testar a classe utilitária de renderização:

package servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import util.XhtmlRendererUtil;

public class HtmlToPdfServlet extends HttpServlet {

	public void service(HttpServletRequest request, HttpServletResponse response)
	    throws ServletException, IOException {

		response.setContentType("application/pdf");
		response.setHeader("Content-Disposition", "attachment; filename=test.pdf");
		response.setHeader("Pragma", "public");
		response.setHeader("Cache-Control", "max-age=0");

		try {
		   XhtmlRendererUtil.build(buildHTML(), response.getOutputStream());
		} catch(Exception e) {
			e.printStackTrace();
		}

	}

	private String buildHTML() {

		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.append("<html>");
		stringBuilder.append("<body>");
		stringBuilder.append("<h1>Bom, ta transformando...</h1>");
		stringBuilder.append("<h2>O problema agora eh como pegar essa Tripa HTML</h2>");
		stringBuilder.append("<h3>diretamente da pagina renderizada</h3>");
		stringBuilder.append("<h4>via javascript puro no submit, ajax sei la o que</h4>");
		stringBuilder.append("<h5>pra transformar uma pagina (layout com tudo nem sei se da)</h5>");
		stringBuilder.append("<h5>num pdf.</h5>");
		stringBuilder.append("</body></html>");
		return stringBuilder.toString();
	}

}

lobometal_xhtmlrenderer.odt
(renomear para .zip)

Eu prefiro programaticamente…

Um dia desses resolvi migrar uma pequena aplicação Struts 1x para Mentawai quando me deparei com o struts-config.xml…

Bom, depois de 1 cigarro a ficha caiu, e sem mais delongas olhem o resultado:

struts-config.xml

<action-mappings>
    <action
      path="/WSTestMethod1Action"
      type="com.coral.ws.test.web.action.WSTestAction"
      name="WSTestForm"
      scope="request"
      input="/tenant/method1.jsp"
      parameter="method"
      unknown="false"
      validate="false" >
    </action>
    <action
      path="/WSTestMethod2Action"
      type="com.coral.ws.test.web.action.WSTestAction"
      name="WSTestForm"
      scope="request"
      input="/tenant/method2.jsp"
      parameter="method"
      unknown="false"
      validate="false" >
    </action>
    <action
      path="/WSTestMethod4Action"
      type="com.coral.ws.test.web.action.WSTestAction"
      name="WSTestForm"
      scope="request"
      input="/tenant/method4.jsp"
      parameter="method"
      unknown="false"
      validate="false" >
    </action>
    <action
      path="/WSTestMethod5Action"
      type="com.coral.ws.test.web.action.WSTestAction"
      name="WSTestForm"
      scope="request"
      input="/tenant/method5.jsp"
      parameter="method"
      unknown="false"
      validate="false" >
    </action>
    <action
      path="/WSTestMethod6Action"
      type="com.coral.ws.test.web.action.WSTestAction"
      name="WSTestForm"
      scope="request"
      input="/tenant/method6.jsp"
      parameter="method"
      unknown="false"
      validate="false" >
    </action>
    <action
      path="/WSTestMethod7Action"
      type="com.coral.ws.test.web.action.WSTestAction"
      name="WSTestForm"
      scope="request"
      input="/tenant/method7.jsp"
      parameter="method"
      unknown="false"
      validate="false" >
    </action>
    <action
      path="/WSTestMethod8Action"
      type="com.coral.ws.test.web.action.WSTestAction"
      name="WSTestForm"
      scope="request"
      input="/tenant/method8.jsp"
      parameter="method"
      unknown="false"
      validate="false" >
    </action>
    <action
      path="/WSTestMethod9Action"
      type="com.coral.ws.test.web.action.WSTestAction"
      name="WSTestForm"
      scope="request"
      input="/tenant/method9.jsp"
      parameter="method"
      unknown="false"
      validate="false" >
    </action>
    <action
      path="/WSTestMethod10Action"
      type="com.coral.ws.test.web.action.WSTestAction"
      name="WSTestForm"
      scope="request"
      input="/tenant/method10.jsp"
      parameter="method"
      unknown="false"
      validate="false" >
    </action>
    <action
      path="/WSTestMethod11Action"
      type="com.coral.ws.test.web.action.WSTestAction"
      name="WSTestForm"
      scope="request"
      input="/tenant/method11.jsp"
      parameter="method"
      unknown="false"
      validate="false" >
    </action>
    <action
      path="/WSTestMethod12Action"
      type="com.coral.ws.test.web.action.WSTestAction"
      name="WSTestForm"
      scope="request"
      input="/tenant/method12.jsp"
      parameter="method"
      unknown="false"
      validate="false" >
    </action>
    <action
      path="/WSTestMethod13Action"
      type="com.coral.ws.test.web.action.WSTestAction"
      name="WSTestForm"
      scope="request"
      input="/tenant/method13.jsp"
      parameter="method"
      unknown="false"
      validate="false" >
    </action>
    <action
      path="/WSTestMethod14Action"
      type="com.coral.ws.test.web.action.WSTestAction"
      name="WSTestForm"
      scope="request"
      input="/tenant/method14.jsp"
      parameter="method"
      unknown="false"
      validate="false" >
    </action>
    <action
      path="/WSTestMethod15Action"
      type="com.coral.ws.test.web.action.WSTestAction"
      name="WSTestForm"
      scope="request"
      input="/tenant/method15.jsp"
      parameter="method"
      unknown="false"
      validate="false" >
    </action>
    <action
      path="/WSTestMethod16Action"
      type="com.coral.ws.test.web.action.WSTestAction"
      name="WSTestForm"
      scope="request"
      input="/tenant/method16.jsp"
      parameter="method"
      unknown="false"
      validate="false" >
    </action>
    <action
      path="/WSTestMethod17Action"
      type="com.coral.ws.test.web.action.WSTestAction"
      name="WSTestForm"
      scope="request"
      input="/tenant/method17.jsp"
      parameter="method"
      unknown="false"
      validate="false"  >
    </action>
    <action
      path="/WSTestMethod18Action"
      type="com.coral.ws.test.web.action.WSTestAction"
      name="WSTestForm"
      scope="request"
      input="/tenant/method18.jsp"
      parameter="method"
      unknown="false"
      validate="false" >
    </action>
  </action-mappings>

ApplicationManager utilizando Convention over Configuration

for (int i = 1; i <= 18; i++) {
    action("/WSTestMethod" + i + "Action", WSTestAction.class)
    .fwdOk("/wstest/method" + i + ".jsp");
}

Eu prefiro programaticamente…

Detectando java plugin de maneira simples

Após algumas tentativas (com certo sucesso) de implementar algo com applet e javascript para detectar a versão do plugin do java instalado no browser cliente, descobri esse dectector não intrusivo:

http://www.pinlady.net/PluginDetect/

O autor fez um trabalho muito bacana e é realmente simples agora detectar o java no browser do cliente veja
só:

<script type="text/javascript">
//verifica se tem java instalado
var status = PluginDetect.isMinVersion('Java', '0', 'getJavaInfo.jar');
var installed = status >=0 ? true : false;
//verifica se a versão do java é no mínimo 1.5
var vstatus = PluginDetect.isMinVersion('Java', '1.5', 'getJavaInfo.jar');
var vinstalled = vstatus == 1 ? true : false;
if (!installed) {
    alert('Seu browser não tem o plugin do java instalado');
}

if (!vinstalled) {
    alert('A versão do plugin do java instalado é inferior a 1.5');
}
</script>

Você vai precisar também de dois arquivos: “PluginDetect.js” e “getJavaInfo.jar” que você pode obter no próprio site do projeto.

Basta importar o PluginDetect.js no header da página

<script type="text/javascript" src="PluginDetect.js"></script>

e jogar o jar na mesma pasta da página.

Displaytag formatando datas e números

Displaytag é um componente bem interessante que trata a parte de grids.

O que vou mostrar aqui é uma maneira rápida de formatar datas e números sem que você precise fazer isso nos seus objetos.

Basicamente você irá utilizar o atributo format:

format=”{0,date,dd/MM/yyyy}”
format=”{0.number.0.000,00} R$”

Aqui um exemplo completo para data

<display:column format="{0,date,dd/MM/yyyy}" title="Início" property="startDate" />

Gerando relatórios do Schemaspy via Maven

Antes de mais nada, para um melhor entendimento sobre o SchemaSpy e um startup mais rápido visite : Boaglio.com

Pra quem não conhece o Maven: Maven Home

Então, agora já que você conhece o SchemaSpy vamos partir pra configuração do mesmo com o Maven para aproveitar “a carona” do comando “mvn site” na criação dos relatórios do SchemaSpy.

Criei um projeto blank apenas para mostrar a configuração em si, no caso iremos atentar apenas para os arquivos de configuração.

Estrutura padrão Maven criada no eclipse utilizando o plugin M2 (http://m2eclipse.codehaus.org/update-dev)

Update site para plugin:

Estrutura:

Quando você instala o Maven ele cria no diretório do usuário uma pasta “.m2″. Nessa pasta ficará o repositório das dependências (jars) bem como um arquivo de configuração chamado “settings.xml” entre outras coisas.

Como o SchemaSpy ainda não possui plugin pra maven (pelo menos eu não encontrei nenhum) precisamos “Rodá-lo” de alguma forma e para isso editaremos esse arquivo settings.xml com a seguinte configuração:

<settings>
<profiles>
<profile>
<development>
<activation>
<activeByDefault>true</activeByDefault>
<activation>
<properties>
<!-- O arquivo jar do schemaspy foi colocado nesse diretório "d:\java" -->
<schemaspy.home>d:\java\</schemaspy.home>
<schemaspy.version>3.1.1</schemaspy.version>
<!-- Aqui indicamos onde ficarão os arquivos de documentação gerados -->
<!-- Notem que ficará dentro do site gerado pelo Maven -->
<schemaspy.report.dir>${project.build.directory}/site/schemaspy</schemaspy.report.dir>
<!--Você deve fornecer o driver do seu banco, aqui no caso o Mysql-->
<schemaspy.jdbc.driver>c:\java\mysql-connector-java-5.0.4.jar</schemaspy.jdbc.driver>
<!-- A seguir configurações comuns para acesso ao banco de dados-->
<schemaspy.database>spytes</schemaspy.database>
<schemaspy.driverClassName>com.mysql.jdbc.Driver</schemaspy.driverClassName>
<schemaspy.username>spytest</schemaspy.username>
<schemaspy.password>1234</schemaspy.password>
<schemaspy.database.host>localhost</schemaspy.database.host>
</properties>
</profile>
</profiles>
</settings>

Ok. Agora vamos para o POM:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>
<groupId>spytest</groupId>
<artifactId>spytest</artifactId>
<packaging>war</packaging>
<version>0.0.1</version>
<!--Note que todas as variáveis ${algumaCoisa} são as mesmas-->
<!--definidas no arquivo settings.xml visto anteriormente.-->
<build>
<plugins>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>site</phase>
<configuration>
<tasks>
<echo>Gerando relatorio sobre o banco em: ${schemaspy.report.dir}</echo>
<java jar="${schemaspy.home}/schemaSpy_${schemaspy.version}.jar" output="${schemaspy.report.dir}/schemaspy-out.log" error="${schemaspy.report.dir}/schemaspy-error.log" fork="true">

<arg line="-t=mysql" />
<arg line="-db=${schemaspy.database}" />
<arg line="-u=${schemaspy.username}" />
<arg line="-p=${schemaspy.password}" />
<arg line="-cp=${schemaspy.jdbc.driver}" />
<arg line="-host=${schemaspy.database.host}" />
<arg line="-o=${schemaspy.report.dir}" />
</java>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!--Adicione a mesma dependência do connector que você declarou no arquivo settings.xml-->
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.0.4</version>
</dependency>
</dependencies>
</project>

Bom agora vem a parte mais complicada:

Abra o shell/cmd acesse a pasta do projeto onde se encontra o arquivo pom.xml e execute o comando “mvn site”.

Se tudo deu certo você verá dentro da pasta “target/site” do seu projeto a pasta “schemaspy” com os relatórios gerados.

Abaixo você poderá efetuar o download do “projeto blank” (projeto usando a estrutura do Maven no eclipse) bem como o arquivo “settings.xml” (que deverá ficar na pasta .m2).

OBS: Após download favor renomear o arquivo spytest.odt para spytest.zip e settings.odt para settings.xml

spytest.odt
settings.odt

GridPaging com Mentawai Paginator Tag e MentaAjax

Exemplo mostrando como mesclar a tag paginator do Mentawai com a feature MentaAjax resultando em um gridPaging com navegação via ajax.

GridPaging

No seu ApplicationManager você não precisa fazer nada de diferente veja:

//Forward para city.jsp que contém o formulário de busca
action(CityAction.class).fwdOk("/city.jsp");
//Forward para city_list.jsp, que contém a tag paginator que irá construir o grid
action(CityAction.class, "loadAll") .fwdOk("/city_list.jsp");

Na action tudo o que você precisa é enviar para o output uma lista com os valores:

//Neste exemplo estou usando MentaBeans para obter a lista de objetos City do banco
public String loadAll() throws Exception {
    output.setValue("list", beanSession.loadList(new City()));
    return SUCCESS;
}

Agora o arquivo city.jsp que contém o formulário de busca com MentaAjax:

<%@ taglib uri="/WEB-INF/mentawai.tld" prefix="mtw" %>
<html>
<head>
<title>GridPaging</title>
<mtw:ajaxConfig />
<script type="text/javascript">
function find(pg) {
    var req = new mtw.request();
    req.setUrl("CityAction.loadAll.mtw");
    req.onSuccess(function(trans) {
        var res = new mtw.response(trans);
        $("grid").innerHTML = res.getString();
    });
    req.addParameter("page", pg);
    req.send();
}
</script>
</head>
<body>
<a href="javascript:void(0)" onclick="find(1)">Show !</a>
<div id="grid"></div>
</body>
</html>

No arquivo city_list.jsp iremos usar o PaginatorTag do Mentawai:

<%@ taglib uri="/WEB-INF/mentawai.tld" prefix="mtw" %>
<table>
<tr>
 <td>
  <mtw:paginator size="5" value="list">
     <mtw:isEmpty negate="true">
        Resultados <mtw:resultFrom /> - <mtw:resultTo /> de: <mtw:resultTotal /><br />
     </mtw:isEmpty>
     <mtw:hasPrevious>
        <a href="javascript:void(0)" onclick="find(<mtw:out />);">Anterior</a>
     </mtw:hasPrevious>
     <mtw:pageNumbers pagesToShow="2">
        <mtw:isCurrPage><mtw:out /></mtw:isCurrPage>
        <mtw:isCurrPage negate="true">
          <a href="javascript:void(0)" onclick=find(<mtw:out />);><mtw:out /></a>
        </mtw:isCurrPage>
     </mtw:pageNumbers>
     <mtw:hasNext>
       <a href="javascript:void(0)" onclick=find(<mtw:out />);>Próximo</a>
     </mtw:hasNext>
  </td>
 </tr>
<mtw:loop>
<tr>
  <td><mtw:out value="id" /></td>
  <td><mtw:out value="name" /></td>
</tr>
</mtw:loop>
</mtw:paginator>
</table>

Versão do Mentawai 1.11

Sutilezas do Maven

1) Deploy:
Sabemos que para acessar a sessão “Manager” do tomcat, o browser aponta para a url “/manager/html”. Pois bem se você pretende usar o Maven Tomcat Plugin preste bem atenção para não vacilar como eu

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>tomcat-maven-plugin</artifactId>
<configuration>
<server>tomcat</server>
<update>true</update>
<url>http://192.168.0.2:8080/manager/html</url>
</configuration>
</plugin>

Bastante óbvio não acham ? Para o deploy até que funciona, agora se você usar “mvn tomcat:undeploy” vai receber “de Grátis” um errão bem bacana. Para resolver isso remova o “/html” no fim ficando dessa forma:

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>tomcat-maven-plugin</artifactId>
<configuration>
<server>tomcat</server>
<update>true</update>
<url>http://192.168.0.2:8080/manager</url>
</configuration>
</plugin>

2) Problema com OutOfMemory Java Heap Space:
Crie uma variável de ambiente (um JAVA_HOME por exemplo) chamada MAVEN_OPTS
Ficando no seguinte formato (exemplo no linux):

export MAVEN_OPTS=”-Xms256m -Xmx512m -XX:PermSize=512m -XX:MaxPermSize=512m”

Se você tem 1gb de memória logicamente você não irá trocar os valores de 512 por 1024.
Essa configuração pode variar dependendo do tamanho do seu projeto, nas chamadas de comandos do tipo mvn compiler:compile , mvn package, mvn tomcat:deploy etc…

3) JAR Fantasma
Não sei se você já reparou mas…
Usando o Eclipse WTP e criando um projeto web dinâmico a IDE cria dentro do workspace o seguinte diretório oculto “metadata”. Dentro dele temos vários outros mas a atenção especial fica para esse aqui:
“.metadata/.plugins/org.eclipse.wst.server.core/tmp0/webapps”

Ele guarda o seu projeto web dinâmico para que você possa utilizar aquele “plugin” para iniciar/parar o tomcat e o mais incrível e emocionante foi que o Maven ao inves de pegar o JAR do repositório local
resolveu adotar essa pasta como bicho de estimação para pegar os JARS e compor o WAR :]

Mandei o comando no slack “find -iname meujar.jar” dentro do workspace abri todos os jars encontrados e o único jar que não havia sido alterado era o que estava nesse diretório.

Sim isso aconteceu porque o projeto em questão não é um novo projeto Maven do zero e sim um projeto já existente com estrutura de diretórios própria no qual o Maven foi incorporado.

E agora como eu faço para “rodar” minha APP sem os JARS que estão na WEB-INF/lib então ??

mvn jetty:run

4) Desabilitar testes

use -Dmaven.test.skip=true

Exemplo:
mvn package -Dmaven.test.skip=true

5) Remover jars sem uso do WAR para deploy

Adicione no pom.xml :

<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<warSourceExcludes>WEB-INF/lib/algumacoisa-1.0.1.jar, WEB-INF/lib/outra_coisa_que_nao_quero-3.1.0.jar</warSourceExcludes>
</configuration>
</plugin>