Can you configure velocity to use a toolbox file from the classpath? - spring

I'm using spring with a VelocityViewResolver configured by Spring
<bean id="velocityViewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
<property name="order" value="1" />
<property name="suffix" value=".vm" />
<property name="contentType" value="text/html;charset=UTF-8" />
<property name="exposeSpringMacroHelpers" value="true" />
<property name="toolboxConfigLocation" value="/WEB-INF/velocityToolbox.xml" />
</bean>
This works if the velocityToolbox.xml file is under WEB-INF ; however I'm trying to put it in the classpath. Is there a way to do it ?
I've tried specifying toolboxConfigLocation as "classpath:/wherever/velocityToolbox.xml", but it does not find the resource, and I end up with no toolbox configured, and a NPE at runtime (for some reason, it seems like the code expect the location to start with a '/', or add the '/' itself before looking for the resource).
Given that the resource is located using ServletContext.getResourceAsStream, with the content of the toolboxConfigLocation property prefixed by a "/", is there a way I can "define" a resource in my spring config that would somehow 'point' to an actuall classpath resource ?
Any idea is welcome.

I believe it has to be a full path, eg:
/WEB-INF/classes/com/myapp/resources/toolbox.xml
The ServletToolboxManager#getInstance(ServletContext context, String toolboxConfigLocation) method is used to generate a ToolboxManager configured with the toolbox in order to create the ChainedContext used in the VelocityToolboxView.
This method pre-appends the path with a '/' if one does not exist, and then uses ServletContext#getResourceAsStream(String path) to read it in.
With this in mind, you'll have success if you set it as a full path from the context root.

Related

FileReadingMessageSource.WatchServiceDirectoryScanner: turn off recursive descent into sub-directories?

Versions:
Spring: 5.2.16.RELEASE
Spring Integrations: 5.3.9.RELEASE
macOS Big Sur: 11.6
I am using XML to set up the directory scanner FileReadingMessageSource.WatchServiceDirectoryScanner like so:
<int-file:inbound-channel-adapter id="channelIn" directory="${channel.dir}" auto-create-directory="false" use-watch-service="true" filter="channelFilter" watch-events="CREATE,MODIFY">
<int-file:nio-locker ref="channelLocker"/>
<int:poller fixed-delay="${channel.polling.delay}" max-messages-per-poll="${channel.polling.maxmsgs}"></int:poller>
</int-file:inbound-channel-adapter>
with the following bean definitions:
<bean id="channelLocker" class="org.springframework.integration.file.locking.NioFileLocker"/>
<bean id="channelFilter" class="org.springframework.integration.file.filters.ChainFileListFilter">
<constructor-arg>
<list>
<bean class="org.springframework.integration.file.filters.SimplePatternFileListFilter">
<constructor-arg value="SpreadSheets*.xls" />
</bean>
<bean id="filter" class="org.springframework.integration.file.filters.LastModifiedFileListFilter">
<property name="age" value="${channel.filter.age}" />
</bean>
<ref bean="persistentFilter" />
</list>
</constructor-arg>
</bean>
<bean id="persistentFilter" class="org.springframework.integration.file.filters.FileSystemPersistentAcceptOnceFileListFilter">
<constructor-arg index="0" ref="metadataStore" />
<constructor-arg index="1" name="prefix" value="" />
<property name="flushOnUpdate" value="false" />
</bean>
If I look at logs for org.springframework.integration.file.FileReadingMessageSource, I notice that we register both the specified directory (ie, ${channel.dir}) as well as any of its sub-directories. That is, I see logs like this:
15:44:45.706 [main] DEBUG org.springframework.integration.file.FileReadingMessageSource - registering: /Users/kc/scan.here for file events
15:44:45.711 [main] DEBUG org.springframework.integration.file.FileReadingMessageSource - registering: /Users/kc/scan.here/and.here for file events
I've looked Spring docs as well as API docs for the relevant software modules (Eg, FileReadingMessageSource), but I don't see any property or configuration option for turning off recursive descent into sub-directories.
What is the recommended practice here for scanning only files within the specified directory, but not recursing any deeper than that?
If you don't a recursion and scan the whole file tree, just don't use that watch service!
For the create and modify events you can configure a FileSystemPersistentAcceptOnceFileListFilter which checks for the file.lastModified(). I see you do that anyway, therefore it is not clear why do you need a watch service at all?
See some related discussed here: https://github.com/spring-projects/spring-integration/issues/3557.
If you still have some reasonable argument to use watch service for only a root dir, please add a comment into that issue and we will revise it respectively.

