That's a refinement you can do to updgrade legacy applications. Using yaml config files combines with Spring profiles is a great way to configure your apps. Let's see how to do that.
Firstly, let's consider that we weill inject something in a java class depending on the runtime environment.
@Service
public class MyService {
@Value("${service.url}")
private String URL;
}
application.yaml should be like this :
service:
url: http://alexdp.free.fr/violetumleditor
---
spring:
profiles: production
service:
url: http://violet.sourceforge.net
Firstly, let's consider that we weill inject something in a java class depending on the runtime environment.
@Service
public class MyService {
@Value("${service.url}")
private String URL;
}
application.yaml should be like this :
service:
url: http://alexdp.free.fr/violetumleditor
---
spring:
profiles: production
service:
url: http://violet.sourceforge.net
Thus, if I lauch my applicatin without JVM Spring profile params, URL will be http://alexdp.free.fr/violetumleditor. If I launch it with -Dspring.profiles.active=production, URL will be http://violet.sourceforge.net. Great! But this feature is natively supported only for Spring Boot based applications. So, let's active this on legacy apps with this XML spring config file fragment :
<bean id="yamlProperties"
class="org.springframework.beans.factory.config.YamlPropertiesFactoryBean">
<property name="resources">
<list>
<value>classpath:application.yml</value>
</list>
</property>
<property name="documentMatchers">
<bean
class="mypackage.SpringProfileDocumentMatcher" />
</property>
</bean>
<context:property-placeholder properties-ref="yamlProperties" />
Of course, you saw that we wrote our custom SpringProfileDocumentMatcher.
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Properties;
import java.util.Set;
import org.springframework.beans.factory.config.YamlProcessor.DocumentMatcher;
import org.springframework.beans.factory.config.YamlProcessor.MatchStatus;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
/**
* Allows to use Spring profiles without Spring Boot for legacy Spring based apps
*
* @author Alexandre de Pellegrin
*
*/
public class SpringProfileDocumentMatcher implements DocumentMatcher, EnvironmentAware {
private static final String[] DEFAULT_PROFILES = new String[] { "default" };
private String[] activeProfiles = new String[0];
public SpringProfileDocumentMatcher() {
}
public SpringProfileDocumentMatcher(String... profiles) {
addActiveProfiles(profiles);
}
public void addActiveProfiles(String... profiles) {
LinkedHashSet set = new LinkedHashSet(
Arrays.asList(this.activeProfiles));
Collections.addAll(set, profiles);
this.activeProfiles = set.toArray(new String[set.size()]);
}
@Override
public MatchStatus matches(Properties properties) {
String[] profiles = this.activeProfiles;
if (profiles.length == 0) {
profiles = DEFAULT_PROFILES;
}
return new ArrayDocumentMatcher("spring.profiles", profiles).matches(properties);
}
@Override
public void setEnvironment(Environment environment) {
if (environment != null) {
addActiveProfiles(environment.getActiveProfiles());
}
}
private class ArrayDocumentMatcher implements DocumentMatcher {
private final String key;
private final String[] patterns;
public ArrayDocumentMatcher(final String key, final String... patterns) {
this.key = key;
this.patterns = patterns;
}
@Override
public MatchStatus matches(Properties properties) {
if (!properties.containsKey(this.key)) {
return MatchStatus.ABSTAIN;
}
Set values = StringUtils.commaDelimitedListToSet(properties
.getProperty(this.key));
for (String pattern : this.patterns) {
for (String value : values) {
if (value.matches(pattern)) {
return MatchStatus.FOUND;
}
}
}
return MatchStatus.NOT_FOUND;
}
}
}
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Properties;
import java.util.Set;
import org.springframework.beans.factory.config.YamlProcessor.DocumentMatcher;
import org.springframework.beans.factory.config.YamlProcessor.MatchStatus;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
/**
* Allows to use Spring profiles without Spring Boot for legacy Spring based apps
*
* @author Alexandre de Pellegrin
*
*/
public class SpringProfileDocumentMatcher implements DocumentMatcher, EnvironmentAware {
private static final String[] DEFAULT_PROFILES = new String[] { "default" };
private String[] activeProfiles = new String[0];
public SpringProfileDocumentMatcher() {
}
public SpringProfileDocumentMatcher(String... profiles) {
addActiveProfiles(profiles);
}
public void addActiveProfiles(String... profiles) {
LinkedHashSet
Arrays.asList(this.activeProfiles));
Collections.addAll(set, profiles);
this.activeProfiles = set.toArray(new String[set.size()]);
}
@Override
public MatchStatus matches(Properties properties) {
String[] profiles = this.activeProfiles;
if (profiles.length == 0) {
profiles = DEFAULT_PROFILES;
}
return new ArrayDocumentMatcher("spring.profiles", profiles).matches(properties);
}
@Override
public void setEnvironment(Environment environment) {
if (environment != null) {
addActiveProfiles(environment.getActiveProfiles());
}
}
private class ArrayDocumentMatcher implements DocumentMatcher {
private final String key;
private final String[] patterns;
public ArrayDocumentMatcher(final String key, final String... patterns) {
this.key = key;
this.patterns = patterns;
}
@Override
public MatchStatus matches(Properties properties) {
if (!properties.containsKey(this.key)) {
return MatchStatus.ABSTAIN;
}
Set
.getProperty(this.key));
for (String pattern : this.patterns) {
for (String value : values) {
if (value.matches(pattern)) {
return MatchStatus.FOUND;
}
}
}
return MatchStatus.NOT_FOUND;
}
}
}
That's it!