Kanban

Kanban

Tenho trabalhado num projeto de um kanban simples para tarefas pessoais baseado em JavaScript e HTML5 com local storage. Tenho utilizado no dia a dia para gerenciar minhas tarefas no trabalho, tarefas de casa, além de tarefas relativas ao desenvolvimento do próprio aplicativo. Está sendo útil para mim, e creio que pode ser para outros.

Não busco concorrer com KanbanFlow, Trello e outros kanbans que são online e compartilhados por times. Se trata apenas de um kanban local, armazenado no cache do navegador, para pequenas gestões de pequenas atividades, sem burocracia.

A quem interessar: http://github.com/rafaelodon/kanban

Feedbacks são bem vindos!

Desabilitando a execução do maven-javadoc-plugin herdado do Parent

Aconteceu comigo de vários componentes maven de um projeto herdarem um POM Parent que configura a utilização do Plugin maven-javadoc-plugin. Esse plugin varre todo o código do componente e gera os HTMLs da documentação estilo JavaDoc. No entanto, esse plugin parece demandar muitos recursos para ser executado, onerando o tempo de build. No caso de projetos com muitos componentes, isso pode inclusive significar um tempo muito grande de fila de builds para integração contínua. Para desabilitar a execução do plugin adicionei a entrada project/build/pluginManagement abaixo ao pom.xml dos componentes em questão:

<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/xsd/maven-4.0.0.xsd">

        ...

	<build>
		<pluginManagement>
			<plugins>
				<plugin>
					<artifactId>maven-javadoc-plugin</artifactId>
					<configuration>
						<skip>true</skip>
					</configuration>
				</plugin>
			</plugins>
		</pluginManagement>
	</build>

        ...

</project>

Se você desejar ainda desabilitar esse plugin por padrão mas eventualmente gerar o JavaDoc quando for necessário, é possível colocar essa configuração de build acima dentro de um profile do Maven, bastando assim escolhê-lo no momento da execução.

Criando Applets para os Painéis do Gnome 3

Qual não foi minha surpresa quando fui para o Gnome 3 e soube que o applet Giplet que podia ser adicionado aos paineis Gnome para mostrar o seu IP local, só funcionava para Gnome 2. Resolvi verificar como estava o estado-da-arte da fabricação de applets para Gnome 3. Vou relatar aqui os passos mais fáceis que encontrei.

Baixe o Panel Applet Generator, que adianta muito sua vida e permite criar applets compatíveis com Gnome 2 e 3. A forma mais fácil de fazer isso é via Git:

git clone https://github.com/palfrey/panel-applet-generator

No diretório do panel-applet-generator, execute o comando abaixo, que no caso está parametrizado para criar um Applet chamado “showmyip” com uma descrição correspondente. Para quem interessar, há outras opções disponíveis, tais como definir ícone, categoria, etc.

python panel-applet-generator.py -n showmyip -d "Displays your local IP address on the panel."

Depois disso basta entrar na pasta showmyip que será criada. Lá estará toda a estrutura básica de um Applet Gnome tanto para a versão 2 quanto para o 3. O que você precisa fazer é continuar implementando o arquivo .py principal criado, que vai ter o nome do seu applet seguido da palavra “Applet”, no caso acima showmyipApplet.py. Para quem não sabe é basicamente um programinha python baseado em Gtk. No meu caso, simplesmente adicionei um import ao socket para obter o IP e adicionei um label com o IP ao applet:

try:
    from gi.repository import Gtk
except: # Can't use ImportError, as gi.repository isn't quite that nice...
    import gtk as Gtk

import socket

def applet_factory(applet, iid, data = None):
    hostname = socket.gethostname()
    ip = socket.gethostbyname("%s.local" % hostname)
    label = Gtk.Label("IP: "+ip)
    applet.add(label)
    applet.show_all()
    return True

O passo final é gerar o .deb e instalar nosso Applet para adicionar ao painel. Nesse ponto o panel-applet-generator não ajuda muito (sugestão de pull request, hein?).

Vá até o diretório do applet e compacte-o com Tarball e Gzip e adicione o prefixo “0.1.orig.tar.gz” (o 0.1 vem do controle de versão que está descrito na estrutura do pacote DEBIAN). Dê um debuild para gerar o .deb. Em seguida basta instalar via dpkg.

