Velocity ResourceLoader

Saca o Velocity? É um Template Engine para Java que é bem bacana. Resolvi usar ele no EncomendaZ 3, exatamente na parte de geração de e-mails. Fiz um template bonitinho mas também deixo livre para as pessoas criarem seus próprios. O meu próprio template, digamos, o template default, fica embarcado no próprio JAR. E o usuário pode ir lá e dizer um template próprio que, obviamente, estará fora do JAR. Estará no Filesystem dele.

Aí, quando você chega lá no Velocity e quer programar isso, surge um problema. O sacana pede que você informe qual a classe responsável em encontrar e carregar os recursos pra ele. Normalmente, você usa o ClasspathResourceLoader, que serve para encontrar os recursos dentro do próprio classpath. Mas, caso você queira obter arquivos do disco do cara, precisa informar o FileResourceLoader.

Aí é que está o problema. Quando você diz o FileResourceLoader, também precisa informar o file.resource.loader.path que é para ele ir buscar todos os resources de lá. Mas, peraí. Eu não quero fixar isso. O próprio usuário do programa vai informar onde está o arquivo que ele deseja que seja seu template. Aí, vamos para a mãe de todas as soluções em programação: uma leve gambiarra. Dá uma sacada no código aí embaixo. Ele está todo comentado!

public class Teste {
	private String getTemplate(final Tracking tracking, final Person person) {

                // Vamos ver se o usuário informou seu próprio template.
		String template = prefs.getEmailTemplate();

                // Iniciando o contexto o Velocity.
		VelocityContext context = new VelocityContext();

                // Colocando um objeto para ser usado pelo velocity para preencher os dados.
		context.put("encomenda", tracking);

                // Uma vez que o Velocity fez as devidas substituições no template, onde ele vai guardar?
                // No nosso caso, em um StringWriter.
		StringWriter w = new StringWriter();

                // Aqui é a questão. Se o cara não informou um template, então vem NULL ou vazio.
		final Properties properties = new Properties();
		if (template == null || "".equals(template)) {

                        // O cara não informou, então vamos usar o nosso que está no classpath.
			properties.setProperty(VelocityEngine.RESOURCE_LOADER, "classpath");
			properties.put("classpath." + Velocity.RESOURCE_LOADER + ".class", ClasspathResourceLoader.class.getName());

                        // Esse é o nosso template padrão que está no classpath.
			template = "email.vm";
		} else {

                        // Opa! O cara informou um template.
                        // O resourceloder é para um arquivo.
			properties.put("resource.loader", "file");

                        // A classe responsável em carregar os resources!
			properties.put("file.resource.loader.class",
				"org.apache.velocity.runtime.resource.loader.FileResourceLoader");

                        // O Path para encontrar os arquivos. A gambiarra é aqui.
                        // O método getPath() retorna apenas o diretório, removendo o nome do arquivo.
			properties.put("file.resource.loader.path", getPath(template));

                        // Vamos pegar agora só o nome do arquivo, tirando o diretório.
			template = template.substring(template.lastIndexOf('/') + 1);
		}

                // Precisamos sempre iniciar uma VelocityEngine. Se usarmos Velocity.init(), não dá certo.
                // Ela será global e todas chamadas subsequentes vão usar sempre a primeira configuração.
		VelocityEngine ve = new VelocityEngine();
		ve.init(properties);

		try {
                        // Manda fazer o Merge e Voila!
			ve.mergeTemplate(template, "UTF-8", context, w);
		} catch (Throwable t) {
			throw new EmailNotSentException();
		}
		return w.toString();
	}

	private Object getPath(final String template) {
		return template.substring(0, template.lastIndexOf('/'));
	}

}

Entenderam? A mágica acontece na hora de setar o file.resource.loader.path. Como o cara informou seu próprio arquivo, ele provavelmente estará em um lugar tipo c:/meustemplates/meutemplate.vm. Pegamos primeiro só a parte do diretório (c:/meustemplates/) e setamos como path. Depois, pegamos só a parte do arquivo. Gambiarra, né não? Mas funcionou, então, não reclame! 😛