Um pouco de gambi não faz mal a ninguém: Spring em ambientes não gerenciados

Outro dia, na Caelum, passamos por uma situação interessante. Dois colegas de trabalho, Lucas e Marcio, estavam programando no projeto de final de curso deles e tinha a seguinte situação: o projeto é todo baseado no Spring, mas mesmo assim eles decidiram realizar o deploy em um servidor de aplicação, no caso o Wildfly. É uma decisão interessante, ao invés de optar por um caminho ou outro, eles juntaram as forças das duas plataformas. Vai ser o tema do próximo post deste blog :).

E a decisão valeu a pena! Eles precisavam lidar com WebSockets e a especificação no JAVA EE 7 deixa muito simples implementar qualquer funcionalidade baseada  nesse protocolo. O problema é que agora eles tem uma classe que é gerenciada pelo servidor de aplicação, mas precisam acessar objetos que estão gerenciados pelo Spring. O que fazer? Os container não se falam e, nessa situação, só nos restou partir para um workaround(gambi).  Dê uma olhada no esboço da classe deles:

	@ServerEndpoint("/channel/sales")
	public class AgileBoardEndpoint {
		
		@Inject
		private ConnectedUsers connectedUsers;
		//dao gerenciado pelo Spring
		@AutoWired
		private TaskDao tasks;
		
		@OnOpen
		public void onNewUser(Session session){
			connectedUsers.add(session);
		}
		
		@OnMessage
		public void onMessage(String message) {
			tasks.save(new Task(message));
			connectedUser.alert(task);
		}
	}

Não adianta eles anotarem nenhum atributo com AutoWired, já que, como dissemos, o objeto desta classe não vai ser instanciado pelo Spring. Para resolver tal situação é necessário que você acesse o objeto que representa uma parte do container do Spring de maneira programática.

	@ServerEndpoint("/channel/sales")
	public class AgileBoardEndpoint {
		
		@Inject
		private ConnectedUsers connectedUsers;
		
		@OnOpen
		public void onNewUser(Session session){
			connectedUsers.add(session);
		}
		
		@OnMessage
		public void onMessage(String message) {
                        ApplicationContext ctx = ApplicationContextHolder.getInstance();
			TaskDao tasks = ctx.getBean(TaskDao.class);
			tasks.save(new Task(message));
			connectedUser.alert(task);
		}
	}

A classe ApplicationContext possui o método getBean, que te permite fazer o lookup de um objeto pela sua classe. A instância retornada foi criada exatamente da mesma forma que seria se você tivesse pedido a injeção pelo atributo usando o AutoWired. A pergunta que fica é: como vamos obter uma instância do objeto do tipo ApplicationContext?  É justamente que entra nossa solução de contorno(workaround).

	@Component
	public class ApplicationContextHolder implements ApplicationContextAware{
		
		private static ApplicationContext instance;

		@Override
		public void setApplicationContext(ApplicationContext applicationContext)
				throws BeansException {
			ApplicationContextHolder.instance = applicationContext;
		}

		public static ApplicationContext getInstance(){
	        return instance;
		}


	}

Implementamos a interface ApplicationContextAware quando precisamos ser notificados da criação do ApplicationContext. É parecido com um desses listeners que implementamos quando queremos ser notificados, por exemplo, sobre o inicio do nosso servlet container. Como, em geral, todas as classes ficam gerenciadas sob o mesmo contexto, podemos guardar a instância passada em um atributo estático.

O benefício dessa solução é que conseguimos acessar o contexto do Spring de qualquer lugar, foi justamente o que fizemos a partir da classe que lidava com os WebSockets do JAVA EE. É um bom truque para integrar duas pontas que não se falam muito. O malefício, é exatamente o mesmo :). Você pode acessar de qualquer lugar, tem que tomar cuidado para não sair usando este poder onde não é necessário e acabar prejudicando a manutenção do código. Outro ponto ruim é que você acabou de deixar seu código mais difícil de testar.

Este tipo de decisão acontece todos os dias enquanto estamos desenvolvendo. O importante é saber o que ganhamos e perdemos, porque assim vamos por um caminho baseado em um julgamento crítico, sabendo exatamente as consequências.  No mundo dos sonhos, o Spring vai implementar a especificação do CDI e vai ficar tudo bem para a gente. Lembre, saber tirar proveito da tecnologia que você usa, faz muita diferença no produto final que será entregue.

Spring Security, Spring JPA e o JAVAEE

