Será que o Struts 1 estava certo?

Só para você não estranhar. O blog é sobre Spring e os exemplos usados durante o post são baseados no Spring MVC. Só que esse realmente é um post de um tema mais geral.

Há muito tempo atrás tínhamos o Struts1 e o seu jeito pouco automático de fazer as coisas. Recebíamos request response como argumentos e, além disso, recebíamos também um outro parâmetro que, se não me engano, era do tipo ActionForm. Esse bendito/maldito desse ActionForm fazia o povo agonizar. Você basicamente tinha que copiar os atributos da sua classe para lá e, se tudo chegasse como deveria, você criava seu objeto de domínio e seguia com sua vida. Ele até “evoluiu” e permitiu que você tivesse atributos do mesmo tipo do seu domínio, para minimizar a duplicidade.

    public class AutorForm extends ActionForm {
      private Autor autor = new Autor();

      public void setAutor(Autor autor) {
        this.autor = autor;
      }

      public Autor getAutor(){
        return this.autor;
      }
    }

E talvez essa tenha sido a pior contribuição do Struts para os frameworks web do mundo Java. Todo mundo começou a achar que os formulários da aplicação deveriam mapear para objetos de classes do seu domínio. Claro temos os casos felizes, como o que segue abaixo.

    public String novo(@Valid Autor autor,BindingResult bindingResult){
      if (bindingResult.hasErrors()) {
        return form(autor);
      }   

      autorDao.save(autor);
      return "redirect:/autores";
    }

Só que, o meu sentimento, é que na maioria das vezes o caso não é feliz, como esse aqui.

    public String novo(@Valid Livro livro,BindingResult bindingResult){
      if (bindingResult.hasErrors()) {
        return form(livro);
      }   

      autorDao.save(livro);
      return "redirect:/livros";
    }

Você pode até parar e pensar: calma ê, isso daí é um cadastro de um livro com autor, então faz sentido criar um objeto Livro  direto. Só que é isso mesmo que o framework faz para você? O objeto do tipo Livro que foi criado, está em perfeito estado para uso? Vamos ver.

    if(livro.getAutor().getEstado().equals(Estado.BLA)) {
      ...
    }

O resultado desse código é um belo de um NPE. O estado do autor não foi preenchido automaticamente e o programador, olhando apenas para essa parte do código, não tem como saber disso. Dessa situação começam a proliferar as verificações de null no código. Ah, então vou fazer com que o método retorne Optional! Só que o estado é obrigatório, essa solução não deveria passar num código review.

Os exemplos não param aqui. Agora temos uma tela onde é necessário atualizar apenas o email do autor. E o que acontece muito é o código acabar com esse método no controller.

    public String atualizaEmail(Autor autor,BindingResult bindingResult){
      if (bindingResult.hasErrors()) {
        return form(livro);
      }   

      ...
    }

E agora? Como você vai validar que o email não veio em branco? O @Valid vai tentar aplicar a validação no objeto inteiro e ele não está completamente preenchido. Você pode até buscar uma saída e ter perfis de validação na Bean Validation. Uma outra solução que eu já vi, é a de retirar as anotações de validação do modelo e criar métodos que fazem validações específicas. Perceba, uma simples tela afetou a definição do seu modelo!

Para finalizar, quero que você note que eu nem falei do fato de você ter um monte de setter e getter no seu domínio que você não necessariamente quer. E uma parte importante, eu também era meio que a favor de fazer a maioria das coisas que eu estou criticando no post. Para mim parecia um tradeoff ok. Deixou de ser quando eu percebi o quanto de problema básico a gente tinha nos sistemas.

  1. NPE por conta de objetos inconsistentes
  2. Validações falhando por conta de ficar preso ao lance de usar a classe de domínio
  3. Métodos claramente de view explodindo nos domínios. Você tenta buscar pelo método na sua IDE e um monte só é achado na view.
  4. Dificuldade em montar um fluxo de uma tela porque você fica dependente de já ter o modelo escrito. Aí, já que vai escrever o modelo, já mapeia para o ORM e por aí vai.
  5. Uso quase nulo do construtor, potencializando a criação de objetos que não possuem a consistência exigida pelo negócio.

Talvez existam até mais motivos, mas esses foram o que vieram direto na minha mente.

Já faz um bom tempo que não associo mais formulários diretamente com domínio. E eu agradeço o Struts1 por isso! Os caras eram visionários, de verdade. Podemos agradecer também ao livro DDD, mas prefiro agradecer ao Struts mesmo :P. Está definido na minha vida que a parte web da aplicação é algo separado do domínio e faço de tudo para que as coisas não se misturem. Pegando o primeiro exemplo que eu dei aqui, que já o caso feliz. Eu faria assim:

    public String novo(@Valid NovoAutorForm form,BindingResult bindingResult){
      if (bindingResult.hasErrors()) {
        return novoForm(form);
      }   

      Autor autor = form.toAutor();
      ...
    }

Agora pegando o exemplo do cadastro de autor.

    public String novo(@Valid NovoLivroForm form,BindingResult bindingResult){
      if (bindingResult.hasErrors()) {
        return novoForm(form);
      }   

      Livro livro = form.toLivro(autorDao);
      ...
    }

