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!

Advertisements

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.

Prefira as tags de formulário do Spring MVC!

Uma das coisas que atrasa o desenvolvimento de um projeto é quando o programador resolve investir tempo para construir uma parte do software que já foi pensada por uma biblioteca ou framework. Quando pensamos no Spring MVC, um caso que me vem a mente são formulários que não são construídos usando as tags providas pelo framework.

    <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
    <%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>

      <div class="form-group">
          <input type="text" name="nome" value="${profissional.nome}"/>
      </div>
      <div class="form-group">
        <input type="text" name="cpf" value="${profissional.cpf}"/>          
      </div>
      <div class="form-group">
        <input type="text" name="dataNascimento" 
          value="<fmt:formatData value="${profissional.dataNascimento.time}" pattern="dd/MM/yyyy"/>
      </div>
      <div class="form-group">       
       <select name="estado">
        <c:forEach items="${estados}" var="estadoAtual">
<option value="${estadoAtual.value}" ${profissional.estado == estadoAtual ? 'selected' : ''}>${estadoAtual.nome}
          </option>
        </c:forEach>
       </select>
      </div>

Desenvolvendo dessa forma, vai fazer com que o programador perca tempo implementando toda aquela lógica de manter o estado dos campos nos erros de validação, ou quando o usuário tiver que entrar numa tela de edição. Só que fica pior, viu o código para manter o estado do  select? E para exibir data formatada?

Para todos os casos citados, você poderia usar as tags de formulário do Spring MVC. O mesmo form que foi mostrado acima, poderia ser escrito da seguinte forma:

    <%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
    <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
    <div class="form-group">                    
        <form:input path='nome' type='text'/>
        <form:errors path='nome'/>          
    </div>
   <div class="form-group">                    
        <form:input path='cpf' type='text'/>
        <form:errors path='cpf'/>          
    </div>
    <div class="form-group">                    
        <form:input path='dataNascimento' type='text'/>
        <form:errors path='dataNascimento'/>          
    </div>
    <div class="form-group">
      <form:select path='estado' itemLabel="nome" itemValue="value" items="${estadoList}"/>
      <form:errors path='estado'/>
    </div>

Você simplesmente usa as tags disponíveis referenciando as propriedades do seu objeto e deixa o Spring MVC fazer o trabalho dele. Por exemplo, ele já vai verificar o estado relacionado com seu objeto e deixar a opção escolhida para você. Só que ainda vai além, no campo de data ele vai usar a annotation de formatação que você usou no seu atributo e já vai exibir a data da maneira correta.

  public class Profissional {
      
     ...
     @DateTimeFormat(iso=ISO.DATE)
     private Calendar dataNascimento;
     @NotNull
     private Estado estado;

     ...
  }  

Isso tudo somado, poupa um bom tempo de desenvolvimento que pode ser investido para melhorar outras partes importantes do seu código.

Lembre de sempre tirar proveito do que o seu framework já possui, não gastando tempo escrevendo algo que já foi pensado e implementado por outras pessoas cujo foco era exatamente facilitar a sua vida! Como é o último dia do mês, faço aqui o meu jabá dizendo que isso e muitas outras coisas podem ser encontradas no meu livro na Casa do Código :).

Lidando com exceptions dentro do Spring MVC

Em qualquer projeto que participamos, uma das única coisas que temos certeza, é que uma exception vai acontecer. Tanto é que a especificação de Servlets já define uma maneira formal de fazer o tratamento das básicas.

  <error-page>
  	<error-code>404</error-code>
  	<location>/WEB-INF/views/errors/404.jsp</location>
  </error-page>

  <error-page>
  	<exception-type>java.lang.Exception</exception-type>
  	<location>/WEB-INF/views/errors/500.jsp</location>
  </error-page>