Quando pensamos em adicionar a parte de segurança dentro da nossa aplicação, a primeira solução que vem a nossa cabeça é usar o Spring Security. Por sinal é um pensamento que faz todo sentido, já que hoje em dia ele é o projeto de segurança mais completo do mercado. Só que quando estamos no mundo JAVAEE, já existe uma especificação responsável por essa área, a JAAS. Colocando as duas lado a lado, não é complexo perceber que o Spring Security é uma solução bem mais moderna, com uma arquitetura mais bem bolada e com pontos de extensões mais definidos. Além disso é muito mais sincronizado com a realidade da maioria dos projetos. Queremos fazer segurança baseada em roles urls e ter uma maneira fácil de fazer essa associação é um grande diferencial. Este post é para você, que está numa aplicação JAVAEE, mas tem o desejo de usar o Spring Security como framework de segurança. Vou assumir que o leitor já sabe o básico do framework.

A configuração inicial não muda muito, supondo que você usa Maven, é necessário adicionar as novas dependências no pom.xml. 

		<!-- spring -->

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
			<version>4.0.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>4.0.1.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>4.1.0.RELEASE</version>
		</dependency>

O primeiro ponto diferente é em relação a configuração do filtro. Normalmente basta que você declare uma classe que herde AbstractSecurityWebApplicationInitializer e pronto. Só que como não estamos usando nada além do Spring, precisamos mudar um pouco código e passar todas as nossas classes de configuração que devem ser carregadas.

   public class SpringSecurityFilterConfiguration extends
		AbstractSecurityWebApplicationInitializer {

	public SpringSecurityFilterConfiguration() {
		super(SecurityConfiguration.class,SystemUserDAO.class,JPAConfiguration.class);
	}
   }

Geralmente você não precisa fazer isso, porque este passo já foi realizado na classe de configuração do Spring MVC. A classe SecurityConfiguration não tem nada demais, você pode acessá-la seguindo o link. Ela basicamente define as regras de segurança, da mesma forma que seria feito em um projeto usando unicamente a stack do Spring. A classe SystemUserDAO também não tem nada demais, ela implementa a interface UserDetailsService e precisa de um EntityManager injetado. E agora sim, temos uma situação. Qual EntityManager deve ser injetado? Lembre que estamos dentro de um servidor de aplicação e o melhor que usemos o contexto de persistência provido pelo container. Para a nossa sorte, o Spring é bastante extensível e não tem a menor pretensão de ser apenas usado isolado de todo mundo.

Como já temos um persistence.xml configurado, integrado com a JTA e utilizando o DataSource configurado no servidor, precisamos apenas ensinar ao Spring que queremos tirar proveito de todas essa configuração. Para isso vamos dar uma olhada na classe JPAConfiguration.

	package br.com.casadocodigo.security;

	//imports

	import org.springframework.context.annotation.Bean;

	public class JPAConfiguration {

		@Bean
		public EntityManagerFactory emf() throws NamingException {
			Context ctx = new InitialContext();
			EntityManagerFactory lookup = (EntityManagerFactory) ctx
					.lookup("java:comp/env/persistence/casadocodigo-emf");
			return lookup;
		}
	}

Simplesmente buscamos a referência para a EntityManagerFactory a partir da JNDI. Uma especificação muito usada no mundo Java, onde conseguimos associar nomes a objetos que podem ser recuperados no servidor. Uma pergunta que pode estar na sua mente é: de onde veio esse nome? Para isso vamos usar outro recurso da especificação JAVAEE, nesse caso a de Servlets. Conseguimos expor a EntityManagerFactory na JNDI, a partir do nosso arquivo web.xml.

	<persistence-unit-ref>
		<persistence-unit-ref-name>persistence/casadocodigo-emf</persistence-unit-ref-name>
		<persistence-unit-name>casadocodigo-persistence-unit</persistence-unit-name>
	</persistence-unit-ref>

A tag persistence-unit-ref-name define o nome que você quer expor na JNDI. Já a tag persistence-unit-name faz referência ao nome configurado no arquivo web.xml.

Pronto! Dessa forma conseguimos adicionar o Spring Security por exemplo, em um projeto usando JSF dentro de um servidor JAVAEE. Aproveitamos as configurações da JPA já feitas pela aplicação e simplesmente escrevemos a ponte para o Spring. Uma outra pergunta que pode ficar na cabeça é: o servidor JAVAEE já tem o CDI, só que o Spring faz a  mesma coisa. Não tem problema algum, não estamos querendo colocar o Spring para ser o controlador da Injeção de Dependências do sistema, estamos apenas usando o necessário para o Spring Security funcionar. Outra detalhe bem interessante é que a parte principal do Spring Security funciona dentro de um Filterpor isso conseguimos usar ele para fazer a segurança de qualquer aplicação web.

O Spring foi pensado justamente para ser extensível. É claro que o melhor dos mundos é usar os módulos do próprio Spring e ser feliz, mas isso nem sempre é possível. Também não fique preso ao estigma de usar Spring ou JAVAEE, use o que se encaixe melhor na sua aplicação. Encare tudo como ferramenta, nada de nutrir amor por tecnologia :).