Começando com a magia do Spring Boot

No livro sobre Spring que escrevi para a Casa do Código e no SetupMyProject fiz a opção pelo uso normal das extensões do Spring. Para criar o projeto web, usei o Spring MVC no seu modo convencional configurando tudo que era necessário, assim como fiz para a parte da JPA, Spring Security etc. No fim acho realmente importante que as pessoas conheçam bem as tecnologias que usam, para terem um olhar crítico sobre a ferramenta e poderem achar os pontos de extensão necessários para os projetos do dia a dia.

Na época da escrita do livro, o time do Spring já tinha lançado uma outra extensão chamada Spring Boot, cujo objetivo era acelerar o processo de configuração da aplicação, estabelecendo configurações defaults baseadas nas experiências dos desenvolvedores do próprio Spring, assim como pelo feedback provido pelos usuários do framework. Optei por não usá-lo, justamente pelos motivos citados no primeiro parágrafo. Agora, com tudo mais sedimentado e com um conhecimento realmente bom do framework resolvi que era hora de começar a construir uma aplicação com o Spring Boot, já que, principalmente durante a escrita do livro, fui obrigado a navegar muito pelos internals do Spring e tudo mais. Peguei como exemplo a própria app do livro, ela possui regras boas o suficiente para me fazer passar por diversos problemas. O código inicial já está no github.

Então vamos começar! O primeiro passo, como não poderia ser diferente, é a criação do projeto. Você pode realizar este passo no próprio SetupMyProject. O zip vai vir com o mínimo configurado, mas é aí que você já vai perceber a diferença. Para conseguir subir o servidor e acessar a primeira rota, só precisamos de uma classe configurada no projeto.

	@SpringBootApplication
	@Controller
	public class CasadocodigoSpringbootApplication {

		@RequestMapping("/")
		@ResponseBody
		public String index(){
			return "funciona?";
		}

		public static void main(String[] args) {
			SpringApplication.run(CasadocodigoSpringbootApplication.class, args);
		}
	}

Aqui já temos um pouco de mágica. Você vai rodar sua aplicação web a partir de um bom e velho método main. A classe SpringApplication vai ler as configurações da classe passada como argumento e pronto, você tem a sua aplicação no ar. A outra parte da mágica acontece por conta da annotation @SpringBootApplication. Ela é um Sterotype do Spring Boot que já encapsula algumas outras annotations, como a @EnableAutoConfiguration. Essa última, por sua vez, carrega a AutoConfigurationPackages que é responsável por configurar os pacotes que devem ser escaneados, baseados na localização da sua classe.

Um outro ponto bem impressionante é o pom.xml criado, você vai perceber que ele possui pouquíssimas dependências! Vamos dar uma rápida olhada.

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.3.0.BUILD-SNAPSHOT</version>
	</parent>	

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

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

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

Perceba que você adiciona algumas dependências que eles chamam de starters. A grande sacada é fazer com esses artefatos já baixem tudo que você precisa. E aqui, eu preciso dizer que, pelo menos para mim, eles tomaram uma decisão bastante acertada. Ao invés de te dar a opção de escolher entre vários frameworks, servidores web, libs específicas e etc, eles tomaram algumas decisões e empurraram pra gente. Para a maioria dos projetos isso não muda e, se for preciso, você pode sobreescrever tudo que eles fizeram. A motivação deles foi a mesma que tivemos ao construir o SetupMyProject. Já estamos cheios de frameworks, precisamos é de uma maneira mais fácil de usá-los.

Por mais que já tenhamos o projeto configurado, provavelmente esse não é bem o modelo que vamos seguir. Os nossos controllers não vão ser declarados dentro da mesma classe de configuração. O normal é criar uma outra classe que representa este seu controller.

        @Controller
	@Transactional
	@RequestMapping("/produtos")
	public class ProductsController {

		@Autowired
		private ProductDAO products;
		@Autowired
		private FileSaver fileSaver;
		@Autowired
		private ServletContext ctx;
		@Autowired
		private InternalResourceViewResolver resolver;

		@RequestMapping(method=RequestMethod.GET)
		@Cacheable(value="lastProducts")
		public ModelAndView list() throws ServletException, IOException{
			System.out.println("ola");
			ModelAndView modelAndView = new ModelAndView("products/list");
			modelAndView.addObject("products", products.findAll());
			return modelAndView;
		}

		...

	}