Para fechar, o exemplo da atualização do cadastro do email.

    public String atualizaEmail(@Valid AtualizaEmailAutorForm form,BindingResult bindingResult){
      if (bindingResult.hasErrors()) {
        return novoForm(form);
      }   

      Autor autor = autorDao.findById(form.getId());
      autor.setEmail(form.getEmail());

      ...
    }

Para todos os exemplos eu crio objetos que representam o formulário, até para o caso super feliz. Tive uma diminuição absurda em erros bobos na aplicação, como validações simples que falhavam e NPEs. As classes de domínio ficaram bem mais enxutas, apenas com métodos que realmente precisam ser acessados. Depois de um tempo usando, posso dizer que o lance da duplicidade é coisa pequena perto dos ganhos. E para ser sincero, não é bem uma duplicação. É apenas uma incrível coincidência e, muitas vezes, uma “forçada de coincidência” por parte do programador(a) .

As classes que representam forms, em geral, tem os atributos que devem ser mapeados do formulário html(id do autor é criado como um Integer ou Long) e também possuem um método que pegam as informações e criam os objetos em questão. E aí, por exemplo, já temos um belo de um uso para o construtor das classes de domínio. Outro ponto possivelmente ruim é essa passagem do DAO como argumento do método toYYY.  Eu considero má prática, mas acho pior ainda criar uma outra classe de conversão para isso. Entre duas ruins, fui com a que eu achava menos deprimente.

Claro que agora temos alguma duplicidade e precisamos também escrever mais código. Só que os ganhos tem sido tão bons, que estou bem ok com isso. Você agora tem uma classe super coesa em relação ao formulário em questão e, se precisar, pode criar quantos métodos quiser específicos para aquele cenário.

Pelo que eu vejo no mercado, o padrão é atrelar com o domínio mesmo. Eu não tenho feito mais assim e voltei para os velhos ActionForms :).  E você, como faz? Fique a vontade, deixe o seu comentário e enriqueça a discussão.

Advertisements

Entrevista com Arjen Poutsma, commiter do Spring!

Eu estava esperando ansiosamente para escrever este post, ele contém uma rápida entrevista com um dos principais commiters do Spring, Arjen! Ele respondeu algumas perguntas que fiz sobre o mundo que nos espera com a nova versão do framework, com o suporte integral ao conceito de Reactive Programming. Já vou deixar aqui a versão em inglês, depois posto a tradução.

Alberto: Just to introduce to our readers, could you talk a little bit about you and your role in the Spring Framework?

Arjen:  My name is Arjen Poutsma. I have been working on Spring for over ten years. I started out with Spring Web Services, then worked on Spring MVC – specifically the REST features, then Spring Scala, and for the last two years I have been working on Spring Reactive, together with Brian Clozel, Sebastien Deleuze, Stephane Maldini, abd Rossen Stoyanchev.

In this project, our team has been creating a new reactive foundation for Spring. We have essentially looked at the Spring MVC module, evaluated the code therein, and rewrote those pieces we need over to a non-blocking, event-driven style. We used Reactor as a foundation for this. The resulting code lives in the new spring-web-reactive module, next to the existing spring-webmvc module. The two modules share many concepts, but do not share any code. This is because Spring Web Reactive runs on a Reactive Streams HTTP adapter layer that’s fully non-blocking and reactive all the way down to the HTTP runtime. So while Spring MVC is built for and runs on Servlet containers, Spring Web Reactive runs also on non-Servlet runtimes such as Netty.

On top of the Spring Web Reactive module, we have two programming models. One is the existing annotation-based model, with @Controller, @RequestMapping and so on. These are the exact same annotations you use in Spring Web MVC. The other model is a recently introduced functional model, with RouterFunctions and HandlerFunctions.

I’ve attached a picture which hopefully makes this clearer.

architecture

Alberto: So, we are all excited about the new version of Spring, with the Reactive support. Could you tell us how this update will affect our way of programming?

Arjen: Just to be clear: upgrading to Spring 5 alone will not require any changes any changes to your code. As with previous major version upgrades, we have made sure that Spring 5 is backward compatible with Spring 4. So if you have a Spring 4 MVC application running on a Servlet engine, you will not have to change anything.

However, if you choose to use the new Reactive features, you will have to reconsider your architecture, as there any many differences. For instance, the guarantee that a HTTP request will be handled in a single thread is no longer the case: request will be handled by many threads, each doing a piece of the work when there is need for it (i.e. backpressure). Essentially, you are switching from a model a big thread pool, where most threads are waiting (either for incoming HTTP request, or waiting on a blocking datastore) to a model with a smaller thread pool, where each thread switches between requests many times and is effectively . Fortunately, you will not have to deal with these threads yourself, as this is where Reactor comes in. Reactor allows you to define your request handling pipeline in a functional, high-level abstraction through Flux and Mono. So rather than returning a List<Person> from your controller, for instance, you would return a Flux<Person>. However, this does also mean that relying on ThreadLocals will no longer work, and there are quite a number of frameworks and libraries (including Spring) that rely on these for some of their functionality.

Alberto: A lot of frameworks and libraries do not support this style of programming, Hibernate is an example. How can we combine our most used frameworks and Reactive Programming? The stack will evolve to support? I really like Spring Data JPA and I was thinking about that.