cd showmyip/
tar -czf ../showmyip_0.1.orig.tar.gz .
debuild -us -uc
sudo dpkg -i ../showmyip_0.1-1_i386.deb

Para adicionar o applet criado ao painel, segure a tecla Windows + Alt clique com botão direito sobre o painel, acione “Add to Panel”, e você verá na lista de applets o seu applet querido.

Serializando e Deserializando Classes de Entidade do Hibernate/JPA para XML com XStream

Em um projeto que trabalhei tivemos a necessidade de representar em XML a nossa principal entidade de banco de dados, com [quase] todas as suas relações diretas e indiretas (uma grande árvore). A opção que se mostrou mais fácil foi o uso da biblioteca XStream. No entanto, esbarramos em alguns problemas uma vez que o Hibernate trabalha com Proxies e implementações próprias das Collections do Java, afim de promover a abordagem Lazy para a obtenção de entidades relacionadas. Nesse post demonstro como resolvemos esse problema.

Se estiver usando maven, adicione ao pom.xml da sua aplicação a dependência xstream-hibernate:

<dependency>
	<groupId>com.thoughtworks.xstream</groupId>
	<artifactId>xstream-hibernate</artifactId>
	<version>1.4.7</version>
</dependency>	

É muito importante que você crie e configure uma única instância de XStream que deverá ser usada tanto na serializaçaõ quanto na deserialização, para que tudo funcione. Configure-a da seguinte forma:

	
import org.hibernate.collection.internal.PersistentBag;
import org.hibernate.collection.internal.PersistentList;
import org.hibernate.collection.internal.PersistentMap;
import org.hibernate.collection.internal.PersistentSet;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.hibernate.converter.HibernatePersistentCollectionConverter;
import com.thoughtworks.xstream.hibernate.converter.HibernatePersistentMapConverter;
import com.thoughtworks.xstream.hibernate.converter.HibernatePersistentSortedMapConverter;
import com.thoughtworks.xstream.hibernate.converter.HibernatePersistentSortedSetConverter;
import com.thoughtworks.xstream.hibernate.converter.HibernateProxyConverter;
import com.thoughtworks.xstream.hibernate.mapper.HibernateMapper;
import com.thoughtworks.xstream.mapper.MapperWrapper;

public class MeuXStream {

    public static XStream xs;

    static {

        xs = new XStream() {
	    @Override
	    protected MapperWrapper wrapMapper(final MapperWrapper next) {
	        return new HibernateMapper(next);
	    }
        };

        xs.registerConverter(new HibernateProxyConverter());
        xs.registerConverter(new HibernatePersistentCollectionConverter(xs.getMapper()));
        xs.registerConverter(new HibernatePersistentMapConverter(xs.getMapper()));
        xs.registerConverter(new HibernatePersistentSortedMapConverter(xs.getMapper()));
        xs.registerConverter(new HibernatePersistentSortedSetConverter(xs.getMapper()));

        xs.alias("Bag", PersistentBag.class);
        xs.alias("Map", PersistentMap.class);
        xs.alias("List", PersistentList.class);
        xs.alias("Set", PersistentSet.class);

        xs.autodetectAnnotations(true);
    }
    ...
}

A maior parte das linhas acimas é para tratar a conversão de coleções para XML. Suas Classes de Entidades podem conter coleções, e estas vão estar com os tipos abstratos das Collections (Ex: Set, List, Map). Em tempo de execução, o Hibernate instanciará essas propriedades com suas próprias implementações dessas Collections (ex: org.hibernate.collection.PersistentList para o caso de List). No entanto você não vai querer que no seu XML final conste detalhes dessas coleções específicas do hibernate. Para isso, defina o HibernateMapper como o MapperWrapper do seu XStream. Além disso, registre os conversores do Hibernate fornecidos pela biblioteca xstream-hibernate. Por fim, é interessante definir alias para as 4 coleções do hibernate, pois, mesmo depois de convertidas, o XML final utiliza no atributo class o nome da classe do Hibernate, o que é irrelevante, mas fica poluído e verboso.