E agora é só pedir para escanear o pacote… Opa, não precisa mais! Como eu criei esse classe em um pacote abaixo do pacote da classe de configuração, todas as classes já vão ser escaneadas de maneira automática. Agora é necessário configurar o acesso a JPA e, nesse momento, nossos corações vão ficar felizes novamente. O nosso único trabalho é adicionar um starter no pom.xml. 

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

Quase tudo que você acha que deveria fazer para configurar o acesso ao banco com a JPA, vai ser feito automagicamente. Não será necessário configurar persistence.xml, EntityManagerFactory, EntityManager, PlatformTransactionManager nem nada, tudo vai ser feito pra você. A única coisa que você precisa entregar a ele é o DataSource com os  dados de acesso ao banco. Podemos criar um método com essa configuração na própria classe que contém nosso main. 

@SpringBootApplication
	public class CasadocodigoSpringbootApplication {
		@Bean
		public DataSource dataSource(Environment environment) {
			DriverManagerDataSource dataSource = new DriverManagerDataSource();
			dataSource.setDriverClassName("com.mysql.jdbc.Driver");
			dataSource.setUrl("jdbc:mysql://localhost:3306/casadocodigo");
			dataSource.setUsername("root");
			dataSource.setPassword("");
			return dataSource;
		}

		public static void main(String[] args) {
			SpringApplication.run(CasadocodigoSpringbootApplication.class, args);
		}
	}

Além disso, ele também já inclui as dependências para a Spring Data JPA, para facilitar a criação dos seus DAOs. Para completar essa primeira parte da migração do projeto, foi necessário fazer uma pequena configuração extra para os jsps. O Spring Boot sugere que você use outra tecnologia de view, mas como o jsp era a utilizada no projeto do livro, eu resolvi ir com ela mesmo. Como o tomcat está sendo executado de forma embedded, é necessário que adicionemos uma nova dependência no pom, que é a do compilador de jsps para este tipo de cenário.

	<dependency>
		<groupId>org.apache.tomcat.embed</groupId>
		<artifactId>tomcat-embed-jasper</artifactId>
		<scope>provided</scope>
	</dependency>

Além disso, como é procedimento normal, precisamos configurar um prefixo e um sufixo para busca da nossa view. Podemos fazer isso do jeito tradicional, adicionando um método que retorna um InternalResourceViewResolver. Só que podemos aproveitar a oportunidade para já apresentar um outro detalhe que você pode tirar proveito, que são os arquivos de configuração externos. Você pode criar um arquivo chamado application.properties na raiz do seu classpath, geralmente em src/main/resources. Lá você pode colocar configurações específicas das tecnologias utilizadas no projeto, abaixo segue o meu exemplo.

  spring.mvc.view.prefix=/WEB-INF/jsp/
  spring.mvc.view.suffix=.jsp

  #hibernate
  spring.jpa.hibernate.ddl-auto=update

Perceba que ele é bem diferente de um spring-context.xml da vida. Você não tem que declarar um bean para ser usado, simplesmente configura as propriedades do bean já escolhido. 

Para fechar com chave de ouro, pelo menos para mim :), eles deram mais um passo pensando na vida fácil do usuário. Nessa semana eles lançaram um novo starter chamado de DevToolsUma facilidade adicionada foi a de recarregamento do projeto para cada mudança que você faz no código. Só que, como eles mesmos disseram, esse recarregamento é mais esperto do que um simples hot deploy de um servidor web tradicional. Além disso, ainda adicionaram o suporte para o Live Reloadque possibilita o recarregamento automático das páginas no seu navegador sempre que você alterar uma classe sua no servidor!

Ufa! Falei bastante hoje :). Acho o Spring Boot um projeto muito útil e que, com certeza, você devia dar uma olhada. É um approach que por muito eu esperava no mundo Java e espero que influencie outros projetos a fazer o mesmo. Por fim, acho importante lembrar que você deve continuar procurando o motivo do acontecimento das coisas, para não ficar um “programador de Spring Boot” ao invés de um programador  Java.

Lidando com eventos dentro do Spring

No livro que escrevi  sobre o Spring MVC na Casa do Código, construo um projeto similar ao e-commerce usado na própria editora. Gosto bastante de exemplos baseados em lojas online, por mais que sejam batidos, eles possuem o necessário para discutirmos diversas aspectos dentro do desenvolvimento de um projeto. Por exemplo, quando uma compra é fechada e aprovada é necessário que algumas ações sejam tomadas: enviar um email, gravar a compra no banco de dados, disparar o processo de geração do ebook  etc.

