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.

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.

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!

Resolvendo conversão automática para ZERO no JSF 2 com Jboss 7 AS / EAP 6

Tive um problema com JSF 2.0 onde se um campo mapeado para uma propriedade numérica não era preênchido, ao submeter o formulário ele era interpretado como zero. Isso me trouxe transtornos pois o campo era obrigatório, e zero era um valor válido.

Ainda que no meu caso específico fosse possível resolver definindo o campo como required ou mesmo criando um Validator que verificasse seu preenchimento, não fiquei satisfeito com a conversão automática feita pelo JSF, pois ela poderia afetar outros pontos do sistema

Descobri que no Jboss 7 AS / 6 EAP, que utiliza a implementação Mojarra do JSF e que também utiliza internamente o Tomcat como web container, é possível definir uma propriedade de sistema que desabilita essa conversão automática para zero. No entanto, como não teria sempre acesso aos ambientes onde a aplicação iria rodar, quis uma maneira programática de definir essa propriedade. Para tanto, adicionei a linha de código abaixo ServletListener da minha aplicação web. Veja:

@WebListener
public class MeuWebAppListener implements ServletContextListener
    ...
    @Override
    public void contextInitialized(ServletContextEvent event) {
        ...
        System.setProperty("org.apache.el.parser.COERCE_TO_ZERO", "false");
    }
    ...
}

Suprindo a falta do @KeepAlive no JSF 2

Para quem programa em JSF 1.2 com RichFaces + A4j, um recurso muito popular é o @KeepAlive. Anotando um ManagedBean dessa forma ele fica com um escopo maior que o de Request e menor que o de Sessão, sobrevivendo na memória enquando o usuário navegar entre páginas que façam uso de um mesmo ManagedBean. Exemplos clássicos: 1. Páginas de listagem e edição; 2. Formulários preênchidos em etapas.

Ao pisar no terreno do JSF 2.0 essa tag deixa de existir. Ainda que existisse, você pode não querer usar o RichFaces e escolher outra biblioteca, como por exemplo a PrimeFaces. A ausência do @KeepAlive no JSF 2.0 parece não ser vista como algo grave já que existem novas possibilidades de controle de escopo tais como as anotações próprio JSF e a as anotações do CDI. O JSF 2.0 fornece por exemplo a @ViewScoped, usada para páginas recheadas de Ajax, mantendo o ManagedBean vivo enquanto existem requests para a mesma página, ou seja, quando não há navegação (métodos acionados devem ser void ou retornar String null). Já o CDI fornece o @ConversationScoped que permite dizer quando a “conversação” com o ManagedBean inicia e termina de forma programática.

Para quem ainda sente falta do simples e preciso @KeepAlive, uma solução é recorrer ao pacote CDI Extensions (CODI) do projeto Apache MyFaces, que fornece a notação @ViewAcessScoped que nos dá exatamente o comportamente desejado. Para instalá-lo via Maven, adicione as seguintes dependências ao projeto:

<dependency>
	<groupId>org.apache.myfaces.extensions.cdi.modules</groupId>
	<artifactId>myfaces-extcdi-jsf20-module-api</artifactId>
	<version>1.0.3</version>
	<scope>compile</scope>
</dependency>

<dependency>
	<groupId>org.apache.myfaces.extensions.cdi.modules</groupId>
	<artifactId>myfaces-extcdi-jsf20-module-impl</artifactId>
	<version>1.0.3</version>
	<scope>compile</scope>
</dependency>

<dependency>
	<groupId>org.apache.myfaces.extensions.cdi.core</groupId>
	<artifactId>myfaces-extcdi-core-api</artifactId>
	<version>1.0.3</version>
	<scope>compile</scope>
</dependency>

<dependency>
	<groupId>org.apache.myfaces.extensions.cdi.core</groupId>
	<artifactId>myfaces-extcdi-core-impl</artifactId>
	<version>1.0.3</version>
	<scope>compile</scope>
</dependency>

Para usar a anotação no seu ManagedBean, faça como segue:

import br.gov.frameworkdemoiselle.stereotype.ViewController;

@ViewAccessScoped
public class MeuMB {
    ...
}

JSF 1.2: Forçando a recriação de um Managed Bean

A configuração do escopo de ManagedBeans no JSF 1.2 é um tópico que gera bastante confusão. Além de poder definir se o ManagedBean vai ter um ciclo de vida atrelado do Request, Sessão ou Aplicação, existem outros modificadores como o @KeepAlive do RichFaces.

O fato é que essa farofa de escopos misturados com recursos de outras bibliotecas trazem algumas situações indesejadas. Seja em sistemas legados ou seja quando estamos em equipes muito heterogêneas, onde não dá pra garantir um bom planejamento do escopo correto de um MB, pode acontecer de você desenvolver uma solução considerando que o ManagedBean vai ser criado e inicializado ao acessar uma determianda tela, mas ao executar a aplicação, descobre erros que advém do fato do ManagedBean estar sobrevivendo na memória, sendo difícil perceber seu real ciclo de vida. Nesse instante, pode surgir a necessidade da recriação de um Managed Bean, evitando vários problemas.

A dica é implementar um método que será acionado por um commandButton ou commandLink da sua aplicação e que, ao invés de navegar diretamente para uma tela, obterá o ManagedBean via EL (no exemplo abaixo a classe DestinoMB), criará ele novamente, e em seguida chamará a tela desejada:

public class OrigemMB(){
	...
	public String onClickMeuBotao(){				
		FacesContext context = FacesContext.getCurrentInstance();
		ELContext elContext = context.getELContext();
		String elMB = "#{destinoMB}"; // Você deve conhecer a EL do seu MB
		
		ValueExpression expression = context.getApplication().getExpressionFactory()
			.createValueExpression(elContext, elMB, DestinoMB.class);
		expression.setValue(elContext, null);

		//Se necessário é possível ainda obter o MB para incializações
		DestinoMB destinoMB = (DestinoMB) context.getApplication().getExpressionFactory()
			.createValueExpression(elContext, elMB, DestinoMB.class).getValue(elContext);
                destinoMB.inicializar();
			
		return "outcomeDaTelaDesejada";
	}
	...
}