Caso você tenha mapeado alguma herança, certifique-se também de colocar a anotação @Proxy com lazy=false na super-classe. Isso evita que o Hibernate utilize Proxies nas instâncias dessa classe, o que leva ao XStream a serializar essas Proxies como se fossem instancias apenas da super-classe, gerando conflitos quando ele esbarra com uma propriedade da sub-classe e causando erros de de conversão (ConversionException). Lembrando que mesmo que você coloque essa anotação, isso não impedirá que carregamento das propriedades do tipo anotado seja Lazy, pois ainda há a configuração de cada propriedade nas próprias entidades (FetchType), apenas evitará a confusão com Proxies da super-classe.

	
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@Proxy(lazy = false)
public class MinhaSuperClasse implements Serializable {
    ...
}

Provavelmente você não desejará representar toda sua cadeia de classes no XML, podando alguns pontos. Para isso, anote as propriedades que não desejar serializar com a anotação @XStreamOmitField. Para que essas anotações sejam reconhecidas, foi preciso aquele ultimo comando do bloco estático de inicialização do XStream.

Por fim, para serializar, o mais importante é carregar a sua entidade logo antes de transformar em XML, pois dessa forma, com a sessão ainda aberta, o próprio XStream irá percorrer todos os getters das propriedades, forçando o carregamento de tudo que foi configurado com a abordagem Lazy.

	
...
MinhaEntidade entidade = minhaEntidadeDAO.carregarPeloId(id);
String xml = xs.toXML(entidade)
...

Para deserializar, obtenha de volta o mesmo XStream usado na serialização e faça:

	
...
String xml = lerXmlDeAlgumLugar();
MinhaEntidade entidade = (MinhaEntidade) xs.fromXML(xml);
...

Atente para a limitação de que o XML gerado em um momento só poderá ser deserializado em um outro momento se a mesma estrutura de classes e propriedades se mantiver. Qualquer mudança de nome de classe ou de propriedade, ou mesmo alteração de pacote, resultará em um erro de conversão. Para contornar esse problema pode-se definir alias para todas as propriedades e classes com as anotações @XStreamAlias e @XStreamAliasType, ou fazendo chamadas à xs.alias(…) ou xs.aliasType(…). Pode-se ainda criar Converters customizados para definir como serializar/deserializar uma classe específica, ou no caso de um comportamento geral para todas a classes, o ideal é escrever um Mapper customizado. Detalhes sobre esse processo de customização você encontra nessa ajuda do XStream.

10 anos com Java. Relembrando…

Hoje me dei conta que há exatos 10 anos atrás, em agosto de 2004, tive meu primeiro contato real com o Java. Apesar de nesses 10 anos não ter trabalhado apenas com Java, ela foi meu principal “ganha-pão”. Nesse post faço uma volta ao passado para relembrar como eram as coisas nessa época.

Começei a programar muito novo, ainda criança, brincando de mover a tartaruguinha do Micromundos com Linguagem Logo. Na pré-adolescência e início da adolescência brincava muito com Basic e Pascal, tudo console, e mais pra frente Visual Basic, Delphi já no Win 32. E foi no ensino-médio técnico que a coisa ficou séria, quando aprendí C/C++ e o bom e velho PHP com pacotão Web (HTML+CSS+JavaScript), garantindo meus primeiros freelances. Mas foi só em agosto de 2004, quando inciei a graduação em Ciência da Computação, que me enfiaram o Java goela abaixo pela primeira vez. Era uma recomendação da SBC na época. As universidades todas seguiam tal tendência, já que era uma plataforma livre e o mercado de Java na época estava quente.

E assim, durante a graduação usei Java para tudo: construir aplicativos console, desktop, web e mobile; exercitar o conceito de cliente/servidor e técnicas de arquitetura paralela e distribuída; estudar algoritmos, estruturas de dados, grafos, orientação por objeto e quase todas os assuntos que envolviam programação no curso. E apesar de diversos trabalhos enquanto estudante, foi só em 2007 que tive meu primeiro contato realmente profissional com Java em uma empresa de desenvolvimento.