Can not load property file in spring-context.xml. Property file Path is given as a placeholder in dev.properties

I am loading property file in spring-context.xml and i am giving
external property file location in
${spring.profiles.active}.properties which is in classpath and using the location as a placholder in spring-context.xml. My spring-context.xml is:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="true" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="searchSystemEnvironment" value="true" />
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="locations" ref="propertyConfigurerFiles" />
</bean>
<bean id="propertyConfigurerFiles" class="java.util.ArrayList">
<constructor-arg>
<list>
<value>/WEB-INF/properties/common.properties</value>
<!--In Developemnet Enviroenment it will be dev.properties-->
<value>/WEB-INF/properties/${spring.profiles.active}.properties</value>
<!--External Property File Location as a Placeholder-->
<value>${app.config.batch.location}</value>
</list>
</constructor-arg>
</bean>
And my dev.properties is:
app.config.batch.location=E:/project/properties/config.properties
My problem is that is ${app.config.batch.location} placeholder is not
resolved in spring-context.xml and its trying to load file
${app.config.batch.location} in place of
E:/project/properties/config.properties.
I hope I explained the problem well. Please help!
Thanks in Advance!!!
You need to create bean of class PropertyPlaceHolderConfigurer.
Not just some ArrayList bean. Why do you think you need this ArrayList bean?
It seems you are using spring profiles, instead of messing with initialization time property value binding what you can do is ...
1) read the property file(profile's)
/WEB-INF/properties/${spring.profiles.active}.properties
2) create a java class that can read these property values. (don't forget to use spring profiles interfacing class)
3) as you are trying to read a property file whose location is embedded in property file(step-1), object created at step-2 will give value for key <value>${app.config.batch.location}</value>
now you can load this property file using available file reader class.
4) create Properties object and access the values in it.
Note:: if any of your bean initialization depends on key-value read at step-4, do initialization manually or create your ***custom class(servlet) that get loaded before any other class (even spring's DispactherServlet).

Use environment variable in Spring beans.xml

I have a peculiar requirement of setting the value of some bean from system environment variable , is this thing possible in spring ?
Pseudo code
<bean id="connectionFactory"
class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
<constructor-arg value="testhost" />
<property name="username" value="$SystemEnv.RabbitUserName" />
<property name="password" value="$SystemEnv.RabbitPassword" />
</bean>
Now these RabbitUserName and RabbitPassword are externalized in some environmental variable which is outside .war file .
Is there any elegant way to achieve the same in spring ?
To get the System Environment Variable you can use this for the value:
#{ systemEnvironment['RabbitUserName'] }
The #{ systemProperties['RabbitUserName'] } mentioned in another answer won't give you a System Environment Variable, rather it will read a Java system property ie, those things you can set on the command line when starting the JVM, eg:
java -DRabbitUserName="reisen" etc...
However, this is probably a better choice of place for a connection credential than an environment variable, as, for example, you can then configure it differently for different launch configurations and it is also slightly less exposed than an environment value - which all the other applications also have full visibility on.
For more information on the difference between environent variables and system properties see Java system properties and environment variables
These are examples of the Spring Expression Language (SpEL), and you can also use it with annotation metadata, for example
#Value("#{ systemEnvironment['RabbitUserName'] }")
For more information on SpEL you can take a look at the Spring reference here: https://docs.spring.io/spring/docs/4.0.x/spring-framework-reference/html/expressions.html
This should work.
<bean id="connectionFactory"
class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
<constructor-arg value="testhost" />
<property name="username" value="#{systemProperties['RabbitUserName']}" />
<property name="password" value="#{systemProperties['RabbitPassword']}" />
</bean>

Update Views With Spring MVC And Thymeleaf Without Redeploy The App

I am using Spring MVC 3.2, Thymeleaf, Thymeleaf dialect with Tomcat and every time that I change a view I don't want redeploy my app. As suggested from others I am setting:
<property name="cacheable" value="false"/>
in the template resolver, but it not working.
Also a full reload (CTRL + F5) has not effect.
Here my full Thymeleaf configuration:
<!-- Thymeleaf template engine -->
<bean id="templateResolver"
class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
<property name="prefix" value="/WEB-INF/templates/" />
<property name="suffix" value=".html" />
<property name="templateMode" value="HTML5" />
<property name="cacheable" value="false"/>
</bean>
<bean id="templateEngine"
class="org.thymeleaf.spring3.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver" />
<!-- These lines add the dialect to Thymeleaf -->
<property name="additionalDialects">
<set>
<bean class="nz.net.ultraq.thymeleaf.LayoutDialect"/>
</set>
</property>
</bean>
<bean class="org.thymeleaf.spring3.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine" />
</bean>
Is there something else that I can do to resolve this issue?
It depends on many many things and mainly on "where and how" you update your view. If you are using a IDE (Netbeans or Eclipse) it main depend on the IDE itself and on the deployement process.
Current organization on disk :
Source directories => [ on build ] => target or build directories => [ on deploy ] => tomcat directories
But IDE often tries to be developper's friendly and it can happen that target directories and tomcat directories are the same. But it also may depend of the IDE and its configuration.
Also when you save files under source webapp, the IDE may automatically copy them to target.
But this is not guaranteed by <property name="cacheable" value="false"/> of Thymeleaf config. All what it guarantees is that if a template is changed in tomcat directories, next request will use it.
So to be sure where the problem really comes, you will have to find where tomcat actually gets the templates and if those templates are modified.
(and I didn't even talked of browsers cache ...)
In Tomcat "Server Options", check "Serve modules without publishing" did the trick for me.

Spring PropertyPlaceholderConfigurer not replacing placeholder

I want to pass the WSDL url for an internal web service into my Spring beans.xml dynamically, using a PropertyPlaceHolderConfigurer.
Here's the scenario:
My web application is deployed in WebLogic 10.3.
The WSDL url is contained in a properties file that is located outside my application (directly under the corresponding domain folder, while my application is inside the autodeploy folder). I set the location of this properties file in my domain's setDomainEnv.cmd file like below:
set JAVA_PROPERTIES=%JAVA_PROPERTIES% %CLUSTER_PROPERTIES% -Dproperty.file.path.config=%DOMAIN_HOME%\Service.properties
This is what my Service.properties file contains:
Service.WSDL.PATH=http://localhost:8088/mockServiceSoap?WSDL
My Spring beans.xml configuration:----
<bean id="file.path" class="java.lang.System" factory-method="getProperty">
<constructor-arg index="0"><value>property.file.path.config</value></constructor-arg>
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" ref="file.path"/>
</bean>
<bean id="myServiceId" class="com.test.service.ServiceImpl">
<property name="myServiceSoap">
<ref bean="myService"/>
</property>
</bean>
<bean id="myService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
<property name="serviceInterface" value="com.test.service.ServiceSoap"/>
<property name="wsdlDocumentUrl" value="${Service.WSDL.PATH}"/>
</bean>
I enabled DEBUG log specifically for PPC and this is what I saw in my application log:
INFO org.springframework.beans.factory.config.PropertyPlaceholderConfigurer 178 - Loading properties file from URL [file:D:/bea10.3/user_projects/domains/my_domain/Service.properties]
So, it seems that although the Service.properties file is getting loaded by PPC, the ${Service.WSDL.PATH} is NOT getting replaced.
What am I doing wrong here?
Also, how can I find out if PPC tried replacing the value of the placeholder and with what value? I was hoping the log file would contain that info but there was nothing there.
Any help is appreciated.
I've figured out, that PropertyPlaceholderConfigurer needs to be declared first in the application Context file, otherwise there's no guarantee of the load order. It took me a few hours to realize this.
Try moving the "file.path" bean into the PropertyPlaceHolderConfigurer's location property.

Resources