Arjen: Indeed, another difference between a traditional architecture and a reactive one is data stores. Obviously, in Reactive environments, there is a strong preference for non-blocking, asynchronous data stores. For some data stores, this is a great fit; for others it is not. There are a couple of NoSQL data stores that do reactive support: Couchbase, and MongoDB come to mind. For other, more traditional data access APIs, such as JDBC and JPA, there is no reactive support yet, so if you rely on them, there is no other choice than to wrap these synchronous, blocking APIs, and give them access to their own thread pool. Oracle is actually working on a non-blocking version of JDBC, but details are very sparse at this point. For JPA, the question is whether a session-based model actually makes sense in a reactive world.

Alberto: Could you explain how the Reactive Programming will help our applications to scale? Is there this relation between Reactive Programming and scalibility?

Arjen: I like to compare the two programming styles with an analogy. Imagine you are eating in a restaurant, and want to order food. The moment you make your order with the waiter, he goes off to the kitchen and prepare it. When the food is done, he will come back from the kitchen to serve it to you. After that, he will look around to see if there are any other customers in need of his attention. Now imagine the owner of the restaurant wants to have more tables, i.e. he wants to scale up. If he wants to make sure every table does not have to wait for too long, he would have to hire as many waiters as he added tables, adding a lot of cost. This is the traditional, one-thread-per-request Servlet model, where the customers are HTTP clients, and the waiters represent threads.

A reactive model would operate more like a real-life restaurant, where the waiters just relay the orders to kitchen staff, and many people (i.e. threads) are working on your order before it is served to you. Looking at it this way, it becomes quite obvious why the reactive model scales better: you are using the resources available more efficiently.

That said, the reactive model does come with a certain cost. It is harder to write asynchronous code, and it is harder to debug. So Reactive Programming will definitely help *certain* application to scale; for others I would simply say that the effort is simply not worth it. There will always be many cases where imperative is just fine for the task at hand and others where reactive and non-blocking are a must. And with Spring 5, you can do both.

Espero que você tenha gostado da entrevista! Arjen foi muito solícito e acho que nos forneceu boas respostas.

Facilidades com Mapping no Spring MVC 4.3

Olá pessoal, tudo bem? Me chamo Alex Felipe, sou editor de conteúdo na Alura com mais foco no blog da Alura, e também, sou desenvolvedor! A partir de agora e eventualmente, estarei escrevendo alguns posts sobre Spring, ou seja, novidades do framework, dicas que fazem toda diferença no nosso dia-a-dia como desenvolvedor entre diversos tópicos super bacanas! Espero que gostem 🙂

Uma das maiores rotinas para nós, desenvolvedores que usam o Spring, é justamente mapear um método para disponibilizarmos um recurso no sistema. Como fazemos isso no Spring MVC? Basicamente utilizamos a annotation @RequestMapping, como por exemplo, para exibir a home do nosso sistema:

@Controller
public class HomeController {

	@RequestMapping("/")
	public String home() {
	    return "home";
	}

}

E então, quando acessamos o endereço "/" do nosso sistema, ele exibe o conteúdo da nossa página home. Porém, existe casos em que estamos dentro de um controller e queremos acessar a mesma URL, porém, dependendo do verbo HTTP, queremos que o nosso sistema tenha um comportamento diferente.

Por exemplo, em um sistema que eu estava desenvolvendo, continha o controller ProdutoController e dentro tinha duas funcionalidades a gravar() e a listar():

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("produtos")
public class ProdutoController {

	@RequestMapping(method = RequestMethod.POST)
	public String gravar(Produto produto) {
		// recebe produto e salva
		return "redirect:/produtos";
	}

	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView listar() {
		ModelAndView mav = new ModelAndView("/produtos/lista");
		// pega produtos do banco e lista
		return mav;
	}

    //métodos

}

Note que ambos os métodos estão mapeados para o mesmo endereço, "/produtos". Porém, para que isso seja possível no controller, diferenciamos cada método pelo request method do HTTP. Em outras palavras, quando acessamos o endereço "produtos" com GET entramos no método listar() e quando acessamos com POST entramos no método gravar().

Por enquanto não há muita novidade, mas e se ao invés de ficar acessando @RequestMapping(method=AlgumaCoisa), pudéssemos fazer algo como @GetMapping para GET, ou então, @PostMapping para POST? Seria super bacana, né? Pois é, a partir da nova release 4.3 do Spring que saiu agora em Julho de 2016, já podemos fazer isso!

Então aquele mesmo controller, com essas novas annotations, ficaria da seguinte forma:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("produtos")
public class ProdutoController {

	@PostMapping
	public String gravar(Produto produto) {
		// recebe produto e salva
		return "produtos/salvo";
	}

	@GetMapping
	public ModelAndView listar() {
		ModelAndView mav = new ModelAndView("/produtos/lista");
		// pega produtos do banco e lista
		return mav;
	}

	//métodos

}

Veja que agora o nosso mapeamento é muito mais objetivo! Em outras palavras, não precisamos mais nos preocupar em ficar passando o parâmetro method para indicar a qual tipo de request method o método irá funcionar. Além do @GetMapping e @PostMapping, temos também os seguintes métodos do HTTP:

Lembrando que essas novas annotations não são implementações totalmente novas, pois, por de trás dos panos, elas ainda fazem uso da annotaton @RequestMapping. Por exemplo, a implementação do @GetMapping:

@Target(value=METHOD)
 @Retention(value=RUNTIME)
 @Documented
 @RequestMapping(method=GET)
public @interface GetMapping

Isso significa que ainda podemos utilizar os mesmos atributos que utilizamos no @RequestMapping com exceção do method que já não é mais necessário 😉

Abstraindo o download de arquivos no Spring MVC

Há um tempo atrás, escrevi um post sobre como realizar download de arquivos utilizando o Spring MVC. Rafael Ponte,  um amigo meu, empreendedor/programador que mora em Fortaleza, perguntou se não tinha um jeito mais fácil de realizar o mesmo processo. Basicamente ele queria que tivesse uma abstração melhor para indicar um download, algo como o que segue:

	@RequestMapping(value = "/downloads/{code}", 
		method = RequestMethod.GET)
	public FileDownload dowload(@PathVariable("code") String code,
			@AuthenticationPrincipal User user) {

		Optional<Course> foundCourse = courseDao.findByUserEmailAndCode(
				user.getEmail(), code);

		if (!foundCourse.isPresent()) {
			return ResponseEntity.notFound().build();
		}

		Book book = bookDao.find(foundCourse.get().getCode());
		return new FileDownload(...);
	}

Pronto, eu não encontrei nada. Confesse que não procurei muito, e, ainda acho, que existe a chance de ter algo mais elegante e que eu não conheça. De todo jeito eu caí no mesmo problema, trabalhando em um projeto interno aqui na Caelum e a pessoa que estava pareando comigo fez o mesmo questionamento :/. Nesse momento paramos para pensar um pouco mais e chegamos na seguinte solução de código:

@RequestMapping(value = "/downloads/{code}", 
	method = RequestMethod.GET)
public HttpEntity<?> dowload(@PathVariable("code") String code,
		@AuthenticationPrincipal User user) {

	Optional<Course> foundCourse = courseDao.findByUserEmailAndCode(
			user.getEmail(), code);
	if (!foundCourse.isPresent()) {
		return ResponseEntity.notFound().build();
	}

	Book book = bookDao.find(foundCourse.get().getCode());
	return new DownloadEntity(book.generateDrmVersion(user), 
		code + ".pdf");
}

Basicamente alcançamos a mesma abstração que Rafael tinha sugerido :). A sacada é que um dos possíveis retornos do seu método é um objeto cuja classe seja igual ou filha de HttpEntity. Herdamos dela e criamos nossa abstração para representar um download.

package br.com.caelum.alumni.infra.spring;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;

public class DownloadEntity extends HttpEntity<byte[]> {

	private HttpEntity<byte[]> httpEntity;

	public DownloadEntity(byte[] bytes, String fileName) {
		HttpHeaders httpHeaders = new HttpHeaders();
		httpHeaders.add("Content-disposition", "attachment; filename=\""+fileName+"\"");
		httpEntity = new HttpEntity<byte[]>(bytes, httpHeaders);
	}

	public HttpHeaders getHeaders() {
		return httpEntity.getHeaders();
	}

	public byte[] getBody() {
		return httpEntity.getBody();
	}

	public boolean hasBody() {
		return httpEntity.hasBody();
	}

	public boolean equals(Object other) {
		return httpEntity.equals(other);
	}

	public int hashCode() {
		return httpEntity.hashCode();
	}

	public String toString() {
		return httpEntity.toString();
	}
}

Herdamos de HttpEntity e simplesmente delegamos as chamadas dos métodos para o HttpEntity que criamos internamente no nosso objeto. Uma solução simples e que você pode reaproveitar para vários projetos seus.

Acho que esse é um passo muito importante quando falamos de programação, no caso aqui estamos usando orientação a objetos. Precisamos ser capazes de aplicar os conceitos que aprendemos em qualquer situação, não importa se o código é nosso ou provido por algum framework. Tem uma interface, você pode implementar, ta vendo uma classe que não é marcada como final, você pode herdar! O foco é a solução e a facilidade de manutenção :).

Como você disponibiliza os mesmos objetos para várias views?

Nos últimos tempos, no meu tempo extra, desenvolvi um sistema administrativo junto com um amigo meu de aulas na CaelumRasa Lariguet. Como vários destes sistemas, existem elementos dinâmicos que devem aparecer em toda santa tela que é carregada pela aplicação. No nosso caso, tínhamos que exibir sempre os últimos agendamentos e um calendário com o número de agendamentos para determinado dia. Abaixo segue um print:

componentes

Para fazer isso, uma das maneiras é adicionar esses objetos em todo request tratado pela aplicação.

	@Controller
	@RequestMapping("/cliente")
	public class ClienteController {

		@Autowired
		private ProximosServicos proximosServicos;
		@Autowired
		private CalendarioDia calendario;		
		
		@RequestMapping("/form")
		public ModelAndView form(Cliente cliente) {
			ModelAndView modelAndView = new ModelAndView("cliente/form-add");
			modelAndView.addAttribute("proximosServicos",proximosServicos);
			modelAndView.addAttribute("calendario",calendario);
			return modelAndView;

		}

		//outros métodos
	}

