Spring FreeMarkerConfiguration Template Loader Path overwritten by Configuration - spring

I have a Java based web app running with Spring 4. I'm using FreeMarker for my web templates. My WebConfig class uses a FreeMarkerConfigurer object to set up my template loader path and some other settings as so:
#Bean
public FreeMarkerConfigurer freemarkerConfig() throws IOException {
FreeMarkerConfigurer fmc = new FreeMarkerConfigurer();
fmc.setTemplateLoaderPath("/WEB-INF/templates");
fmc.setDefaultEncoding("UTF-8");
Properties settings = new Properties();
settings.setProperty("date_format", "dd MMM yyyy");
fmc.setFreemarkerSettings(settings);
return fmc;
}
This works fine: views resolve correctly, including any <#import 'spring.ftl' as spring> directives which can be loaded from the jar.
Now I want to customise my application more, for example setting a custom template exception handler. I've created a FreeMarker Configuration object and added this to the FreeMarkerConfigurer:
Configuration cfg = new Configuration();
cfg.setTemplateExceptionHandler(new MyTemplateExceptionHandler());
fmc.setConfiguration(cfg);
But this stops my views resolving - it seems to overwrite the TemplateLoaderPath from the FreeMarkerConfigurer object and I have to explicitly set the path in the Configuration instance instead for my templates to be located by the project:
cfg.setDirectoryForTemplateLoading(new File("C:/myProject/WEB-INF/templates"));
Additionally, spring.ftl is no longer loaded from the jar and I have to put a copy in my template directory for it to be loaded.
Why does adding a Configuration to a FreeMarkerConfigurer overwrite my template loader path? Is my setup incorrect or does a Configuration have greater priority?

FreeMarkerConfigurer is just a factory that helps to build a freemarker.template.Configuration and exposes it with its method getConfiguration().
If you want to further configurate the configuration, extract it first from your configurer, because there will be only one Configuration used by FreeMarker.

Related

Does ehcache 3.8.1 still use ehcache.xml for configuration?

Does ehcache 3.8.1 no longer automatically pick up the configuration settings in an ehcache.xml file at the source root directory?
Yes, it's looks so, now it needs to be done using a XML file by configuringe a CacheManager at creation time, according to this schema definition.
XML programmatic parsing
If you are obtaining your CacheManager through the JSR-107 API, what follows is done automatically when invoking
javax.cache.spi.CachingProvider.getCacheManager(java.net.URI, java.lang.ClassLoader)
final URL myUrl = getClass().getResource("/configs/docs/getting-started.xml");
XmlConfiguration xmlConfig = new XmlConfiguration(myUrl);
CacheManager myCacheManager = CacheManagerBuilder.newCacheManager(xmlConfig);
myCacheManager.init();
Obtain a URL to your XML file’s location
Instantiate an XmlConfiguration passing the XML file’s URL to it
Using the static
org.ehcache.config.builders.CacheManagerBuilder.newCacheManager(org.ehcache.config.Configuration)
allows you to create your CacheManager instance using the Configuration from the XmlConfiguration
Initialize the cacheManager before it is used.
Reference - http://www.ehcache.org/documentation/3.8/xml.html

Spring boot : load profile specific application.properties from file system

I am trying to load external properties files based on the current active profile
and the properties files I have defined as below :
default -> resources/config/application.properties (for dev)
qa -> c:\external-configuration\config\application-qa.properties
prod -> c:\external-configuration\config\application-prod.properties
how spring can be configured to read all these application*.properties from different sources?
I tried to define a PropertySourcesPlaceholderConfigurer as below but spring could resolve the properties value based on the active profile, I always get the default values defined in application.properties
#Bean
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer properties = new PropertySourcesPlaceholderConfigurer();
properties.setLocation(new FileSystemResource("c:\external-configuration\config\application-qa.properties"),new FileSystemResource("c:\external-configuration\config\application-prod.properties"));
properties.setIgnoreResourceNotFound(false);
return properties;
}
First specify which profile you want to load with spring.profiles.active. Secondly, as it isn't one of the default locations, add spring.config.additional-location to add additional locations to scan. So when you start your line should look like
java -jar <your-jar>.jar --spring.profiles.active=prod --spring.config.additional-location=file:C:/external-configuration/config/
This is also documented in the Spring Boot documentation.
And remove your custom PropertySourcesPlaceholderConfigurer as that isn't needed.
You can also use java annotation for property resource and use server environment (active profile) to identify which properties file to load.
Like, this code snippet here, it will look for property 'envTarget' and in case its not found or null, it will use default 'qa':
#PropertySource({
"classpath:application-${envTarget:qa}.properties"
})