Em 2004 Java era um terreno ainda em exploração. Estávamos na JDK 1.4, mas muita gente ainda usava a 1.3. Ainda falava-se muito em Java applets como uma grande vantagem, mas ele já havia definitivamente perdido espaço para o Flash. De fato muito sites corajosos (não eram só os sites de banco) usavam applets para alguns propósitos, principalmente interativos. Java Desktop também era muito bem visto, mas ainda havia muita referência baseada em AWT ao invés do SWING. Java FX nem sonhava em existir! E como essa história de levar tudo para a Web ainda estava acontecendo (e o Java EE teve papel importante no patrocínio dessa transição), teve muito manolo que optou por fazer sistemas desktop cliente/servidor enormes, com muito RMI (o RCP do Java), ao invés de partir para o uso de EJBs. Aliás, EJB foi um recurso arquitetural nobre que o Java incentivou desde muito cedo e que até hoje é sub-utilizado, dando lugar para soluções com desenhos vergonhosamente pouco sofisticados.

Lembro-me também da hype sobre Java ME e das promessas sobre o Mobile, muito antes dessa realidade dos Smartphones atuais. Os professores e evangelizadores da tecnologia falavam muito sobre a questão do Java ser multi-plataforma e de poder ser usado em aplicações embarcadas de hardwares específicos. Mas acho que pouca gente teve contato com essa parte. Quem carregou o Java pra frente no Mobile foi a Nokia em sua época de ouro. Suporte ao Java era uma feature a ser observada na compra de um aparelho novo. Cheguei a ver inúmeras vagas de emprego de empresas que faziam aplicativos para celular, e tenho ainda conhecimento de alguns colegas que participaram de soluções de software corporativas baseada em outros Handhelds que usavam Java, como os Palm.

Pra fechar, destaco o Java EE, que julgo ter sido a perna do Java que mais cresceu. Em 2004 quem soubesse Servlet e JSP básico já era disputado pelo mercado. O conceito de MVC ou o simples conceito de divisão em camadas despontava como tendência, e quem diria que iria durar tanto tempo. Mas pouco se falava de JSF e muito menos de Spring. O que alguns usavam era o Struts para ajudar com o vai-e-vem dos servlets. A efervescência dos frameworks ainda iria começar: muitos surgiram e muitos desapareceram. E alguns viraram especificação! Algumas soluções de ORM existiam, e o Hibernate ainda não era unanimidade (muito menos existia JPA). Lembro de dar manutenção em um projeto que usava Apache IBatis p/ ORM (nunca mais ouví falar). Aliás, unanimidade mesmo era que precisávamos de muito XML pra fazer qualquer coisa em projetos web. Annotations só vieram na JDK 1.5…

Fica a saudade dessa época onde o Java era uma terra de oportunidades e uma grande novidade. O legado deixado é enorme, o que me leva a crer que vamos demorar a deixar de escutar o nome “Java” por aí (seria o novo Mainframe?). Há ainda quem diga que se tornou uma ferramenta implacável no cinto-de-utilidades do programador, ao lado do C/C++ por exemplo (todos anos a fio no Top 5 do Ranking TIOBE). Por fazer parte da minha história, torço para que a plataforma continue se adequando aos novos ventos e gerando oportunidades!

Compartilhando recursos estáticos entre webapps JEE6 através de um JAR

Na API Servlet 3.0 é possível colocar recursos estáticos dentro de um JAR para serem acessados pelos webapps que dependem desse JAR. Para tanto, basta empacotar os recursos desejados na pasta META-INF/resources do JAR. Os arquivos aí colocados estarão disponíveis para os WAR como se estivessem na raíz do webapp. Ex:

O arquivo:

MeuWebApp.war/WEB-INF/lib/WebCommons.jar/META-INF/resources/public/img/logo.png

Estará disponível no servidor como:

http://localhost:8080/MeuWebApp/public/img/logo.png

No caso de um projeto Maven, essa pasta pode ser contemplada no código fonte em [PASTA DO PROJETO]/src/main/resources/META-INF/resources