Como você já pode imaginar, essa solução ia ficar um pouco complicada. E se a gente precisasse colocar mais um objeto na tela? Teríamos que alterar diversos controllers! Um paliativo seria criar uma classe que faria esse trabalho para a gente.

	@Controller
	@RequestMapping("/cliente")
	public class ClienteController {

		@Autowired
		private ComponentesComunsViews componentes;
		
		@RequestMapping("/form")
		public ModelAndView form(Cliente cliente) {
			ModelAndView modelAndView = new ModelAndView("cliente/form-add");
			componentes.adicionaNaView(modelAndView);
			return modelAndView;

		}

		//outros métodos
	}

Mesmo assim, vamos ter que receber um objeto dessa classe injetado em todos os controllers e aí vamos ter que invocar um método nele para colocar tudo necessário no request. Uma segunda alternativa seria criar um interceptor. Dessa forma podemos interceptar todas as requisições, como se fosse um filtro de Servlet, e já disponibilizar esses objetos para a view.

Todas essas são formas são válidas, mas eu estou aqui para sugerir mais uma, que talvez você até já conheça. Ao invés de fazer com que todo controller ou que algum interceptor faça este trabalho, podemos simplesmente disponibilizar um objeto especializado em toda a view, de maneira automática! Vamos pegar especificamente o componente que busca os próximos serviços.

@Component
@Scope(scopeName = WebApplicationContext.SCOPE_REQUEST)
public class ProximosServicos {

	@Autowired
	private AgendamentoDao agendamentoDao;
	@Autowired
	private DataSolicitadaAgendmento dataSolicitada;

	public List<Agendamento> lista() {		
		List<Agendamento> agendamentos = agendamentoDao
				.buscaAgendamentosDoDiaAPartirDeUmHorario(dataSolicitada.get());
		return agendamentos;
	}
}

Na página, para listar os agendamentos, basta que a gente continue fazendo como já estava sendo feito.

  	<c:forEach var="agendamento" items="${proximosServicos.lista()}">
		...
	</c:forEach>

O único problema que falta resolver é: como que esse objeto foi parar na jsp? Para ensinar ao Spring MVC, basta que invoquemos mais um método na hora de configurar o nosso objeto do InternalResourceViewResolver. 

	@Bean
	public InternalResourceViewResolver internalResourceViewResolver() {
		InternalResourceViewResolver resolver = new InternalResourceViewResolver();
		resolver.setPrefix("/WEB-INF/views/");
		resolver.setSuffix(".jsp");
		//aqui faz a mágica
		resolver.setExposeContextBeansAsAttributes(true);
		return resolver;
	}

Pronto, o objeto do tipo ProximosServicos sempre vai estar disponível em qualquer página da sua aplicação. O método setExposeContextBeansAsAttributes, como o próprio nome informa, faz com que qualquer objeto gerenciado pelo Spring fique acessível nas páginas. Caso você ache que isso pode fazer com que a gente fique usando tudo que é objeto direto na jsp, fique tranquilo, você também pode usar o setExposedContextBeanNames e passar o nome do bean que deve ser disponibilizado.

Mesmo enxergando o perigo de um acesso indiscriminado aos objetos na view, eu gosto dessa abordagem e tenho usado em meus projetos. Acho que deixamos as classes mais coesas, tanto nos controllers quanto nos objetos específicos para a camada de visualização.

Chegou até o fim do post? Conta para mim o que você achou :). Baseado nas discussões, muitas vezes, já surge a ideia para um próximo!

O problema do input vazio no formulário

Numa aplicação web com muitos formulários, é muito comum existirem alguns campos de preenchimentos obrigatórios e outros não. E uma coisa que eu aprendi e que prezo muito nas aplicações é que tudo deve ser validado antes de chegar na regra de negócio em si. Não quero ficar tomando erros por conta de dados em estado inválido.

Uma situação curiosa que aconteceu comigo nesses dias é que tinha um cadastro onde o email precisava ser único para a entidade. É uma situação que acontece comumente em todas as nossas aplicações, outros casos clássicos são CPF, login de usuário etc. Criei até um validador da Bean Validation genérico para isso. Um ponto interessante nesse cadastro é que o email, por mais que precise ser único, não é obrigatório e, por conta disso, o usuário pode deixar o campo vazio.

