#Value for custom values is not showing up - spring

I have a project in Spring Boot (1.5.9). I have properties in an application.properties in src/main/resources and in an external application.properties. I also can provide values through the command line.
My main class looks like this:
#SpringBootApplication
#SpringBootConfiguration
#EnableJpaRepositories
#EnableEncryptableProperties
#Import(RepositoryRestMvcAutoConfiguration.class)
public class Application {
public Application() { super(); }
public static void main(String[] args) {
final SpringApplication app = new SpringApplication(Application.class);
app.run(args);
}
}
I have a different class that looks something like this:
#Component
public class MyAuthenticationProvider extends AuthenticationProviderInterface {
#Value("${myproject.authentication.url:https://blah:8443}")
private String authenticationURL = "https://test.blah:8443";
#Override
public void afterPropertiesSet() {
System.err.println(authenticationURL);
}
..... other stuff.....
}
From the System.err, I get https://test.blah:8443.
Now, I can get all of my major variables: spring.main.banner-mode, spring.datasource.name, etc. I just do not seem to be able to get my custom variables--myproject.authentication.url or myproject.authentication.accessstring.
The spring variables are behaving as expected--I can follow precedence from the properties files to the command line. It's only the custom variables that are giving me fits.
Does anyone have any suggestions? I'm trying to give good information, but I am typing between two unconnected computers and I will occasionally have a typo.
Thank you.

Change #Comopnent to #Component.

OK. This was blatant stupidity and Eclipse "helping" me again. The Save actions added a "final" and (unsurprisingly) the values were not being updated. Sorry for wasting people's time.

Related

#Autowired notation is not working as expected

#SpringBootApplication
public class MainApplication {
#Autowired
static BibliographyIndexer bi;
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
bi.reindex();
}
}
#Repository
public class BibliographyIndexer {
...
}
Whenever I access the properties of bi I get a NullPointerException. I know the #Autowired notation didn't work. But why?
Note: both classes are under the same package.
Additional question: Since I want to run a method upon the start of the spring application. Is this the best approach since #pepevalbe's answer already gave me the workaround I needed. Is there another way to run a method upon the start of the spring application?
Because you can't #Autorwire an static class. It doesn't get initialized so you get a NPE when trying to use it.
There are workarounds to wire a bean into a static class, but it is strongly discouraged.
EDIT:
If you need to execute code after initilization you could add an event listener:
#SpringBootApplication
public class MainApplication {
#Autowired
BibliographyIndexer bi;
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
#EventListener(ApplicationReadyEvent.class)
public void doAfterStartUp() {
bi.reindex();
}
}
There are several reasons #Autowired might not work.
When a new instance is created not by Spring but by for example manually calling a constructor, the instance of the class will not be registered in the Spring context and thus not available for dependency injection. Also when you use #Autowired in the class of which you created a new instance, the Spring context will not be known to it and thus most likely this will also fail.
Another reason can be that the class you want to use #Autowired in, is not picked up by the ComponentScan. This can basically be because of two reasons.
The package is outside the ComponentScan search path. Move the package to a scanned location or configure the ComponentScan to fix this.
The class in which you want to use #Autowired does not have a Spring annotation. Add one of the following annotatons to the class: #Component, #Repository, #Service, #Controller, #Configuration. They have different behaviors so choose carefully........
Your problem is that you cannot use bi in main because main is static.
Making bi static doesn't help because static fields will not be #Autowired (It is possible but does not make sense in the concepts of Dependency Injection).
Remove static and move bi.reindex() to a new method annotated with #PostConstruct. It will be executed after the MainApplication-bean is fully initialized and here you can use your injected bi.
In main method you can refer to context and from it get access to bean BibliographyIndexer. In static main spring can not creates and injects bean so this is how you can get it from context.
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MainApplication.class, args);
BibliographyIndexer bibliographyIndexer = context.getBean(BibliographyIndexer.class);
bibliographyIndexer.reindex();
}
You can also do as in answer from pepevalbe and execute this code after initialization.

how to conditionally not create beans in spring boot?