Spring Reload Properties on Demand

I am looking to load properties from file On Demand in my spring boot application.
I have a requirement to load the properties from the file when there is a change in the properties file.
I am using #Configuration properties to load the properties, by setting the spring.config.location=file:/../test-properties.yml.
Bean class that holds the data
#Configuration
#ConfigurationProperties
public class GetValues {
List<String> values = new ArrayList<String>();
//getters and setters
}
test-properties.yml
values:
- X1
- X2
- X3
I have a rest service to load the data loaded from properties file to backend, within which I am Autowiring the GetValues Bean.
Whenever there is a change in the property file, I will call this service which would load the properties to the Backend.
Right Now, It is loading the same properties everytime, as the properties are already loaded in to the context.
Is there a way to reload the properties from the file everytime I hit the service? I don't want to use the Spring-Cloud, #RefreshScope.
There are two solutions
Using spring devtools you can achieve auto restart when config file change. Only desirable in development environment.
Make that when you hit the service the properties file get loaded, and update your config class attributes. Abstract:
Properties prop = new Properties();
InputStream input = null;
input = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileNameProperties);
prop.load(input);
bean.setValue(prop.getProperty("attribute1"));
If you don't want to use Spring Cloud Config / #RefreshScope, one option would be to the usage of a FileChangeListener / WatchService found in Apache commons (or similar library) or JDK 7 and reload the properties. Make sure you synchronize the usage of the loaded properties and updating them when the file changes, maybe through a ReentrantReadWriteLock.

how to manage spring-cloud bootstrap properties in a shared library?

