Using Velocity's WebappResourceLoader with Spring - spring

I'm trying to use Velocity to create an email template that is mailed by Spring's JavaMailSender class. The resource loader that I decided to use to find the Velocity template in my web app is WebappResourceLoader which is located in the Velocity tool jar.
However, depending on how I use the WebappResourceLoader, I get either a NPE when the web app starts up or the template can't be found.
If I specify the init properties for the Velocity engine in my Spring application context I get the NPE. My configuration is as follows:
<bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
<property name="velocityProperties">
<props>
<prop key="resource.loader">webapp</prop>
<prop key="webapp.resource.loader.class">org.apache.velocity.tools.view.WebappResourceLoader</prop>
<prop key="webapp.resource.loader.path">/WEB-INF/velocity/</prop>
</props>
</property>
</bean>
The stack trace I get when the app is starting up is:
java.lang.NullPointerException
at org.apache.velocity.tools.view.WebappResourceLoader.getResourceStream(WebappResourceLoader.java:145)
at org.apache.velocity.runtime.resource.loader.ResourceLoader.resourceExists(ResourceLoader.java:224)
at org.apache.velocity.runtime.resource.ResourceManagerImpl.getLoaderForResource(ResourceManagerImpl.java:641)
at org.apache.velocity.runtime.resource.ResourceManagerImpl.getLoaderNameForResource(ResourceManagerImpl.java:624)
at org.apache.velocity.runtime.RuntimeInstance.getLoaderNameForResource(RuntimeInstance.java:1464)
at org.apache.velocity.runtime.VelocimacroFactory.initVelocimacro(VelocimacroFactory.java:159)
at org.apache.velocity.runtime.RuntimeInstance.init(RuntimeInstance.java:261)
at org.apache.velocity.app.VelocityEngine.init(VelocityEngine.java:107)
at org.springframework.ui.velocity.VelocityEngineFactory.createVelocityEngine(VelocityEngineFactory.java:251)
at org.springframework.ui.velocity.VelocityEngineFactoryBean.afterPropertiesSet(VelocityEngineFactoryBean.java:57)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1460)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1398)
To fix this I changed my application context to as below:
<bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean" />
And then in the service class, right before the code to merge application data with the template (which is at /WEB-INF/velocity and named regemail.vm) I added the following code:
velocityEngine.addProperty("resource.loader", "webapp");
velocityEngine.addProperty("webapp.resource.loader.class", "org.apache.velocity.tools.view.WebappResourceLoader");
velocityEngine.addProperty("webapp.resource.loader.path", "/WEB-INF/velocity/");
velocityEngine.setApplicationAttribute("javax.servlet.ServletContext", "localhost:8080");
The app starts fine but when the email is going to be sent, I get the following error:
SEVERE: Servlet.service() for servlet default threw exception
org.apache.velocity.exception.ResourceNotFoundException: Unable to find resource 'regmail.vm'
at org.apache.velocity.runtime.resource.ResourceManagerImpl.loadResource(ResourceManagerImpl.java:483)
at org.apache.velocity.runtime.resource.ResourceManagerImpl.getResource(ResourceManagerImpl.java:354)
at org.apache.velocity.runtime.RuntimeInstance.getTemplate(RuntimeInstance.java:1400)
at org.apache.velocity.app.VelocityEngine.mergeTemplate(VelocityEngine.java:370)
at org.apache.velocity.app.VelocityEngine.mergeTemplate(VelocityEngine.java:345)
at org.springframework.ui.velocity.VelocityEngineUtils.mergeTemplate(VelocityEngineUtils.java:58)
at org.springframework.ui.velocity.VelocityEngineUtils.mergeTemplateIntoString(VelocityEngineUtils.java:122)
at com.mywebapp.web.service.RegistrationServiceImpl$1.prepare(RegistrationServiceImpl.java:60)
at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:353)
at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:345)
at com.mywebapp.web.service.RegistrationServiceImpl.sendRegEmail(RegistrationServiceImpl.java:65)
at com.mywebapp.web.controller.SignUpController.onSubmit(SignUpController.java:97)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
I'm using Velocity version 1.6.4 and Velocity tools 2.0. Any help much appreciated. Thanks!

You need to pass ServletContext object:
velocityEngine.setApplicationAttribute("javax.servlet.ServletContext", request.getSession().getServletContext());
Another option would be using ClasspathResourceLoader instead and putting your templates within classpath.

If you look at the documentation for VelocityEngineFactoryBean, you'll notice that there's a 'resourceLoaderPath' property that you can set. Given your configuration above, it looks like you've placed your Velocity templates under /WEB-INF/velocity/, so use that as the value for 'resourceLoaderPath' and the factory bean should load your templates just fine.