Isso é bastante útil para compartilhar html, xhtml, jsp, css, js, imagens, arquivos de properties, dentre outros, entre diferentes WAR. Para obter o mesmo resultado nas versões anteriores da API era necessário bolar algum mecanismo que acessasse recursos programaticamente via ServletContext, ou mesmo utilizando bibliotecas de terceiros específicas para esse propósito, como é o caso da Weblets.

No caso de se tratar de um projeto JSF, pode ser desejado compartilhar entre os WAR não somente recursos estáticos como também ManagedBeans, Converters e outros beans gerenciados pelo framework. Para isso, crie no JAR também o arquivo META-INF/faces-config.xml.

Forçando o uso de Hibernate 3.x com JPA no Jboss AS 7 / EAP 6

O Jboss AS 7 já provê a biblioteca do Hibernate 4 para os projetos, não sendo necessário incluí-la na lib de seu WAR. No entanto, pode acontecer de você querer usar uma versão mais antiga do Hibernate, como por exemplo a versão 3. Além de incluí-la como lib, você precisa avisar para o Jboss para deixar de prover o Hibernate dele, no caso a versão 4. Para tanto, alguns passos sesão necessários.

Você precisa adicionar ao WAR final o arquivo META-INF/jboss-deploy-structure.xml com o segunte conteúdo:

<jboss-deployment-structure>
	<deployment>
		<exclusions>
			<module name="org.hibernate" />
		</exclusions>		
	</deployment>
</jboss-deployment-structure>

Você também vai precisar adicionar uma propriedade a mais na sua Persistence Unit declarada no persistence.xml:

...
	<property name="jboss.as.jpa.providerModule" value="hibernate3-bundled" />
...

E por fim, certifique-se de fornecer o JAR do hibernate ao compilar o pacote final. Para quem usa Maven, basta adicionar o seguinte:

<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-entitymanager</artifactId>
	<version>3.6.10-Final</version>
</dependency>

Não se esqueça de verificar a árvore de dependências do Maven para saber se alguma dependência indireta está requerendo alguma biblioteca específica do Hiberante também na versão 4. Se houver, o downgrade pode não ser possível ou pelo menos será não-trivial. Boa sorte!

Abandonando o antigo formulário j_security_check através de JSF 2 e Servlet 3

Muitas são as aplicações web em Java que utilizam o Serviço de Autenticação e Autorização do Java (JAAS) para fazer a segurança. E um elemento famoso no JAAS é o JSP/HTML de login que possui um formulário mágico que faz um POST para a URL “j_security_check“, enviando o password e a senha através dos inputs de nome “j_password” e “j_username” respectivamente. Muitos torcem o nariz para esse tipo de coisa, já que a plataforma está de certa forma engessando a tela de login.

As vantagens de querer fazer um formulário de login JSF 2 são muitas! Primeiramente você pode querer herdar o layout de um template via Facelets. Você pode também querer utilizar componentes ricos de bibliotecas como PrimeFaces ao invés de simples inputs HTML padrão. O JSF também dá um bom suporte para validação de campos e mensagens de erro/sucesso. Mas dadas as restrições impostas pelo j_security_check e seus amigos, como unir os dois mundos então?

Com a chegada do JEE 6 e a nova Servlet API 3.0, foi previsto um método bem óbvio na interface HttpServletRequest chamado request.login(String username, String password). O resto você provavelmente já imagina: pode-se acionar o JAAS programaticamente dentro de um ManagedBean do JSF por exemplo, unindo o melhor dos dois mundos.

A idéia então é fazer um XHTML de login e fazer um ManagedBean que cuide do comportamento dessa tela. Veja como fica a configuração de segurança do web.xml:

<?xml version="1.0"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" 
        xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
           http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	version="3.0">

	...
	
	<security-constraint>
		<web-resource-collection>
			<web-resource-name>private</web-resource-name>
			<description>private resources</description>
			<url-pattern>/private/*</url-pattern>
		</web-resource-collection>
		<auth-constraint>
			<role-name>*</role-name>
		</auth-constraint>
	</security-constraint>
	
	<login-config>
		<auth-method>FORM</auth-method>	
		<form-login-config>
			<form-login-page>/public/pages/security/login.jsf</form-login-page>
			<form-error-page>/public/pages/security/login.jsf</form-error-page>
		</form-login-config>
	</login-config>
	
	<security-role>
		<role-name>*</role-name>
	</security-role>

</web-app>

Perceba que no web.xml a página de login é tanto o formulário quanto a página de erro, ou seja, em caso de login inválido a aplicação retornará para a página de login, permitindo continuar interagindo com o formulário para exibir mensagens por exemplo. A página /public/pages/security/login.xhtml pode ser algo assim (observe que ela herda um template via Facelets):

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
	xmlns:p="http://primefaces.org/ui"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	template="/WEB-INF/template/main.xhtml">
	<ui:define name="body">
		<p:messages id="messages" globalOnly="true" />
		<h:form id="form">			
			<h:panelGrid columns="3">
				<p:outputLabel value="Usuário" for="inputUsuario" />
				<p:inputText id="inputUsuario" required="true"
					value="#{loginMB.usuario}" />
				<p:message for="inputUsuario" />

				<p:outputLabel value="Senha" for="inputSenha" />
				<p:password id="inputSenha" required="true"
					value="#{loginMB.senha}" />
				<p:message for="inputSenha" />
			</h:panelGrid>

			<p:commandButton id="btEntrar" value="Entrar" ajax="false"
				action="#{loginMB.onClickLogar()}"
				icon="ui-icon-check" />
		</h:form>
	</ui:define>
</ui:composition>

E um exemplo de ManagedBean para tal página pode ser (os imports estão omitidos):

...

@ManagedBean
@ViewScoped
public class LoginMB {

    private static final String PAGINA_INDEX = "/private/pages/index.xhtml";

    private String usuario;
    private String senha;

    public String onClickLogar() {
        try {
	    HttpServletRequest request = (HttpServletRequest) FacesContext.
                getCurrentInstance().getExternalContext().getRequest();
            request.login(this.usuario, this.senha);
            return PAGINA_INDEX;
        } catch (ServletException e) {
	    //se houver erro no Login Module vai cair aqui... 
            //Você pode fazer log por exemplo!
        } finally {
            //tratar aqui mensagens de segurança que possam ter vindo 
            //do Login Module exibindo-as na forma de FacesMessage
	}

        return null;
    }

    public String getUsuario() {
        return usuario;
    }

    public void setUsuario(String usuario) {
        this.usuario = usuario;
    }

    public String getSenha() {
        return senha;
    }

    public void setSenha(String senha) {
        this.senha = senha;
    }
}

Não se esqueça de que para esse recurso de login programático estar disponível, sua aplicação deve adicionar a dependência provida a API do Servlet 3.0 no pom.xml:

<dependency>
	<groupId>javax</groupId>
	<artifactId>javaee-web-api</artifactId>
	<version>6.0</version>
	<scope>provided</scope>
</dependency>            

Vale ressaltar que o exemplo do post cobre apenas o essencial. Para atingir a excelência num esquema de login JAAS é preciso cuidar de vários outros aspectos. Um deles é definir um bom mecanismo de troca de mensagens do Login Module para com a Aplicação. Uma opção é adicionar a mensagem como atributo no Request, podendo capturá-la no ManagedBean e tranformá-la em uma FacesMessage. Outra questão que deve ser observada é o comportamento da aplicação quando a página de login é acessada por um usuário já logado, podendo redirecioná-lo ou retirá-lo da sessão, o que pode ser feito via JSF adicionando no seu XHTML de login um event listener na fase de Pre-Render View e implementando um listener correspondente:

...
   <f:event listener="#{loginMB.verificaSeUsuarioJaLogado}" type="preRenderView" />
...

Tenho certeza que partindo dessa solução base você poderá buscar várias sofisticações, fugindo do tradicional JSP de login para o JAAS.

Resolvendo o problema de ‘is locally modified’ no plugin SCM do Maven com CVS

Enfrentei problemas ao configurar um build automatizado no Continuum via Maven 2.2.1 onde um dos goals era a execução do plugin SCM para aplicar uma tag (scm:tag) no CVS (acredite, ainda usam CVS por aí). No momento de passar a tag o CVS alegava para o Maven que alguns arquivos estavam localmente modificados (‘… is locally modified’), e a Build era terminada com falha.

No meu caso, os tais arquivos modificados eram arquivos .jasper gerados pela execução anterior de outro plugin, o do Jasper Reports, que compilava arquivos jrxml e os colocava em pastas do source ao invés de afetar apenas o pacote final na pasta target.

Para resolver, configurei uma exclusão de arquivos .jasper no plugin SCM da seguinte forma:

<plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-scm-plugin</artifactId>
   <version>1.5</version>
   <configuration>
      <exludes>**/*.jasper</excludes>
   </configuration>