A maneira simples resolver isso é invocando todos os métodos necessários logo após a compra ter sido aprovada.

	@RequestMapping(value = "checkout", method = RequestMethod.POST)
	public ModelAndView checkout(@AuthenticationPrincipal SystemUser user) {

			BigDecimal total = shoppingCart.getTotal();
			String uriToPay = "http://book-payment.herokuapp.com/payment";
			try {
				restTemplate.postForObject(uriToPay,
						new PaymentData(total), String.class);

                                paymentDAO.persist(new Payment(shoppingCart,user));
                                mailSender.send(...);

				return new ModelAndView("redirect:/payment/success");
			} catch (HttpClientErrorException exception) {
				return new ModelAndView("redirect:/payment/error");
			}			

	}

E se a gente precisar colocar mais um passo? E se a execução de algum dos passos envolver uma condição da compra sendo efetuada? Agora o email só é disparado caso a compra tenha um valor maior que R$ 100,00. Para todos esses casos somos obrigados a ficar alterando o trecho de código responsável, não importa a localização dele. Você pode tirar essa responsabilidade do controller e jogar para outra classe, quando um novo passo precisar ser adicionado será necessário alterar o trecho de código. Este tipo de situação em um projeto, onde um acontecimento gera a necessidade de execução de vários eventos, já é bem conhecido no mundo de desenvolvimento Orientado a Objetos e, inclusive, criaram até um Design Pattern cujo objetivo é justamente lidar com isso, o Observer. 

O pattern já é bem conhecido, tem até suporte dentro do próprio Java. Caso você queira criar uma classe que representa um evento, pode herdar de java.util.EventObject e trabalhar com ela dentro do seu código. O único ponto negativo é que com pattern ou sem pattern, você ainda é obrigado a guardar uma lista de objetos que precisam ser notificados, ou seja, o código continua nas suas mãos. Pensando nisso, alguns frameworks já fornecem a implementação deste Design Pattern para você. Aqui no post, vamos discutir como tirar proveito disso com o Spring.

O primeiro passo é trocar todo aquele código de controle sobre a execução dos eventos. Vamos simplesmente usar a classe ApplicationContext e informar que aconteceu um novo evento no sistema.

        public ModelAndView checkout(@AuthenticationPrincipal SystemUser user) {
          ...

	  restTemplate.postForObject(uriToPay,new PaymentData(total), String.class);
	  applicationContext.publishEvent(new NewPurchaseEvent(this, shoppingCart, user));
        }

O método publishEvent é o responsável por notificar todo mundo que está esperando pelo evento representado pelo objeto do tipo NewPurchaseEvent. Agora vamos dar uma olhada nessa classe.

  public class NewPurchaseEvent extends ApplicationEvent {

	/**
	 *
	 */
	private static final long serialVersionUID = 7410524290553788367L;
	private final ShoppingCart cart;
	private final SystemUser systemUser;

	public NewPurchaseEvent(Object source, ShoppingCart cart,
			SystemUser systemUser) {
		super(source);
		this.cart = cart;
		this.systemUser = systemUser;
	}

	public ShoppingCart getCart() {
		return cart;
	}

	public SystemUser getSystemUser() {
		return systemUser;
	}

  }

Perceba que ela herda de ApplicationEvent, uma classe do próprio Spring que herda de EventObject. A única diferença é que ela também adiciona um timestamp, apenas para saber quando o evento foi criado. Sempre quando você trabalhar com este modelo de programação, que também é conhecido como publish/subscriber, é importante que você tenha classes específicas que representam os eventos. Dessa forma você vai conseguir criar tratadores também bem específicos, mantendo a coesão do código. Agora que um novo evento está sendo disparado, quem deve tratá-lo?

   public class PurchaseEmailEventListener implements ApplicationListener<NewPurchaseEvent>{

	@Autowired
	private MailSender mailer;

	@Override
	public void onApplicationEvent(NewPurchaseEvent event) {
		System.out.println("Enviando email de nova compra "+event.getCart().getTotal());
		SimpleMailMessage email = new SimpleMailMessage();
		email.setFrom("compras@casadocodigo.com.br");
		email.setTo(event.getSystemUser().getLogin());
		email.setSubject("Nova compra");
		email.setText("corpodo email");
		mailer.send(email);
	}

   }

   public class PurchaseSaveDatabaseEventListener implements  ApplicationListener<NewPurchaseEvent>{

	@Override
	public void onApplicationEvent(NewPurchaseEvent event) {
		System.out.println("salvando no banco de dados");
	}

  }

