How to get a boolean variable DEBUG in a Thymeleaf? - spring-boot

How to find out if a project is in debug mode and get a variable of a logical type in the thymeleaf template.
<h1 th:if="${DEBUG}">...</h1>

You can achieve this while injecting the Environment bean to your contorller:
#Controller
#RequestMapping("/public")
public class PublicController {
private final Environment environment;
public PublicController(Environment environment) {
this.environment = environment;
}
#GetMapping("/debug")
public String returnFoo(Model model) {
String envValue = environment.getProperty("debug");
boolean isDebugMode = (envValue != null && !envValue.equals("false"));
model.addAttribute("DEBUG", isDebugMode);
System.out.println(isDebugMode);
return "yourView";
}
}
This is implementation is working for every possible way you might set the debug flag to your application (How can I tell whether my Spring boot application is in debug mode?)

Related

Spring #Value not working in Spring Boot 2.5.5, getting null values

I am trying to inject some property values into variables by means of Spring #Value annotation but I get null values. I tried different configurations and triks but it doesn't work. Think is that before today everythink was working properly. I do not know what I changed in order to get things broken.
Here is my java class:
#Component
#ConditionalOnProperty(prefix = "studioghibli", name = "get")
public class StudioGhibliRestService {
#Value("${studioghibli.basepath}")
private static String BASE_PATH;
#Value("${studioghibli.path}")
private static String PATH;
#Value("${studioghibli.protocol:http}")
private static String PROTOCOL;
#Value("${studioghibli.host}")
private static String HOST;
private static String BASE_URI = PROTOCOL.concat("://").concat(HOST).concat(BASE_PATH).concat(PATH);
#Autowired
StudioGhibliRestConnector connector;
public List<StudioGhibliFilmDTO> findAllFilms() throws SipadContenziosoInternalException {
var response = connector.doGet(BASE_URI, null, null);
if (!response.getStatusCode().is2xxSuccessful() || !response.hasBody()) {
throw new SipadContenziosoInternalException(Errore.INTERNAL_REST_ERROR, "FindAll(), microservizio ".concat(BASE_URI), null);
}
return (List<StudioGhibliFilmDTO>) response.getBody();
}
}
As you can see, the class is annotated with #Component, that because I will need to use it as #Service layer in order to make a rest call in my business logic.
The class is also annotaded with conditional on property...
Here is a screenshot of the debug window at startup:
Since the PROTOCOL value is null, i get a null pointer exception immediately at start up.
Here is part of the application-dev.properties file:
studioghibli.get
studioghibli.protocol=https
studioghibli.host=ghibliapi.herokuapp.com
studioghibli.basepath=/
studioghibli.path=/films
First of all, #Value annotation does not work with static fields.
Secondly, fields with #Value annotation is processed when the instance of the class (a bean) is created by Spring, but static fields exist for a class (for any instance), so when the compiler is trying to define your static BASE_URI field other fields are not defined yet, so you get the NPE on startup.
So you might need a refactoring, try to inject values with the constructor like this:
#Component
#ConditionalOnProperty(prefix = "studioghibli", name = "get")
public class StudioGhibliRestService {
private final StudioGhibliRestConnector connector;
private final String baseUri;
public StudioGhibliRestService(StudioGhibliRestConnector connector,
#Value("${studioghibli.basepath}") String basePath,
#Value("${studioghibli.path}") String path,
#Value("${studioghibli.protocol:http}") String protocol,
#Value("${studioghibli.host}") String host) {
this.connector = connector;
this.baseUri = protocol.concat("://").concat(host).concat(basePath).concat(path);
}
// other code
}
Thanks, It works for me, I have to add some codes to my project. Then I check the spring core document in "#Value" section. Besides
When configuring a PropertySourcesPlaceholderConfigurer using
JavaConfig, the #Bean method must be static.
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer(){
return new PropertySourcesPlaceholderConfigurer();
}

Changes in Application properties doesn't impact - Springboot

I am using Spring boot 2.5.4. I have written web application. Now i am facing issue with application.properties file variables. If i am changing existing value, In code old value is been read, newly defined object is not coming.
Find the below application.properties file
spring.datasource.url=jdbc:mysql://XXXXXXXXXXX:3306/test
spring.datasource.username=user
spring.datasource.password=XXXXXXXXXXXXXXXX
spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver
# Config Variables
ml.url=https://google.com/ml/entities
main.url=https://xxxx.com/staging/mainfile
and application config java file
#Component
public class ApplicationConfig {
#Value("${ml.url}")
public String mlurl;
#Value("${main.url}")
public String mainurl;
#PostConstruct
public void initThat(){
that = this;
}
}
reading variable in code as
#RequestMapping("/readfile")
#RestController
public class AppointmentResource {
private static Logger S_LOGGER = LogManager.getLogger( AppointmentResource.class );
#Autowired
private ApplicationConfig applicationConfig;
#GetMapping(value = "/websiteUrl",produces = MediaType.APPLICATION_JSON_VALUE)
public String getProduct() {
String websiteUrl = applicationConfig.mlurl;
S_LOGGER.info("website url is " + websiteUrl);
return websiteUrl;
}
}
After compiling for few times. Then i changes ml.url to https://google.com/prod/entities/test
but in code still i am getting as https://google.com/ml/entities.
Can anyone help in getting latest changes from application.properties
I am struck here. Help
I fixed it. It was picking from the folder config. which i created for different environments

