Como você já sabe, VRaptor inclue alguns conversores básicos. Eles servem para as tarefas mais comuns, como conversão para String, Double ou Calendar.
Mas existem outros casos quando você quiser preencher o seu modelo com um objeto, para qual não existe um conversor ainda. Para essa tarefa você deve escrever o seu própio conversor.
Por exemplo, imagina se você quer instantiar um objeto categoria com uma sub categoria e VRaptor tem que preenche-la automaticamente quando a requisição foi enviada.
Então o seu modelo poderia ser:
public class Category { private Integer id; private String name; private Category subCategory; //getters e setters }
Na sua camada de apresentação usa um simples formulário com uma caixa de seleção para o id da sub categoria.
Como VRaptor sabe como criar o objeto subCategory? Ele não faz idea e joga uma ConversionException. Se você quiser VRaptor para instantiar a subCategory, você deveria providenciar o seu própio conversor.
Existem 3 passos para escrever o seu conversor:
Primeiro, criar uma classe que implementa org.vraptor.converter.Converter:
public class CategoryConverter implements Converter { }
Agora, adiciona o método public Class<?[] getSupportedTypes();> à classe. Esse método retorna um array de tipos que o conversor conhece e sabe tratar:
public class CategoryConverter implements Converter { public Class<?>[] getSupportedTypes() { return new Class[] { Category.class }; } }
E por último, você deve escrever o método de conversão:
/** * Converts a value to an specific type * * @param value * current value * @param type * desired type * @return the converted value * @throws ConversionException * some convertion problem hapenned */ public Object convert(String value, Class<?> type, LogicRequest request) throws ConversionException;
Se acontecesse uma exceção (ConversionException) durante a conversão, ela será jogada e dada para contêiner de servlet.
Se você não quiser criar nenhuma exceção, simplesmente retorne um valor padrão, nulo ou similar.
Por exemplo:
public Object convert(String categoryId, Class<?> type, LogicRequest request) throws ConversionException { Category category = new Category(); try { category.setId(Integer.parseInt(categoryId)); } catch (NumberFormatException e) { category = null; } return category; }
Se você quiser registrar um conversor que não foi instalado por padrão, use o arquivo vraptor.xml.
Depois ele estará disponível para o sistema inteiro (veja também o outro tutorial como sobrescrever os conversores básicos):
<vraptor> <converter>org.vraptor.examples.converter.CategoryConverter </converter> </vraptor>
Se um tipo pode ser convertido por dois conversores diferentes, utiliza-se o último registrado.
Quando o conversor é registrado, o Vraptor cria uma instância específica. Nenhuma outra jamais será criada. Por isso é importante que você nunca deixe variáveis para ler ou escrever no conversor.
Isso também significa que uma única instância do seu conversor deve ser seguro para threads: dois threads podem e chamarão o método do seu conversor ao mesmo tempo.
Nota: A anotação @Conversion descreve conversores que serão criados quando necessários. Esses conversores podem ficar no cache nas versões no futuro .... a regra básica é: Jamais deixe variáveis para leitura ou escrita nas suas classes de conversor.