I'm in the process of building a library which provides an opinionated configuration for applications which use our Spring Cloud Config/Eureka setup. The idea is to deliver this configuration as a custom starter with little or no spring cloud-related boilerplate in individual microservice apps.
At this point, the majority of the shared configuration that I want to put in this library consists of stuff in bootstrap.yml. I'd like to provide bootstrap.yml in my custom starter, but applications using the library still need to be able to provide their own bootstrap.yml, even if only so they can set their spring.application.name properly.
Due to the way bootstrap.yml is loaded from the classpath, Spring seems to ignore the one in the shared lib if the application has its own bootstrap.yml. I can't even use an ApplicationContextInitializer to customize the Environment because of the special way the bootstrap context treats ApplicationContextInitializers.
Does anyone have any recommendations for an approach that would work here? I want to provide a drop-in lib that makes our opinionated bootstrap config work without having to duplicate a boilerplate bootstrap.yml in all of our projects.
You can add a PropertySource in a shared library to the bootstrap properties by using the org.springframework.cloud.bootstrap.BootstrapConfiguration key in the META-INF/spring.factories file.
For example, you can create a library containing the following:
src/main/java/com/example/mylib/MyLibConfig.java
package com.example.mylib;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
#Configuration
#PropertySource("classpath:mylib-config.properties")
public class MyLibConfig {
}
src/main/resources/mylib-config.properties
eureka.instance.public=true
# or whatever...
src/main/resources/META-INF/spring.factories
org.springframework.cloud.bootstrap.BootstrapConfiguration=com.example.mylib.MyLibConfig
More details: http://projects.spring.io/spring-cloud/spring-cloud.html#_customizing_the_bootstrap_configuration
I was able to find a solution to this. The goals of this solution are:
Load the values from a yaml file in a shared library.
Allow applications using the library to introduce their own bootstrap.yml that is also loaded into the Environment.
Values in the bootstrap.yml should override values in the shared yaml.
The main challenge is to inject some code at the appropriate point in the application lifecycle. Specifically, we need to do it after the bootstrap.yml PropertySource is added to the environment (so that we can inject our custom PropertySource in the correct order relative to it), but also before the application starts configuring beans (as our config values control behavior).
The solution I found was to use a custom EnvironmentPostProcessor
public class CloudyConfigEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
private YamlPropertySourceLoader loader;
public CloudyConfigEnvironmentPostProcessor() {
loader = new YamlPropertySourceLoader();
}
#Override
public void postProcessEnvironment(ConfigurableEnvironment env, SpringApplication application) {
//ensure that the bootstrap file is only loaded in the bootstrap context
if (env.getPropertySources().contains("bootstrap")) {
//Each document in the multi-document yaml must be loaded separately.
//Start by loading the no-profile configs...
loadProfile("cloudy-bootstrap", env, null);
//Then loop through the active profiles and load them.
for (String profile: env.getActiveProfiles()) {
loadProfile("cloudy-bootstrap", env, profile);
}
}
}
private void loadProfile(String prefix, ConfigurableEnvironment env, String profile) {
try {
PropertySource<?> propertySource = loader.load(prefix + (profile != null ? "-" + profile: ""), new ClassPathResource(prefix + ".yml"), profile);
//propertySource will be null if the profile isn't represented in the yml, so skip it if this is the case.
if (propertySource != null) {
//add PropertySource after the "applicationConfigurationProperties" source to allow the default yml to override these.
env.getPropertySources().addAfter("applicationConfigurationProperties", propertySource);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Override
public int getOrder() {
//must go after ConfigFileApplicationListener
return Ordered.HIGHEST_PRECEDENCE + 11;
}
}
This custom EnvironmentPostProcessor can be injected via META-INF/spring.factories:
#Environment PostProcessors
org.springframework.boot.env.EnvironmentPostProcessor=\
com.mycompany.cloudy.bootstrap.autoconfig.CloudyConfigEnvironmentPostProcessor
A couple things to note:
The YamlPropertySourceLoader loads yaml properties by profile, so if you are using a multi-document yaml file you need to actually load each profile from it separately, including the no-profile configs.
ConfigFileApplicationListener is the EnvironmentPostProcessor responsible for loading bootstrap.yml (or application.yml for the regular context) into the Environment, so in order to position the custom yaml properties correctly relative to the bootstrap.yml properties precedence-wise, you need to order your custom EnvironmentPostProcessor after ConfigFileApplicationListener.
Edit: My initial answer did not work. I'm replacing it with this one, which does.

Automatic configuration reinitialization in Spring

In Log4j, there is a feature wherein the system can be initialized to do a configure and watch with an interval. This allows for the log4j system to reload its properties whenever the property file is changed. Does the spring framework have such a Configuration Observer facility wherein the Configuration is reloaded when it changed. The Configuration that needs reloading is not the Springs's applicationContext.xml but various other configuration files that are initialized using the Spring initialization beans.
I found a utility that does something similar to Log4J here. It's basically an extension to PropertyPlaceholderConfigurer that reloads properties when they change.
AFAIK Spring does not provide such a utility. However there is a 3rd party tool, JRebel that enables you to update an entire web application (including the Spring configuration) without requiring a server restart.
A free trial is available, and the purchase price is fairly inexpensive.
I would be extra cautious with reloading spring application context.
What do you expect to happen with singleton beans? If an object has a reference to singleton bean, should it be updated?
I develop using JRebel and I would be very wary of expecting it to refresh your configuration. Works fine with Java, not with Spring though.
If you would like to add context, I have done that in the following way :
public class ApplicationContextUtil
{
static String[] configFiles = {"applicationContextParent.xml"};
private static ApplicationContext context = null;
static
{
context = new ClassPathXmlApplicationContext ( configFiles );
}
public static void addContext( String[] newConfigFiles )
{
// add the new context to the previous context
ApplicationContext newContext = new ClassPathXmlApplicationContext ( newConfigFiles, context );
context = newContext;
}
public static ApplicationContext getApplicationContext ()
{
// return the context
return context;
}
}
This is your context provider class. For details, you can look at my blog

Resources