</plugin>

Review do Livro “Aplicações Java para Web com JSF e JPA” de Gilliard Cordeiro

Em 2012 assumí a disciplina de Tópicos Especiais em Análise e Desenvolvimento de Sistemas na faculdade em que leciono. Por se tratar de uma disciplina do último período do curso, encarei ela como uma “última chance” de resgatar os alunos, ao invés de simplesmente abordar assuntos complexos diversos como é de costume em disciplinas de Tópicos Especiais. Queria deixar algo de legal para os alunos levarem para o mercado de trabalho logo em seguida, já agregando ao currículo. Como o curso escolheu Java e eles já tinham visto algo de Desenvolvimento Web em JAVA em semestres anteriores, decidi abordar os frameworks JSF 2 e JPA 2, muito requisitados pelo mercado. No entanto, para tocar o curso precisaria de literatura em português para me apoiar, tendo em vista que não poderia exigir o inglês dos alunos (faz parte…).

Qual não foi minha Surpresa de descobrir duas grandes coisas: a editora Casa do Código, que como ela mesmo se entitula publica “livros de programadores para programadores”; e dentre os seus títulos descobri também uma grande preciosidade, o livro “Aplicações Java para Web com JSF e JPA” de Gilliard Cordeiro. O que me chamou a atenção foi: o livro aborda duas tecnologias que geralmente andas juntas; é em português, melhor, foi escrito por um brasileiro (já traduções nem sempre carregam o estilo do escritor original); o preço é convidativo, inclusive dando opção de comprar apenas a versão digital por uma pechincha; o tamanho do livro é razoável, considero pequeno, 275 páginas, exigindo apenas alguns minutos por dia para terminar de ler o livro em poucas semanas (essencial para indicar para alunos!)

Adquirí um exemplar e lí. Gostei bastante da escolha didática do livro que faz questão de elucidar como as coisas eram antes do JSF e do JPA e o motivo de cada dessas tecnologias terem entrado no jogo. Essa visão ajuda o leitor a compreender os frameworks não apenas como uma caixa preta a ser dominado mas sim como algo fruto de um design que evoluiu junto com a comunidade de desenvolvimento em torno do Java e de suas tecnologias. Destaco a introdução onde diagramas demonstram a evolução do código de um Servlet puro sem separação nenhuma de lógica de apresentação, negócio e persistência até atingir padrões mais atuais de design, justificando muita coisa repetida sem explicação por muitas equipes.

O livro não se propõe a ser um guia definitivo de JSF e JPA, e assim sendo, não entra em muitos detalhes tal como uma bíblia. Mas sua leitura provê o necessário para desenvolver uma aplicação web completa envolvendo aspectos diversos além do básico tais como segurança e performance. Considero uma excelente leitura para quem já programa em Java e desconhece os dois frameworks ou mesmo quem já programa em JSF mas ainda se sente perdido com conceitos fundamentais tais como o ciclo de uma requisição no JSF, o uso dos componentes básicos JSF, o uso de Ajax com o JSF. Já na parte de JPA, destaco as discussões diversas sobre performance seja ao abordar carregamento preguiçoso (lazy), mapeamento ou o problema das N+1 queries, o que pode embasar até mesmo desenvolvedores experientes na tomada de decisões em seus projetos.

Em resumo, gostei bastante da abordagem livro. Mais tarde também adquirí exemplares de outros títulos relacionados com Java da Casa do Código, dentre eles: Design Patterns, JSF Eficaz, Test-Driven Development, e o recém lançado CDI. Todos são livros relativamente pequenos mas com conteúdo denso, destinados realmente para programadores. Fica a dica!