Using togglz to implement feature flags based on environment (dev, qa, prod)

I am implementing feature flags in my spring application and I would like to use togglz. I would like the features to be based on the environment. For example a feature is being worked or tested so I could have it on in DEV and QA, but it's not ready for the public, so it's turned off in PROD.
I'm looking through the togglz doc and their activation strategies, but none of them seem to be based on the environment. Do I need to implement a custom strategy or can I use one of the existing strategies in a creative way?
If there any concise example that would be most helpful.
Option 1
There is an existing activation strategy by profile provided by togglz-spring-core : SpringProfileActivationStrategy
Option 2
If you want to create yours, it can be achieved easily :
#Configuration
public class ProfileStrategy implements ActivationStrategy {
private static final String PROFILE_PARAM = "profile";
private final Environment environment;
public ProfileStrategy(Environment environment) {
this.environment = environment;
}
#Override
public String getId() {
return "profile";
}
#Override
public String getName() {
return "Profile strategy";
}
#Override
public boolean isActive(FeatureState featureState, FeatureUser featureUser) {
String profile = featureState.getParameter(PROFILE_PARAM);
return profile != null && Arrays.asList(environment.getActiveProfiles()).contains(profile);
}
#Override
public Parameter[] getParameters() {
return new Parameter[] {
ParameterBuilder.create(PROFILE_PARAM)
.label("Profil")
.description("Profile to activate feature")
.largeText()
};
}
}

Can I exclude a specific property from auto configuration

I'm using spring-boot and trying to use "PropertySourcesPlaceholderConfigurer" to load external properties files from filesystem,
but I got an error like below:
Binding to target org.springframework.boot.autoconfigure.web.ServerProperties#3ec11999 failed:
Property: server.environment
Value: BETA
Reason: Failed to convert property value of type [java.lang.String] to required type [org.springframework.core.env.Environment] for property 'environment'; ...
This is because spring-boot tries to auto-config "ServerProperties" and "ServerProperties" looks like:
#ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties
implements EmbeddedServletContainerCustomizer, EnvironmentAware, Ordered {
...
private Environment environment;
...
}
So it tries to "parse" any property with a "server" prefix.
Unfortunately our legacy properties file happens to contain an irrelevant property called
server.environment=BETA
So spring-boot tries to convert string "BETA" to an "Environment" object.
Is there a way that I can exclude "server.environment" from spring-boot's autoconfigure ?
I don't think you can exclude single property, but you can make trick Spring Boot to preserve original Environment object while converting String to Environment.
#Component
#ConfigurationPropertiesBinding
class OriginalEnvironmentPreservingStringToEnvironmentConverter implements GenericConverter {
private static final Set<ConvertiblePair> CONVERTIBLE_TYPES;
static {
Set<ConvertiblePair> types = new HashSet<>();
types.add(new ConvertiblePair(String.class, Environment.class));
CONVERTIBLE_TYPES = Collections.unmodifiableSet(types);
}
private final Environment environment;
public OriginalEnvironmentPreservingStringToEnvironmentConverter(Environment environment) {
this.environment = environment;
}
#Override
public Set<ConvertiblePair> getConvertibleTypes() {
return CONVERTIBLE_TYPES;
}
#Override
public Object convert(final Object o, final TypeDescriptor typeDescriptor, final TypeDescriptor typeDescriptor1) {
return environment;
}
}
I am not sure if there wouldn't be any side effects though. In simple scenario it does work fine.

Spring Boot - Detect and terminate if property not set?