In my application, I have a component that reads data from other system when the application is started.
However, during testing, I don't want this component to be created
#Component
#Slf4j
public class DeviceStatisticsSyncHandler {
#EventListener
public void handle(ApplicationReadyEvent event) {
syncDeviceStatisticsDataSync();
}
#Value("${test.mode:false}")
public boolean serviceEnabled;
}
I can use condition to solve this, but other code readers need to understand, so I don't think this is a very good method:
#EventListener(condition = "#deviceStatisticsSyncHandler .isServiceEnabled()")
public void handle(ApplicationReadyEvent event) {
syncDeviceStatisticsDataSync();
}
public boolean isServiceEnabled() {
return !serviceEnabled;
}
#Value("${test.mode:false}")
public boolean serviceEnabled;
My application doesn't use Profiles, is there any other method to solve this problem.
Spring Boot version:2.1.3
One possible option is not to load the DeviceStaticsticsSyncHandler at all if you're in a test mode.
The "test.mode" is not a good name here, because the production code contains something tightly bound to the tests.
How about the following approach:
#Component
#ConditionalOnProperty(name ="device.stats.handler.enabled", havingValue = "true", matchIfMissing=true)
public class DeviceStatisticsSyncHandler {
// do whatever you need here, but there is no need for "test.mode" enabled related code here
}
Now in Tests you can define a test property "device.stats.handler.enabled=false" on the test itself or even place that definition in src/test/reources/application.properties so it will be false for all tests in the module.
An obvious advantage is that this definition is pretty much self explanatory and can be easy understood by other project maintainers.
for me, it's not the case of the condition rather environment-related. I will solve this problem using spring profile.
Step 1: Create an Interface first
public interface DeviceStatisticsSyncHandler {
public void handle(ApplicationReadyEvent event);
}
Step 2: Create an Implementation for production
#Component
#Profile("!test")
public class DeviceStatisticsSyncHandlerImpl implements DeviceStatisticsSyncHandler {
#EventListener
#Override
public void handle(ApplicationReadyEvent event) {
syncDeviceStatisticsDataSync();
}
}
step 3: create an implementation of test
#Component
#Profile("test")
public class DeviceStatisticsSyncHandlerTestImpl implements DeviceStatisticsSyncHandler {
#EventListener
#Override
public void handle(ApplicationReadyEvent event) {
//do Nothing
}
}
final step
All you need to do is set/toggle the property
-Dspring.profiles.active=test
or
-Dspring.profiles.active=prod
I found a way to achieve this without any further external configuration required.
The idea is to create a general configuration that applies to all integration tests and use #MockBean there to replace the real bean. So one should create a class like this under the test classpath (i.e. that is not scanned during normal application launch):
#Configuration
public class IntegrationTestConfiguration
{
#MockBean
public DeviceStatisticsSyncHandler deviceStatisticsSyncHandler;
}
I was actually surprised that #MockBean can be used here, but the Javadoc explicitly points that out: Can be used as a class level annotation or on fields in either #Configuration classes, or test classes that are #RunWith the SpringRunner..

How to convert plain java main method program on docker

I wanted to migrate/run old java code to docker using Jenkins.
It is structured to run using normal main method of java (Jar file having main method is executed through some script).
Its making use of spring.xml(applicationContext.xml) files with spring-context-2.5.xsd
Uses properties file for all configurations.
Questions as I am looking for recommendations now on:
Does this project needs to be migrated to spring-boot application for migrating to/creating docker image?
If yes, please have a look at current code block
Do I need to replace properties files by yml files?
Current code of main class can be framed as :
public class SIIRunner {
public static void main(String[] args){
String envStr = null;
if (args != null && args.length > 0) {
envStr = args[0];
}
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
SIIExecutor siiExecutor= (SIIExecutor) ctx.getBean("SIIExecutor");
siiExecutor.pollAndOperate();
}
}
#SpringBootApplication
public class Application {
public static void main(String[] args) throws Exception {
ApplicationContext app = SpringApplication.run(Application.class,
args);//init the context
SIIExecutor siiExecutor = (SIIExecutor)
app.getBean(SIIExecutor.class);//get the bean by type
}
#Bean // this method is the equivalent of the <bean/> tag in xml
public SIIExecutor getBean(){
return new SIIExecutor();
}
}
As long as you are starting with a base #Configuration class to begin with, which it maybe sounds like you are with #SpringBootApplication, you can use the #ImportResource annotation to include an XML configuration file as well.
#SpringBootApplication
#ImportResource("classpath:beanFileName.xml")
public class SpringConfiguration {
//
}
Spring boot ideal concept is avoid xml file. but if you want to keep xml bean, you can just add #ImportResource("classPath:beanFileName.xml")
I would recommend remove the beanFileName.xml file. and, convert this file to spring annotation based bean. So, whatever class has been created as bean. Just write #Service or #Component annotation before class name. for example:
XML based:
<bean ID="id name" class="com.example.MyBean">
Annotation based:
#Service or #Component
class MyBean {
}
And, add #ComponentScan("Give the package name").
This is the best approach. Hope this helps.