A interface ApplicationListener deve ser implementada sempre que um novo tratador de evento for criado. Ela define apenas o método onApplicationEvent, que é onde você vai colocar a implementação do seu evento. Apenas para fim de curiosidade, ela herda de uma interface chamada java.util.EventListener, que já vem disponível dentro do próprio Java. A EventListener só serve para marcar que a classe é uma tratadora de eventos, esse tipo de interface é comumente chamada de interface de marcação. Quem for usar os observers do CDI pode usar ela para conseguir encontrar as classes depois.

Analisando o código, conseguimos chegar numa solução bem desacoplada. O local onde o evento é disparado não sabe nada sobre quem está tratando o mesmo. Também conseguimos adicionar novos tratadores sem precisar alterar a lógica do fechamento do pagamento. Uma última coisa que de vez em quando é solicitado é que os eventos aconteçam numa determinada ordem. Na teoria, os eventos não deviam se conhecer e, portanto, não deveria existir ordem. Só que sabemos que na prática, a teoria é outra :). Caso você se encontre nessa situação, basta que seus listeners implementem a interface org.springframework.core.Ordered.

   public class PurchaseSaveDatabaseEventListener implements ApplicationListener<NewPurchaseEvent>,Ordered{

	@Autowired
	private PaymentDAO paymentoDAO;

	@Override
	public void onApplicationEvent(NewPurchaseEvent event) {
               Payment payment = event.buildPayment();
               paymentDAO.persist(payment);
	}

	@Override
	public int getOrder() {
		return 0;
	}

   }

Eles vão ser executados começando pelo número mais baixo, nesse caso você sempre vai gravar a compra antes. A parte ruim é que se tiver muitos eventos você pode acabar se perdendo um pouco, mas de todo jeito pode ser útil para determinados cenários.

Por hoje é isso, espero que os posts estejam sendo úteis para quem está lendo. Ainda tem muitos por vir!

Download de arquivos no Spring MVC

Em um dos projetos que participo atualmente, temos que disponibilizar um zip para download ao fim de uma sequência de telas. Caso eu já tivesse o arquivo pronto, não teria nenhuma dificuldade. Bastaria criar um link na página apontando para o local do arquivo, desta maneira o usuário realizaria o download dele tranquilamente. Uma outra situação, que é o meu caso, é quando necessitamos construir o arquivo naquele determinado momento, em função do que foi solicitado pelo usuário.  Abaixo segue o exemplo do código do método do controller que disponibiliza o download.

@RequestMapping(method = RequestMethod.GET)
    public HttpEntity<byte[]> download(String token) {
        byte[] zipBytes = //pega os bytes de qualquer arquivo
	HttpHeaders httpHeaders = new HttpHeaders();
	httpHeaders.add("Content-Disposition", "attachment; filename=\""+fileToBePacked.getName()+"\"");
	HttpEntity<byte[]> entity = new HttpEntity<byte[]>(zipBytes,httpHeaders);
	return entity;
    }

Perceba que o código é bem direto. Apenas retornamos o array de bytes que representa os dados do arquivo que acabamos de montar. Neste exemplo, apenas para fins de curiosidade, usei o projeto zt-zip(https://github.com/zeroturnaround/zt-zip) da Zero Turnaround  para poder gerar o zip. Um detalhe mais interessante é o uso da classe  HttpEntity, ela permite que você construa a resposta HTTP de maneira bem customizada, definindo os headers que você achar necessário.  No nosso caso adicionamos o header Content-Disposition, para que fosse possível definir o nome do arquivo a ser baixado.

Este foi um post bem simples, mas espero que seja útil :). Continuarei na saga de fazer um post por semana, mesmo que de vez em quando  o assunto seja simples. Por sinal, para você que leu, prefere posts sempre práticos ou quer ver um post sobre algum detalhe mais interno do framework? Especificamente para o dia de hoje, eu fiquei em dúvida sobre qual caminho seguir.

 

Spring MVC, proteja-se do Mass Assignment

Vários sites possuem formulários de cadastro para novos usuários. Também não é incomum que esses mesmo sites, para que possam diferenciar os tipos de  usuários, usem um atributo para guardar os diferentes perfis.

	@Entity
	public class SystemUser {

		@Id
		private String login;
		private String password;
		@ManyToMany(fetch = FetchType.EAGER)
		private List<Role> roles = new ArrayList<>();

		//getter e setters

	}