Three years later, but here's another solution that does not require to adapt your Java code to pass the ServletContext to Velocity Engine.
Simply use VelocityConfigurer instead of VelocityEngineFactoryBean in your Spring Context file like this:
<bean id="velocityConfig" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
<property name="velocityPropertiesMap">
<map>
<entry key="runtime.log.invalid.reference"><value>true</value></entry>
<entry key="runtime.log.logsystem.class"><value>org.apache.velocity.runtime.log.Log4JLogChute</value></entry>
<entry key="runtime.log.logsystem.log4j.logger"><value>velocity</value></entry>
<entry key="input.encoding"><value>UTF-8</value></entry>
<entry key="output.encoding"><value>UTF-8</value></entry>
<entry key="directive.include.output.errormsg.start"><value></value></entry>
<entry key="directive.parse.max.depth"><value>10</value></entry>
<entry key="directive.set.null.allowed"><value>true</value></entry>
<entry key="velocimacro.library.autoreload"><value>true</value></entry>
<entry key="velocimacro.permissions.allow.inline"><value>true</value></entry>
<entry key="velocimacro.permissions.allow.inline.to.replace.global"><value>false</value></entry>
<entry key="velocimacro.permissions.allow.inline.local.scope"><value>false</value></entry>
<entry key="velocimacro.context.localscope"><value>false</value></entry>
<entry key="runtime.interpolate.string.literals"><value>true</value></entry>
<entry key="resource.manager.class"><value>org.apache.velocity.runtime.resource.ResourceManagerImpl</value></entry>
<entry key="resource.manager.cache.class"><value>org.apache.velocity.runtime.resource.ResourceCacheImpl</value></entry>
<entry key="resource.loader"><value>webapp, class, ds</value></entry>
<entry key="class.resource.loader.description"><value>Velocity Classpath Resource Loader</value></entry>
<entry key="class.resource.loader.class"><value>org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader</value></entry>
<entry key="webapp.resource.loader.class"><value>org.apache.velocity.tools.view.WebappResourceLoader</value></entry>
<entry key="webapp.resource.loader.path"><value>/WEB-INF/views/</value></entry>
<entry key="webapp.resource.loader.cache"><value>false</value></entry>
<entry key="webapp.resource.loader.modificationCheckInterval"><value>2</value></entry>
<entry key="ds.resource.loader.instance"><ref bean="templateLoader"/></entry>
<entry key="ds.resource.loader.resource.table"><value>templates</value></entry>
<entry key="ds.resource.loader.resource.keycolumn"><value>code</value></entry>
<entry key="ds.resource.loader.resource.templatecolumn"><value>content</value></entry>
<entry key="ds.resource.loader.resource.timestampcolumn"><value>updated</value></entry>
<entry key="ds.resource.loader.cache"><value>false</value></entry>
</map>
</property>
</bean>
<bean id="templateLoader"
class="org.apache.velocity.runtime.resource.loader.DataSourceResourceLoader">
<property name="dataSource" ref="yourDataSource"></property>
</bean>

Related

Put a spring bean into the Application scope?

This is related this question:
I need to inject a Spring bean into the Tomcat application's scope at startup:
<beans:bean id="myUrl" class="java.lang.String" >
<beans:constructor-arg type="java.lang.String">
<beans:value>${my.registry.location:some.url}</beans:value>
</beans:constructor-arg>
</beans:bean>
Is there a slick way to do this from the Spring XML configuration or do I need to override some servlet method (ugh)?
I thought I would post this in case it helps someone. Spring has a bean that will do this on initialization for you: ServletContextAttributeExporter.
You use it like this:
<bean id="myBean" class="java.lang.String" >
<constructor-arg type="java.lang.String">
<value>${some.property.value}</value>
</constructor-arg>
</bean>
<bean class="org.springframework.web.context.support.ServletContextAttributeExporter">
<property name="attributes">
<map>
<entry key="myBean" value-ref="myBean" />
</map>
</property>
</bean>
You can then do something like this in your non-Spring aware JSP code (for example):
<div>
Value of my bean is: ${applicationScope.myBean}
</div>

DeadLock In Spring-Integration Application

I have a problem with a Legacy Application.
The application have two context, a Spring MVC context and a Spring Integration context.
The Spring MVC and the Spring Integration have two separates Entity Manager, but uses the same #Query repositories.
The application have and high load database access (for Read, Write and Updates) because receive millions of JMS messages all the days, and sometimes a DEADLOCK occurred.
If I put #Lock(OPTIMISTIC) in all the Querys in the repository, the problem is solved, but the Web application stop working “requiredTransactionException” says, this is normal because #Lock requires a Transaction and the MVC context don’t use transactions.
The question is, ¿How can I specify the #Lock in my Spring-Integration entity-manager-factory?
This is my Spring-Integration Entity Manager:
<bean id="entity-manager-factory" parent="entity-manager-factory-parent" depends-on="springJtaPlatformAdapter">
<property name="dataSource" ref="dataSourceInt" />
<property name="jpaPropertyMap">
<map>
<entry key="javax.persistence.transactionType" value="JTA" />
<entry key="hibernate.current_session_context_class" value="jta" />
<entry key="hibernate.transaction.jta.platform" value="XXXXXXXXX (InternalClass, I Can't show name)" />
<entry key="hibernate.connection.autocommit" value="false" />
</map>
</property>
EDIT:
The parent entity manager:
<bean id="entity-manager-factory-parent" abstract="true"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="packagesToScan" value="es.com.bbdd.entities" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false" />
<property name="databasePlatform" value="org.hibernate.dialect.Oracle10gDialect" />
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.default_schema">SALES_SCHEMA</prop>
</props>
</property>
Problem solved. Thanks all the people for the answers.
The problem was that tablas are partitioned by foreign key reference , and the FK are not indexed , causing deadlocks by Oracle that blocks all the child table.
I have created all the indexes for the foreign keys and the problem is solved.