E o campo texto vazio é onde mora o problema. Além de colocar a validação na aplicação, também criei a constraint para a coluna da tabela e, para ela, valor em branco é um valor que precisa ser único :/. Abaixo segue o código do meu validador genérico que deixa passar valores vazios.

	public class UniquenessValidator implements
			ConstraintValidator<ValidateUniqueness, Object> {

		...

		@Override
		public boolean isValid(Object target, ConstraintValidatorContext context) {
			UniquenessDao uniquenessDao = ApplicationContextHolder
				.instance.getBean(UniquenessDao.class);
			boolean valid = true;
			for (String fieldName : fieldNames) {
				Object fieldValue = getFieldValue(target, fieldName);
				if(fieldValue ==null || 
					!StringUtils.hasText(fieldValue.toString())) {
					continue;
				}

Para o banco não considerar a constraint, o valor sendo inserido precisava ser nulo. Caso você já tenha passado por essa situação, conte nos comentários como resolveu. A minha abordagem é sempre tentar usar um recurso do framework em questão, que para esse projeto foi o Spring MVC(quem diria..). Assim como qualquer framework web que se preze, o Spring MVC é cheio de converters para pegar os valores que vem do formulário e converter para os tipos necessários dos nossos objetos. Para ser bem sincero ele possui várias interfaces para tratar da conversão de valores, que foram surgindo com o passar do tempo.

O que precisamos fazer é basicamente registrar um conversor que já existe dentro do framework e que pode tratar textos vazios como Strings nulas :).

@Controller
@RequestMapping("/profissional")
@Transactional
public class ProfissionalController {

	...

	@InitBinder
	public void initBinder(WebDataBinder dataBinder) {
		...
		dataBinder.registerCustomEditor(String.class, "profissional.email",
				new StringTrimmerEditor(true));
	}

Perceba que fiz o registro para um @Controller específico. O código é feito dentro do método anotado com @InitBinder. O conversor usado em questão foi o StringTrimmerEditor, que recebe um boolean como argumento, indicando se ele deve tratar Strings vazias como nulo.

	public class StringTrimmerEditor extends PropertyEditorSupport {

		private final String charsToDelete;

		private final boolean emptyAsNull;

		public StringTrimmerEditor(boolean emptyAsNull) {
			this.charsToDelete = null;
			this.emptyAsNull = emptyAsNull;
		}

		public StringTrimmerEditor(String charsToDelete, boolean emptyAsNull) {
			this.charsToDelete = charsToDelete;
			this.emptyAsNull = emptyAsNull;
		}


		@Override
		public void setAsText(String text) {
			if (text == null) {
				setValue(null);
			}
			else {
				String value = text.trim();
				if (this.charsToDelete != null) {
					value = StringUtils.deleteAny(value, this.charsToDelete);
				}
				if (this.emptyAsNull && "".equals(value)) {
					setValue(null);
				}
				else {
					setValue(value);
				}
			}
		}

		@Override
		public String getAsText() {
			Object value = getValue();
			return (value != null ? value.toString() : "");
		}

	}

Essa é uma das coisas que gosto muito no Spring. Eles possuem certas implementações prontas, típicas de quem já passou por problemas comuns enquanto desenvolve uma aplicação.

Bom essa era um post mais simples, mas acho que pode ser útil para quem passar por uma situação parecida.

Access Control List: Protegendo o acesso a seus objetos

O Spring Security é um dos módulos mais usados do Spring e sem dúvida cumpre o seu papel muito bem. Ele é bastante usado para restringirmos o acesso aos métodos e urls da nossa aplicação baseado nos perfis dos usuários do sistema. Uma situação mais complicada pode aparecer quando dois usuários possuem um perfil que permite o acesso a uma mesma url, por exemplo um endereço que exibe o detalhe de uma prova dele em um sistema de provas online. Segue abaixo o exemplo:

  /prova/resolucao/1

E se o outro usuário logado também tentar acessar o mesmo endereço de detalhe? Como você vai negar o acesso? Provavelmente acabaríamos escrevendo um código parecido com o que segue.

	Usuario usuarioLogado = ...;
	ResolucaoProva resolucao = resolucaoProvaDao.carrega(idResolucao);
	if(!resolucao.pertence(usuarioLogado)){
		throw new SemAutorizacaoException("espertinho...");
	}

Essa é uma solução perfeitamente válida. O ponto negativo é que você não vai reaproveitar para os outros sistemas. Além do mais, esse sistema de permissões pode ficar mais complexo com usuários podendo ver detalhes de outros usuários, mas sem possibilidade edição por exemplo. Tudo vai depender do tipo de aplicação que você estiver desenvolvendo.

Para amenizar essa complexidade, o Spring Security tem um módulo chamado de ACL(Access Control List). O objetivo dele é justamente generalizar essa implementação nos dando a possibilidade de aplicar regras de acesso para qualquer tipo de objeto! Essa semana eu e Rafael Rollo utilizamos ele em um projeto dentro da Caelum. O objetivo do post é justamente mostrar o código necessário para deixar tudo funcionando. Respire bastante, porque não é pouco código não.

Vamos começar pela parte mais interessante, pelo menos para mim, que é a de inserir as permissões de acesso para determinado objeto. Dê uma olhada no código abaixo:

	@Autowired
	private Permissoes permissoes;

	...

	@RequestMapping(value = "/finaliza", method = RequestMethod.POST)
	public String finaliza(HttpSession session,@AuthenticationPrincipal 
				Usuario usuarioLogado) {

		ResolucaoFinal resolucaoFinal = resolucaoEmAndamento
				.geraResolucaoFinal();
		resolucaoFinalDao.save(resolucaoFinal);

		//essa classe isola o código de adicionar permissões
		permissoes.adiciona(usuarioLogado,resolucaoFinal,BasePermission.READ);

		return "redirect:/resolucao/finalizada";
	}
	@Service
	public class Permissoes {
		
		@Autowired
		private MutableAclService mutableAclService;	

		public void adiciona(UserDetails user, Object objeto, 
                      Permission... permissoes) {
	        ObjectIdentity identity = new ObjectIdentityImpl(objeto);
	        MutableAcl acl = mutableAclService.createAcl(identity);
	        PrincipalSid principalSid = new PrincipalSid(user.getUsername());
	        for (Permission permissao : permissoes) {
	        	acl.insertAce(acl.getEntries().size(), permissao, principalSid, true);			
			}
	        mutableAclService.updateAcl(acl);	
		}

	}

Vamos focar na classe Permissoes, já que nela está acontecendo a parte que nos interessa. A classe MutableAclService é a nossa porta de entrada para criar, atualizar e ler as permissões de acesso a um objeto. Tanto que nesse pequeno trecho de código usamos dois métodos dela:

  1. createAcl
  2. updateAcl

Vamos analisá-los por partes, começando pelo createAcl. 

		public void adiciona(UserDetails user, Object objeto, 
                        Permission... permissoes) {
	        ObjectIdentity identity = new ObjectIdentityImpl(objeto);
	        MutableAcl acl = mutableAclService.createAcl(identity);
                ...

O createAcl espera como argumento um ObjectIdentity, que é justamente a representação do objeto que vai ter o acesso controlado. O retorno deste método é um objeto do tipo MutableAcl, a abstração para as permissões de um objetoComo você já deve ter notado, o construtor do ObjectIdentity recebe um Object como parâmetro, já que qualquer tipo de objeto pode ser protegido. Um ponto importante é que esse Object deve ter um método chamado getId, cujo retorno será usado como chave estrangeira nas tabelas que serão criadas(ainda vamos comentar sobre isso). Caso sua classe não possua um método chamado getId, você pode usar o outro construtor que recebe além do objeto, um id como argumento.

Para falarmos do updateAcl, precisamos primeiro dar uma olhada no método insertAcl da classe MutableAcl. Através dele é que vamos adicionando permissões para o nosso objeto.

	        MutableAcl acl = mutableAclService.createAcl(identity);
	        PrincipalSid principalSid = new PrincipalSid(user.getUsername());
	        for (Permission permissao : permissoes) {
	           acl.insertAce(acl.getEntries().size(), permissao, principalSid, true);			
		}

Para ser bem sincero, essa assinatura de método é um pouco estranha para mim :/. Vamos começar pelos argumentos que fazem mais sentido, o segundo e o terceiro. O segundo argumento é um objeto cuja classe implementa a interface Permission, que é a abstração de uma permissão dentro do ACL. Ele já tem uma implementação base que é tranquila para a gente usar.

	public class BasePermission extends AbstractPermission {
		public static final Permission READ = new BasePermission(1 << 0, 'R'); // 1
		public static final Permission WRITE = new BasePermission(1 << 1, 'W'); // 2
		public static final Permission CREATE = new BasePermission(1 << 2, 'C'); // 4
		public static final Permission DELETE = new BasePermission(1 << 3, 'D'); // 8
		public static final Permission ADMINISTRATION = new BasePermission(1 << 4, 'A'); // 16

		protected BasePermission(int mask) {
			super(mask);
		}

		protected BasePermission(int mask, char code) {
			super(mask, code);
		}
	}

Caso você esteja questionando o motivo da permissão ser representada por um int, bem vindo ao time :), neste link eles discutem sobre a decisão. O terceiro argumento do método é justamente o dono do objeto, que é representado pela classe PrincipalSid(Principal Security Identity).  Passamos como argumento no construtor dele um identificador desse “dono” que existe dentro do nosso sistema, no meu caso foi o email do usuário.

Pensando de maneira direta, os argumentos da permissão e do dono daquela permissão deveriam ser suficientes para definir a regra de acesso ao objeto, só que ainda temos outros dois. Vou tentar ser breve aqui, já que para mim eles não fazem muito sentido. A classe MutableAcl possui um outro método chamado updateAce, cuja assinatura segue abaixo:

    void updateAce(int aceIndex, Permission permission) throws NotFoundException;

Perceba que o primeiro argumento é um índice que é necessário para localizar a posição da permissão. Como para atualizar precisa do índice, para inserir você também precisar passar qual é a posição que você deseja adicionar a nova permissão. Essa é a razão do primeiro argumento do método insertAce ser um inteiro. O último argumento é um boolean para indicar se você realmente quer dar aquela permissão de acesso ao objeto. O que eu não entendo é: se você não quer dar permissão de acesso, não é melhor simplesmente não adicionar? Sei lá, devo estar me passando em alguma coisa aqui.

Com as permissões inseridas no objeto do tipo MutableAcl, podemos invocar o updateAcl da classe MutableAclService, para que tudo seja refletido no banco de dados.

Uma vez que as permissões estão associadas, você deve estar se perguntando como vamos proteger o acesso ao método. Dê uma olhada no código abaixo.

	@RequestMapping(value = "/prova/resolucao/{id}", method = RequestMethod.POST)
	@PreAuthorize("hasPermission(#id,'br.com.caelum.provas.models.ResolucaoFinal','read')")
	public String visualiza(@PathVariable("id") Integer id) {
		ResolucaoFinal resolucao = resolucaoDao.carrega(id);
		...
	}

Aqui fizemos uso das expressões suportadas pelo Spring. Por sinal esse é um tema que merece um post aqui no blog :). Temos acesso a função hasPermission que recebe três argumentos.

  1. O id objeto que está tentando ser acessado. Repare que usamos o #” para indicar que ele deve pegar esse valor do argumento de mesmo nome.
  2. A classe do objeto que ele precisa carregar.
  3. O nome da permissão que o usuário logado precisa ter. Esse nome precisa ser o mesmo da constante usada na hora de adicionar a permissão. Por default o ACL vai fazer o carregamento da permissão baseado nessa condição.

O Spring Security ACL vai verificar se o usuário logado tem acesso especificamente a este objeto. Essa é uma solução super genérica e que você pode aproveitar para qualquer projeto seu!

Para fechar, precisamos realizar as configurações necessárias para que tudo funcione. Essa parte toda eu vou deixar isolada em uma classe específica.

	@Configuration
	public class ConfiguracaoACL {

		@Autowired
		private DataSource dataSource;

		@Bean
		public AclAuthorizationStrategy aclAuthorization() {
			AclAuthorizationStrategyImpl aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
					new SimpleGrantedAuthority("ROLE_ADMIN"));				
			return aclAuthorizationStrategy;
		}

		@Bean
		public PermissionGrantingStrategy permissionGrantingStrategy() {
			PermissionGrantingStrategy grantingStrategy = new DefaultPermissionGrantingStrategy(
					new ConsoleAuditLogger());
			return grantingStrategy;
		}

		@Bean	
		public Ehcache ehCache() {
			CacheManager cacheManager = CacheManager.create();
			if (!cacheManager.cacheExists("aclCache")) {
				cacheManager.addCache("aclCache");
			}

			Ehcache ehCache = cacheManager.getEhcache("aclCache");
			return ehCache;
		}
			

		@Bean
		public AclCache aclCache(Ehcache ehCache, PermissionGrantingStrategy grantingStrategy,
				AclAuthorizationStrategy aclAuthorizationStrategy) {
			AclCache aclCache = new EhCacheBasedAclCache(ehCache, grantingStrategy,
					aclAuthorizationStrategy);

			return aclCache;
		}

		@Bean
		public BasicLookupStrategy basicLookupStrategy(AclCache aclCache,
				AclAuthorizationStrategy aclAuthorizationStrategy,
				PermissionGrantingStrategy grantingStrategy) {

			return new BasicLookupStrategy(dataSource, aclCache, aclAuthorizationStrategy,
					grantingStrategy);
		}

		@Bean
		public MutableAclService mutableAclService(BasicLookupStrategy lookupStrategy, AclCache aclCache) {
			JdbcMutableAclService service = new JdbcMutableAclService(dataSource, lookupStrategy,
					aclCache);
			service.setClassIdentityQuery("select last_insert_id()");
			service.setSidIdentityQuery("select last_insert_id()");
			return service;
		}

		@Bean
		public MethodSecurityExpressionHandler expressionHandler(PermissionEvaluator permissionEvaluator) {
			DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
			handler.setPermissionEvaluator(permissionEvaluator);
			return handler;
		}

		@Bean
		public PermissionEvaluator permissionEvaluator(MutableAclService aclService) {
			AclPermissionEvaluator aclPermissionEvaluator = new AclPermissionEvaluator(aclService);
			return aclPermissionEvaluator;
		}

	}

Aqui você precisa medir seus esforços. Não se pode negar que cada um desses métodos produz um objeto que é necessário para o funcionamento, mas não acho saudável gastar vários parágrafos explicando um por um. Vou deixar uma lista apenas com uma breve ideia :).

  1. LookupStrategy: dependência do JdbcMutableAclService para fazer buscas no banco pelas permissões.
  2. AclCache: Camada de cache para não ficar indo no banco o tempo todo.
  3. AclAuthorizationStrategy: Verifica se certa role pode alterar permissões de um objeto.
  4. PermissionGrantingStrategy: Verifica se certo usuário pode realizar uma operação sobre o objeto.
  5. PermissionEvalutaor: Necessária para suportar o hasPermission nos métodos.

Como é de praxe, aconselho que você olhe o Javadoc de todas elas, para saber exatamente do papel de cada uma. Aproveitando, já vou fazer uma atualização no SetupMyProject para suportar a configuração do ACL :). Para completar é necessário criar as tabelas necessárias para que o módulo do ACL funcione e, além disso, modificar sua classe de configuração do Spring Security e adicionar a annotation @EnableGlobalMethodSecurity(prePostEnabled=true). Essa annotation habilita a checagem da annotation @PreAuthorize :).

Para você não esquecer, também deixo as dependências que precisam ser adicionadas no pom.xml.

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-acl</artifactId>
		</dependency>
		<!-- cache -->

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-cache</artifactId>
		</dependency>

		<dependency>
			<groupId>net.sf.ehcache</groupId>
			<artifactId>ehcache</artifactId>
		</dependency>

Pronto! O post foi um pouco longo, mas também o uso e configuração dele não é lá uma das coisas mais fáceis. E esse foi só o primeiro deste ano, se preparem para mais. Também não deixem de mandar sugestões para temas, por aqui ou me referenciando no twitter.