O formulário de cadastro do site, para usuários que estejam apenas interessados em receber newsletter, poderia ser algo como segue:

	<form action="/admin/users" method="post">
		<input type="text" name="login"/>
		<input type="password" name="password"/>

                ...
	</form>

Abaixo temos um exemplo de código de controller para tratar esse cadastro.

	@RequestMapping(method=RequestMethod.POST)
	public ModelAndView savePossibleBuyer(SystemUser systemUser,RedirectAttributes redirectAttributes){
		systemUser.addRole(new Role("ROLE_NEWS"));
		userDAO.save(systemUser);
		redirectAttributes.addFlashAttribute("success", "Usuário cadastrado com sucesso");
		return new ModelAndView("redirect:/produtos/");
	}

Esse trecho de implementação talvez nos passe a impressão que todo novo usuário cadastrado, através desse formulário, vai possuir apenas o perfil ROLE_NEWS. Agora imagine que a pessoa que esteja preenchendo o cadastro seja um pouco mais maliciosa do que de costume e, por conta disso, tente fazer a seguinte alteração no seu form.

	<form action="/admin/users" method="post">
		<input type="text" name="login"/>
		<input type="password" name="password"/>
		<input type="hidden" name="roles[0].name" value="ADMIN"

		...
	</form>

Lembre que isso é facilmente atingível através da manipulação do HTML apresentado no navegador. E agora, o que vai acontecer? O Spring MVC vai fazer o bind do input cujo name é  roles[0].name com os métodos de acesso do atributo roles. Com isso, ao invés de você adicionar apenas um perfil associado com o novo usuário, você vai acabar inserindo dois e, ainda por cima, um deles talvez seja de administrador. Essa é uma técnica de invasão conhecida como Mass Assignment e que, inclusive, já foi explorada uma vez para hackearem o github.

Existem algumas formas de você se defender dela, mas a primeira é entender o motivo que você ficou exposto. Quase todos os frameworks que conhecemos permitem que associemos valores dos formulários diretamente com nossas classes de modelo, e aí é que mora o problema. Várias vezes criamos getters e setters indiscriminadamente e, como os mesmos são usados para receberem os valores dos parâmetros, o sistema acaba ficando suscetível a este tipo de situação. Então uma primeira saída é justamente prestar atenção nos métodos de acesso que criamos.

Só que de vez em quando, por conta de algum framework que está sendo utilizado, somos obrigados a criar mais métodos de acesso do que gostaríamos e aí é que entra uma segunda estratégia. Há muito tempo atrás o tão famoso Struts nos forçava sempre a criar uma classe que representasse o form para receber os dados enviados através das páginas e aí, só depois, que construíamos o objeto final. Era um pouco mais trabalhoso, mas em compensação nos fazia pensar com carinho para cada formulário que existisse no sistema.  Para esse nosso caso, poderíamos seguir essa sugestão.

	public class NewsletterUserForm {

		private String login;
		private String password;

		//getters e setters

		public SystemUser buildSystemUser(){
			SystemUser user = new SystemUser();
			user.setLogin(login);
			user.setPassword(password);
			user.addRole(new Role("ROLE_NEWS"));
			return user;
		}

	}

E agora podemos alterar o código do controller, para tratar do cadastro.

	@RequestMapping(method=RequestMethod.POST)
	public ModelAndView savePossibleBuyer(NewsletterUserForm formUser,...){
		userDAO.persist(formUser.buildSystemUser());
                ....
	}

Caso você ache que é um pouco exagerado já começar se protegendo desse jeito, pode usar um terceiro recurso, provido pelo próprio Spring MVC.

    @InitBinder
    protected void initBinder(WebDataBinder binder) {
	  binder.setAllowedFields("login","password");
    }

Lembre que o método anotado com @InitBinder é responsável por realizar configurações que serão aplicadas sobre os parâmetros que serão recebidos nos métodos do controller. Nesse caso, estamos dizendo que só aceitamos os parâmetros cujos nomes são login e password. Qualquer outro valor que seja passado será ignorado! A classe WebDataBinder ainda permite que você faça diversas outras configurações, por exemplo configurar uma validação customizada.

Pronto, desse jeito você se protege de receber valores indesejados e ainda consegue aproveitar sua classe de modelo entre vários formulários. E você, como faz para se proteger de valores não esperados? Tem alguma outra sugestão?