Why do I need to add #Component when I have to schedule the task in spring boot

I didn't understand the proper use of #Componenet, #Configuration,#Bean annotation.
I want to run one method in every 60 seconds.Please check the below code. If I don't give #Component annotation then it doesn't run. so What is the use of #Component in this context?
#EnableScheduling
public class SchedulingProjectApplication {
private static final Logger log =
LoggerFactory.getLogger(SchedulingProjectApplication.class);
public static void main(String[] args) {
SpringApplication.run(SchedulingProjectApplication.class, args);
}
#Scheduled(fixedDelay = 6000)
public void r()
{
log.info("Start- main-job");
log.info("stop-main-job");
}
}
There are several problems with this piece of code:
Your Spring Boot application is not flagged with #SpringBootApplication (or #EnableAutoConfiguration). As a result, the auto-configuration will not kick in at all (Spring Boot will start your app but won't do anything with it besides basic stuff such as env preparation, etc). It's perfectly fine in certains cases but that isn't probably what you want
You've flagged the run task directly on your app. It's ok for demo but it would be better to move that logic in its own class
So to answer your question: SchedulingProjectApplication is the root source of your app but it's just a simple POJO. There's nothing that instructs the container to process it. Usually the app is a #Configuration (you can use one of the #EnableXYZ on it, you can define additional beans, etc.
If you add #SpringBootApplication on your class, it will scan any #Component in the same package of your app (and all the sub-packages).
More details about code structure in the documentation
One basic/simple structure for you would be:
package com.example.foo;
#SpringBootApplication
#EnableScheduling
public class SchedulingProjectApplication {
public static void main(String[] args) {
SpringApplication.run(SchedulingProjectApplication.class, args);
}
}
And
package com.example.foo;
#Component
public class SchedulingLogger {
private static final Logger log =
LoggerFactory.getLogger(SchedulingLogger.class);
#Scheduled(fixedDelay = 6000)
public void r()
{
log.info("Start- main-job");
log.info("stop-main-job");
}
}
There are other things that you should be aware with regards to configuration (such as moving decisions outside of your #SpringBootApplication if you use slicing).

GroovyTemplates properties configuration

I'm trying to configure the properties of GroovyTemplates. I checked the reference document, but formatting options are not available through the application.properties file. So I did the following to customize some other properties.
#EnableAutoConfiguration
#Configuration
#ComponentScan
public class Application implements CommandLineRunner {
#Autowired
private GroovyTemplateAutoConfiguration.GroovyMarkupConfiguration configuration;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(String... strings) throws Exception {
this.configuration.groovyTemplateConfiguration().setUseDoubleQuotes(true);
this.configuration.groovyTemplateConfiguration().setAutoNewLine(true);
this.configuration.groovyTemplateConfiguration().setAutoIndent(true);
}
}
I can see the double quotes in the rendered HTML files. However, it is still unformatted.
So, I have two questions. The first one is, how can I set the properties properly? Most probably, the one I did is not the proper way. The second question is, why do I still get the unformatted HTML?
In the docs it says
spring.groovy.template.configuration.*= # See Groovy's TemplateConfiguration
So I assume that corresponds up the properties you set (what you are doing isn't wrong, necessarily, but it might happen too late to influence the behaviour at runtime). I would stick with the application.properties if I were you.

Resources