Só que, em geral, quase todo framework MVC que nós usamos já fornece um mecanismo alternativo de tratamento para as exceptions. Com o Spring MVC não podia ser diferente. E o legal é que as possibilidades oferecidas por ele vão além das fornecidas pela especificação. Por exemplo, no SetupMyProject quando uma pessoa tenta baixar um projeto com um token que não existe, é lançada uma exception do tipo NotFoundStatusException, indicando justamente o problema. Em um código convencional, exceptions apenas representam problemas, mas nesse caso também representa um status, que no caso é o 404.  Talvez a maneira mais direta de tratar ela seria da seguinte forma.

	public HttpEntity<byte[]> download(String token,
		HttpServletResponse response) {

		try {
                   //logica para buscar o setup e gerar o projeto
		} catch (NotFoundStatusException e) {
			response.setStatus(404);
            ModelAndView notFound = new ModelAndView("errors/404");
            notFound.addObject("message","the setup does not exist");
            return notFound;
	}

Só que esse código já nos obriga a receber um parâmetro a mais e adicionar um try/catch no nosso método. Caso deixemos ela estourar, vamos ter que mapear no web.xml, só que aí perdemos a chance de tratar como 404. E nesse caso, a página de 404 teria que ter uma mensagem customizada, como faríamos? Para este tipo de situação o Spring MVC já provê um saída.

    public HttpEntity<byte[]> download(String token,HttpServletResponse response) {
          //logica para buscar o setup e gerar o projeto
          return download;
    }

    public ModelAndView handleNotFoundStatusException
	 (NotFoundStatusException ex) {
	 ModelAndView modelAndView = modelAndViewForTokenExceptions(ex);
	 pageMessages.info("token.not_found",
	    "You need to generate a project before download", modelAndView);
	 return modelAndView;
     }

Perceba que basta você anotar o método com @ExceptionHandler e aí você pode receber a exception específica como argumento.  Aqui você já pode adicionar qualquer mensagem que você queira e direcionar para página que você necessite. Só que ainda podemos ir além, podemos informar ao Spring MVC que na verdade, essa exception deve gerar um status 404.

     @ResponseStatus(value=HttpStatus.NOT_FOUND)
     public class NotFoundStatusException extends RuntimeException
		implements RequestedSetupTokenAwareException {

           ...
     }

Você pode usar essa estratégia em qualquer controller seu! Um último ponto é: como você vai tratar aquelas exceptions que você não espera? Da maneira atual, teríamos que colocar um @ExceptionHandler em cada um dos controllers do sistema ou apelar para o modelo de aspectos suportado pelo framework. Para contornar isso, desde a versão 3.2, o Spring MVC possui uma annotation para indicar que certa classe concentra tratamentos globais relativos a um conjunto de controllers.

        @ControllerAdvice
	public class GlobalExceptionHandler {

		@Autowired
		private AccessEnvironment env;
		@Autowired
		private MailSender mailer;
		@Autowired
		private ThreadPoolTaskExecutor executor;

		private Logger logger = LoggerFactory
				.getLogger(GlobalExceptionHandler.class);

		@ExceptionHandler(value = Exception.class)
		public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e)
				throws Exception {

			SimpleMailMessage email = createBaseEmail();
			email.setText(ExceptionUtils.getStackTrace(e));

			executor.execute(() -> {
				logger.error("Enviando mensagem de erro");
				mailer.send(email);
			});

			return new ModelAndView("errors/500");
		}

Você pode usar a annotation @ControllerAdvice sempre que precisar concentrar algum tratamento que seria espalhado em todos os controllers. No nosso exemplo criamos um método anotado com @ExceptionHandler, mas nada nos impede de criar um método anotado com @InitBinder por exemplo. Agora, toda vez que um controller lançar uma exception, caso ninguém forneça um tratamento mais específico, ela vai cair no tratamento global do @ControllerAdvice. Um ponto a mais que merece ser observado é que o envio do email foi feito a partir de outra Thread. Isso é bem importante, já que você não quer fazer seu usuário esperar por conta de um envio de email.

Esse post foi um pouco mais curto e mais simples, entretanto tratar exceptions é uma parte muio importante do sistema. O melhor é que você trate as possibilidades de erro com o mesmo cuidado que você trata as possibilidades de sucesso nas lógicas, para que você forneça feedback positivo o tempo inteiro. Tanto para o usuário quanto para a própria parte interna da aplicação.

Push notifications com Server Sent Events na última versão do Spring

Hoje em dia usamos muitas aplicações onde ficamos na mesma tela por muito tempo. Por exemplo, ficamos na home do facebook e somos notificados de novos posts sem termos a necessidade de ficar atualizando a tela. A mesma coisa acontece na aplicação web do Whatsapp. Um bom questionamento é: como que essas aplicações conseguem ficar fazendo isso? Bem antigamente a técnica mais utilizada era realizar requisições ajax de tempos em tempos para o servidor. O grande problema era que o cliente ficava enviando inúmeros requests sem necessidade, obrigando o servidor a processar todos, sem mesmo ter uma necessidade.

A solução é até meio simples. Ao invés do cliente ficar perguntando para o servidor o tempo inteiro se chegou alguma mensagem, o servidor poderia notificar o cliente quando cada mensagem fosse chegando. Solução conhecida como push notification. Desse jeito o servidor não é inundado de requisições inúteis feitas pelo cliente e, além disso, o cliente é notificado quando realmente uma nova mensagem chega. Ao invés de ficar esperando um certo tempo para poder fazer uma nova requisição pedindo pelas novidades. Para a nossa sorte, o HTML5 especificou duas maneiras para possibilitar as notificações enviadas diretamente pelos servidores.

Provavelmente a mais conhecida é a WebSocket, onde tanto o cliente pode enviar mensagens para o servidor, quanto o servidor pode enviar mensagens para o cliente. Esse é o caso necessário para a aplicação web do Whastapp. Caso você a utilize, basta abrir a sua ferramenta favorita para inspecionar o navegador que você vai encontrar a url wss://w5.web.whatsapp.com/ws. É através dela que você envia e recebe as mensagens.

Agora vamos imaginar uma situação na Casa do Código. Precisamos adicionar uma funcionalidade onde o administrador da loja é capaz de surpreender os atuais visitantes com uma promoção relâmpago de algum livro. Perceba que não queremos que o navegador envie nenhuma mensagem para o servidor e sim, que apenas o servidor notifique o navegador de algum evento. Para este tipo de cenário, podemos uma outra especificação que veio no HTML5 chamada Server Sent Event. E agora a parte mais interessante, a última versão do Spring já vem com suporte nativo para essa outra modalidade de push notification. 

Vamos começar pela método do controller que é responsável pela geração da notificação.

        ...
        public class ProductsController {
         ...
         public static final SseEmitter notifier = new SseEmitter();

	 @RequestMapping("habilita/promocao/{id}")
	 @ResponseStatus(value=HttpStatus.OK)
	 public void enableQuickPromoForProduct(@PathVariable("id") Integer id) throws IOException{
		Product product = products.findOne(id);
		notifier.send(new QuickPromoData(product,messageSource));
	 }
        }

Buscamos um produto e geramos um outro objeto do tipo QuickPromoData. Esse objeto tem apenas as informações necessárias que devem ser enviadas para o cliente. O mesmo vai ser serializado como um JSON, então devemos ter cuidado para só expor o necessário e não ficar gerando mais dados do que precisamos. Sem contar que trabalhar diretamente com o objeto gerenciado pelo hibernate, pode nos levar a problemas de lazy load que não tem nada relacionado com a situação que estamos tratando aqui. É uma ótima prática sempre pensar com bastante carinho nos objetos que serão serializados, ao invés de ficar usando diretamente o objeto de domínio. Além disso estamos trabalhando com uma outra variável chamada notifier.  Ela é do tipo SseEmitter, justamente uma nova classe criada pelo Spring para que possamos enviar os dados para o navegador no protocolo exigido pelo SSE. 

E aqui precisamos parar um segundo para discutir o uso do objeto do tipo SseEmitter. Ele funciona como se fosse seu canal de comunicação com todos os clientes que estão conectados na aplicação naquele momento. Por conta disso precisamos manter o estado desse objeto entre as várias requisições. Na verdade não é nada complicado, basta que deixemos essa variável como estática no próprio controller. Caso isso te incomode você também pode deixá-la como variável de instância, já que todo controller por default vive no escopo da aplicação.

Agora que já notificamos os clientes, precisamos que eles abram a conexão com nosso servidor para que possam ser avisados das novas promoções.

    <script>
	var promoSource = new EventSource("${spring:mvcUrl('PC#enableQuickPromoNotifier').build()}");
	promoSource.onmessage = function(event) {
		var data = event.data;
		console.log(data)
	};
   </script>

A classe EventSource está disponível na maioria dos navegadores. O uso dela é bem direto, basta que instanciemos ele passando a url que abre a conexão que deve ser mantida para o recebimento de dados, também chamada de long pooling. Depois disso, associamos uma função que vai ser chamada sempre que nossa aplicação notificar o canal de comunicação. Para concluir, precisamos do método que trata o request solicitando a abertura da conexão.

     @RequestMapping("habilita/promocao")
     public SseEmitter enableQuickPromoNotifier(){
        return notifier;
     }

Perceba que apenas retornamos o objeto do tipo SseEmitter. Apenas reforçando que é importante que você mantenha o estado desse objeto. Você tem que utilizar a mesma instância retornada por este método para enviar as notificações.

Por fim é necessário que a versão do Spring atualizada no seu projeto. Nesse exemplo foi utilizado o update 4.2.0.RC1. Apenas para lembrar, os posts no blog se baseiam no projeto que está no github.

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.