6

I am using velocity 1.7 withe spring 3.1 framework for sending email. velocity is used for email templates.

Below is the configuration

<bean id="velocityEngine"
    class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
    <property name="velocityProperties">
        <props>
            <prop key="resource.loader">class</prop>
            <prop key="class.resource.loader.class">
                org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
            </prop>
        </props>
    </property>
</bean>

and below is my code

 @Component
 public class EmailUtils {

    @Autowired
    private static VelocityEngine velocityEngine;

    public static void sendMail(String subject, Map data, String template,
            String toName, String toAddress) {


        HtmlEmail email = new HtmlEmail();

        try {
            email.setHostName(hostName);
            email.setSmtpPort(smtpPort);
            email.setSubject(subject);

            System.out.println(template +" template");
            System.out.println(data +" data ");
            System.out.println(velocityEngine +" velocityEngine ");

            String message = VelocityEngineUtils.mergeTemplateIntoString(
                    velocityEngine, template, data);

            System.out.println(message +" message message ");

            email.setMsg(message);
            email.addTo(toAddress, toName);
            email.setFrom(fromAddress, fromName);
            email.send();
        } catch (EmailException e) {
            // TODO Auto-generated catch block

            e.printStackTrace();
        }
    }
}

When I run the application I get following error.

java.lang.NullPointerException
at org.springframework.ui.velocity.VelocityEngineUtils.mergeTemplate(VelocityEngineUtils.java:58)

as the velocity engine is null.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">

<bean id="velocityEngine"
    class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
    <property name="velocityProperties">
        <props>
            <prop key="resource.loader">class</prop>
            <prop key="class.resource.loader.class">
                org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
            </prop>
        </props>
    </property>
</bean>

Please help me. Is there any other configuration that I need to do?

skaffman
  • 398,947
  • 96
  • 818
  • 769
ajm
  • 12,863
  • 58
  • 163
  • 234
  • 1
    Have you properly configured Spring to use XML config, and also annotation config ? – ndeverge Mar 05 '12 at 07:48
  • what are you passing in for your template? I suspect there may be a problem there. – JasonG Mar 27 '12 at 12:20
  • Ashish, did you see nico_ekito's comment about annotation config? Do you have `` in you XML somewhere? It's not in your sample XML configuration snippets here. – Rup Mar 27 '12 at 14:52

4 Answers4

9

I had the same issue when using . I could have solved it in the following way:

@Configuration
public class ApplicationConfiguration {

    @Bean
    public VelocityEngine getVelocityEngine() throws VelocityException, IOException{
        VelocityEngineFactory factory = new VelocityEngineFactory();
        Properties props = new Properties();
        props.put("resource.loader", "class");
        props.put("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        factory.setVelocityProperties(props);
        return factory.createVelocityEngine();      
    }
}

But instead - because I was using velocity templates for my views and had already declared a VelocityConfigurer, so I instead Autowired in my declaration of VelocityConfigurer and called getVelocityEngine() on that.

I did discover that, if it's being autowired into @Service-s or @Component-s and you've declared in a shared applicationContext.xml file, then the xml declaration had to be in that shared applicationContext.xml as well rather than the xxx-servlet.xml config.

I think this is because applicationContext.xml is shared amongst all servlets in the application, so it has to be fully processed before the xxx-servlet.xml files are.

Alternatively, you could enable spring bean factory debugging and see if it's having any issues instantiating it:

log4j.category.org.springframework.beans.factory=DEBUG
ndtreviv
  • 3,473
  • 1
  • 31
  • 45
5

I think you cannot use @Autowired with a static field. You can work around this with a non-static setter:

@Autowired
public void setVelocityEngine(VelocityEngine ve) {
  EmailUtils.velocityEngine = ve;
}

or in another way but I would strongly recommend turning EmailUtils into a non-static bean. Spring handles non-static components much better (no ugly hacks needed), it will be possible to swap implementations and your code will stick to DI philosphy.

mrembisz
  • 12,722
  • 7
  • 36
  • 32
  • hi Thanks for the answer. I removed static as you said. Now I am getting No matching bean of type [org.apache.velocity.app.VelocityEngine] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. while deploying the application. Please help. – ajm Mar 05 '12 at 09:48
  • `VelocityEngineFactoryBean` produces object of type `VelocityEngine`. How do you indicate context files to be loaded? – mrembisz Mar 05 '12 at 10:10
  • Is your bean "velocityEngine" instantiated? You should see it listed if you enable INFO logging for "org.springframework". – mrembisz Mar 21 '12 at 09:23
  • No I dont see it in the list. But I can see EmailUtils in list. – ajm Mar 21 '12 at 09:31
  • Are you sure the file where it is defined is loaded at all? It should also be logged on INFO level. I see it as `INFO [XmlBeanDefinitionReader] Loading XML bean definitions from URL [file:/C:/Projects/my_project/springContext.xml]` – mrembisz Mar 21 '12 at 10:10
  • Yes I can see this for my velocity.xml . Its getting loaded I think. – ajm Mar 22 '12 at 04:20
  • Please post this entire log and code where you create spring context. – mrembisz Mar 22 '12 at 07:42
2

I had the same issue when I was upgrading our project to Java 11 + Spring Boot 2.1.8. I could have solved it in the following way:

@Configuration
public class AppConfig {
    @Bean
    public VelocityEngine velocityEngine() throws Exception {
        Properties properties = new Properties();
        properties.setProperty("input.encoding", "UTF-8");
        properties.setProperty("output.encoding", "UTF-8");
        properties.setProperty("resource.loader", "class");
        properties.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        VelocityEngine velocityEngine = new VelocityEngine(properties);
        return velocityEngine;
    }
}
Vasanth Umapathy
  • 1,571
  • 14
  • 7
1

This is my attempt, I initialize the "velocityEngine" bean manually via ClassPathXmlApplicationContext:

Spring application-context.xml

<bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
  <property name="velocityProperties">
    <value>
      resource.loader=class
      class.resource.loader.class=org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
    </value>
  </property>
</bean>

This is my template_text.vm, put it in the classpath, same level as application-context.xml.

Dear $userName,

You have made the following request on $userTime.

#if( $isFileMissing )
Missing file(s) found in home directory:
#foreach( $missingFile in $missingFiles )
- $missingFile
#end
#end

Best regards,

This is the Java code:

public static void main(String[] args) throws Exception {
    String[] springConfig = {"application-context.xml"};
    org.springframework.context.ApplicationContext context = new org.springframework.context.support.ClassPathXmlApplicationContext(springConfig);

    java.util.Map<String, Object> model = new java.util.HashMap<String, Object>();
    model.put("userName", "John Low");
    model.put("userTime", "2015-10-28 23:59:59");
    model.put("isFileMissing", true);
    model.put("missingFiles", new String[] {"line 1", "line 2", "line 3"});
    String text = org.springframework.ui.velocity.VelocityEngineUtils.mergeTemplateIntoString((VelocityEngine)context.getBean("velocityEngine"), "/template_text.vm", "UTF-8", model);
    System.out.println(text);
}

The output:

Dear John Low,

You have made the following request on 2015-10-28 23:59:59.

Missing file(s) found in home directory:
- file 1
- file AAA
- line 3

Best regards,
oraclesoon
  • 799
  • 8
  • 12