Is there any way for a Spring Boot web application to abort at startup if a required property is not set anywhere (neither in the application.properties file nor the other property sources)? Right now, if the property is included in another property, it seem that Spring Boot simply avoids substitution.
For example, in my application.properties file, I have the line:
quartz.datasource.url=jdbc:hsqldb:${my.home}/database/my-jobstore
Right now, if "my.home" is not set elsewhere, Spring Boot is setting the url literally to "jdbc:hsqldb:${my.home}/database/my-jobstore" (no substitution).
I would like to have the application fail to start if the property my.home were not set anywhere else.
To throw a friendly exceptions just put a default null value in property, check and throw a exception in afterProperty method.
#Component
public static class ConfigurationGuard implements InitializingBean {
#Value("${my.home:#{null}}")
private String myHomeValue;
public void afterPropertiesSet() {
if (this.myHomeValue == null or this.myHomeValue.equals("${my.home}") {
throw new IllegalArgumentException("${my.home} must be configured");
}
}
}
Create a bean with a simple #Value(${my.home}) annotated field. - Then Spring will try to inject that value and will fail and therefore stop when the value is not there.
Just #Value(${my.home}) private String myHomeValue; is enough for normal (not Boot) Spring applications for sure! But I do not know whether Boot has some other configuration to handle missing values: If there is an other failure management than you could check that value in an PostCreation method.
#Component
public static class ConfigurationGuard implements InitializingBean {
#Value(${my.home})
private String myHomeValue;
/**
* ONLY needed if there is some crude default handling for missing values!!!!
*
* So try it first without this method (and without implements InitializingBean)
*/
public void afterPropertiesSet() {
if (this.myHomeValue == null or this.myHomeValue.equals("${my.home}") {
throw new IllegalArgumentException("${my.home} must be configured");
}
}
}
The default behaviour in current versions of Spring Boot (1.5.x, 2.0.x, 2.1.x) is to throw an exception if a placeholder can not be resolved.
There will a be an exception like this one :
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'app.foo.undefined' in value "${app.foo.undefined}"
It works because a bean of type PropertySourcesPlaceholderConfigurer (from spring-context) is automatically registered in Spring Boot, in this class : PropertyPlaceholderAutoConfiguration. And by default, the property ignoreUnresolvablePlaceholders in PropertySourcesPlaceholderConfigurer is set to false, which means an exception must be thrown if a placeholder is unresolved (be it nested or not).
Although they work, I think the approach in the foremost answer is somewhat brittle, as it only works for the predefined name(s), and will silently stop checking the when someone changes quartz.datasource.url in the configs to use a different expansion.
Ideally, I want this value of ignoreUnresolvablePlaceholders to be false to get wholesale expansion checking when parsing my configs such as application.properties or its YAML variants, but it's hard-coded to true for these cases. This unfortunately leaves strings such as ${FOO} in its unexpanded form if FOO cannot be found, making troubleshooting extremely painful. This is especially the case for fields that don't readily appear in the logs such as passwords.
While I couldn't find a way of changing ignoreUnresolvablePlaceholders short of modifying Spring Boot's classes, I did find an alternative of using a custom PropertySource implementation and defining a new syntax such as "${!FOO}" to indicate FOO must exist as an environment variable or die. (The OP didn't mention whether my.home is an environment variable but the code below is for environment variables.)
First, an EnvironmentPostProcessor implementation is required for registering the custom PropertySource. This StrictSystemEnvironmentProcessor.java does this as well as holds the implementation of the custom PropertySource:
package some.package;
#Order(Ordered.LOWEST_PRECEDENCE)
class StrictSystemEnvironmentProcessor implements EnvironmentPostProcessor {
private static final String PROPERTY_SOURCE_NAME = "STRICT_" + StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME;
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
if (environment.getPropertySources().contains(PROPERTY_SOURCE_NAME)) {
return;
}
SystemEnvironmentPropertySource delegate = (SystemEnvironmentPropertySource)environment.getPropertySources()
.get(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME);
environment.getPropertySources().addLast(new StrictSystemEnvironmentPropertySource(delegate));
}
private static class StrictSystemEnvironmentPropertySource extends SystemEnvironmentPropertySource {
public StrictSystemEnvironmentPropertySource(SystemEnvironmentPropertySource delegate) {
super(PROPERTY_SOURCE_NAME, delegate.getSource());
}
#Override
public Object getProperty(String name) {
if (name.startsWith("!")) {
String variableName = name.substring(1);
Object property = super.getProperty(variableName);
if (property != null) {
return property;
}
throw new IllegalStateException("Environment variable '" + variableName + "' is not set");
}
return null;
}
}
}
Instead of returning null, an exception is thrown for names that start with !.
This META-INF/spring.factories is also required so that Spring initializes our EnvironmentPostProcessor:
org.springframework.boot.env.EnvironmentPostProcessor=some.package.StrictSystemEnvironmentProcessor
Then henceforth, I can write all environment variables substitutions in my configs as ${!FOO} to get strict existance checking.
You can also create a #ConfigurationProperties bean, and decorate it with #Validated and #NotNull. This will throw an exception during startup when the value is not present (or null), e.g.
#Validated
#ConfigurationProperties("my")
public class MyProperties {
#NotNull
private String home;
// getter/setter, or constructor. See #ConstructorBinding.
}
For reference: Spring Boot 2.6 - #ConfigurationProperties Validation.
Note that you may need to add spring-boot-starter-validation, or another validator, depending on your project.
Then, you can just supply it as a dependency when needed, e.g.
#Component
public class AnotherBean {
private final MyProperties myProps;
public AnotherBean(MyProperties myProps) {
this.myProps = myProps;
}
// some code that uses myProps.getHome()
}

Resources