disable (or change port) for web interface of neo4j embedded?

I have a number of small web service projects which use embedded neo4j 2.x.
Any combination of them might be deployed on a single tomcat instance, and therefore I need them NOT to all attempt to start a web service on port 7474.
Acceptable solution is either to disable web interface on all of them, or have each use a separate port. Is the first option doable via Spring configuration? If so, how?
I've tried with a config map:
<util:map id="config">
<entry key="enable_remote_shell" value="false" />
<entry key="org.neo4j.server.webserver.port" value="7475" />
</util:map>
<bean id="graphDbFactory" class="org.neo4j.graphdb.factory.GraphDatabaseFactory" />
<bean id="graphDbBuilder" factory-bean="graphDbFactory"
factory-method="newEmbeddedDatabaseBuilder">
<constructor-arg value="${neo4j.database.path}" />
</bean>
<bean id="graphDbBuilderFinal" factory-bean="graphDbBuilder"
factory-method="setConfig">
<constructor-arg ref="config" />
</bean>
<bean id="graphDatabaseService" factory-bean="graphDbBuilderFinal"
factory-method="newGraphDatabase" destroy-method="shutdown" />
<bean id="serverWrapper" class="org.neo4j.server.WrappingNeoServerBootstrapper"
init-method="start" destroy-method="stop">
<constructor-arg ref="graphDatabaseService" />
</bean>
but there's Jetty still binding the web service to port 7474... so I guess that
disabling the remote shell doesn't also disable the web admin interface?
org.neo4j.server.webserver.port is the wrong key, or
i've got the wrong recipe for the configuration (although this one seems consistent with all the neo4j 2.x Spring configuration examples I can find online)
If not, what do I set in the configuration to give each a distinct port number for the web interface? Thanks.
Remove this part
<bean id="serverWrapper" class="org.neo4j.server.WrappingNeoServerBootstrapper"
init-method="start" destroy-method="stop">
<constructor-arg ref="graphDatabaseService" />
</bean>

spring web flow enable scopes

Spring web flow provides additional bean scopes like flow, conversation, flash etc. I can define flow scope beans in flow.xml using var or i can set values to new scoped variables. How i can define it in spring application context xml file. I tried to use this pattern:
<bean id="abc" class="abc" scope="flow"/>
I got error that no scope defined. I searched on google and found this thing
http://blog.springsource.org/2007/05/08/spring-web-flow-bean-scopes-and-jsf/
but don't know how to enable it in spring web flow 2.3
try to define it in your application context:
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="flow">
<bean class="org.springframework.webflow.config.scope.FlowScope"/>
</entry>
</map>
</property>
</bean>

Spring Vs Struts + Freemarker

For a Web application If I've choices between Spring and Struts to use with Freemarker, which one go well, Or I would rather ask, which MVC framework integrates smoothly with Freemarker?
The Spring framework provides everything you need to use FreeMarker for your view layer.
Both have pretty good freemarker support. Its easy to turn on.
Struts2 is a little more pojo based. Spring is a little closer to the servlet api. Spring's default macros in spring.ftl need a little work and you will likely need to roll your own. Some of the macros blow up if an object is not present rather than gracefully testing for it and moving on if it is not there.
I like Spring's application of validation via annotations better than Struts 2 default validation. However, persisting validation errors over redirects is easier in Struts2. For Spring you'll end up needing to roll your own solution where I feel the framework should hide more of that. Needing to use the error prone spring.bind macro with freemarker templates is more cumbersome than it needs to be.
Spring 3.1 is supposed to provide better support for this validation errors living over redirects.
Also note, with Spring I typically use more than one view resolver. e.g. I still leaving support for .jsp on.
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="html" value="text/html"/>
<entry key="ftl" value="text/html"/>
<entry key="xml" value="application/xml"/>
<entry key="json" value="application/json"/>
</map>
</property>
<property name="favorPathExtension" value="true"/>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView"/>
</list>
</property>
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="cache" value="true"/>
<property name="order" value="1"/>
<property name="prefix" value="/"/>
<property name="suffix" value=".ftl"/>
<property name="contentType" value="text/html;charset=UTF-8"/>
<property name="exposeSpringMacroHelpers" value="true"/>
<property name="requestContextAttribute" value="rc"/>
<property name="exposeSessionAttributes" value="true"/>
<property name="exposeRequestAttributes" value="true"/>
</bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
</list>
</property>
</bean>

Resources