Spring override non-application.properties file in Tests - java

I use an external pure Java library that uses a file that is called updater.properties.
Im currently writing some Spring-Boot Integration Tests with #SpringBootTest.
The problem I currently have is that i don’t find any option to pass/use a different updater.properties file with Spring. Is there a solution for this? Or does the library need to be updated for this to work?
The updater.properties is in my resource folder and being parsed internally by the library.
Would be happy for any help!

#SaltukKezer, yes you can. First step make a config class like
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
#Configuration
#PropertySource("classpath:/updater.properties")
public class UpdaterProperties{
#Autowired
Environment env;
public String getMyPropertyValue() {
return env.getProperty("your.property.name");
}
}
Then second step, just get an instance of this class and call the getters

Related

Debugging SpringBoot MVC service application 404 error

A web application I've been working on recently the past like 2 weeks maybe for whatever reason when I finally tested it - won't seem to even enter the method that I have to return a JSON list of objects. I have included the Jackson library and Spring Boot Web, Tomcat, Data-JPA, Hibernate, MySQL, and a library to allow me to access JSP files. The index.jsp comes up but I almost feel like Spring Boot is giving me that free of charge as it's not even entering that method. I have been having the issue for a few days but trying to resolve it on my own - I found another answer that suggested to put a breakpoint inside one of the Spring classes but when I "debugged" it through Eclipse, it didn't even stop at that class - something about pattern matching - One answer suggested adding a context to the application.properties file - didn't help. I've reduced it to as simple as I think I can get it. Can anyone tell me what I might be doing wrong? Before my code, the project is on Github at: https://github.com/sfulmer/Scheduler.git
Here's my controller:
package net.draconia.schedule.controllers;
import java.util.List;
import net.draconia.schedule.beans.Event;
import net.draconia.schedule.dao.EventDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
public class ScheduleController
{
private static final Logger logger = LoggerFactory.getLogger(ScheduleController.class);
#Autowired
private EventDAO mObjDAO;
protected EventDAO getDAO()
{
return(mObjDAO);
}
//#GetMapping("/events")
#RequestMapping(value = "events", method = RequestMethod.GET)
public #ResponseBody List<Event> getEvents()
{
logger.debug("I got here");
return(getDAO().getList());
}
#GetMapping("/")
public String index()
{
return("index");
}
}
Here is the DAO interface - I'll show the class if necessary but this is what the controller looks at:
package net.draconia.schedule.dao;
import java.util.List;
import javax.persistence.EntityNotFoundException;
import net.draconia.schedule.beans.Event;
public interface EventDAO
{
public Event getEventById(final long lId) throws EntityNotFoundException;
public List<Event> getList();
public void remove(final Event objEvent);
public void removeById(final long lId);
public Event save(final Event objEvent);
}
The Event class is so long but if I need to include it, I will. The application.properties file is here:
spring.datasource.url = jdbc:mysql://localhost:3306/schedule
spring.datasource.username = root
spring.datasource.password = R3g1n# M1lL$ 1$ My Qu3eN!
spring.mvc.view.prefix: /WEB-INF/jsp/
spring.mvc.view.suffix: .jsp
server.servlet.contextPath=/scheduler
and here is my Application class(with the SpringBootApplication annotation):
package net.draconia.schedule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#SpringBootApplication(scanBasePackages = {"net.draconia.schedule.controller"})
public class ScheduleApp implements WebMvcConfigurer
{
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder)
{
return(builder.sources(ScheduleApp.class));
}
public static void main(String[] args)
{
SpringApplication.run(ScheduleApp.class, args);
}
}
I'm relatively new to Spring Boot but haven't ever ran into this problem ever before as I work with it at work and it works fine but we use entirely REST services there and I am using JSP files as well as sorta end-points that respond with JSON but you can't respond from REST services with JSP views so unfortunately I can't copy work's project to get that working or I would sigh Any thoughts on how I can get this working or what I am omitting?
My guess is that you're mixing things from Spring and Spring boot, and that's getting problems on loading beans, as you're probably changing the annotations load order or loading other beans rather than spring boot defaults as expected. For example, you implements WebMvcConfigurer, but you aren't providing any WebMvc Configuration, like a ViewResolver bean
My advice is to follow this guide: https://spring.io/guides/gs/spring-boot/
and use only the annotations from spring boot if using spring boot, or spring if using spring (they're similar, but not exactly the same, configuration is different).
Anyways, you can check loaded beans in Spring application context (Inject it in Application class) with ctx.getBeanDefinitionNames() method and see if your controller is there (i guess not).
By looking into code, my first impression is that, you have some typo in here:
#SpringBootApplication(scanBasePackages = {"net.draconia.schedule.controller"})
Your controller class package name has net.draconia.schedule.controllers.
So can you please correct your scanBasePackages with proper package name.
If that is not the case, please update full stack trace along with GET request which you are submitting into application. Will take a look & update answer accordingly.

Java Spring bean creation and access from external jar

I have spring jar for my entity beans created as export--java--JARfile, where i have beans defined as follows:
package com.my.beans;
#Component("expBean")
public class ExpBean {
}
I also have config in this jar
package com.my;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class pBeanConfig {
#Bean
public ExpBean getExpBean() {
return new ExpBean();
}
}
I have client spring application where i am adding above jar having only beans as by external dependency and trying to get my beans when spring app starts using following code in main code in client spring app.
package com.my;
import java.util.Arrays;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
#SpringBootApplication()
#ComponentScan("com.my")
public class Application {
public static void main (String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
WMyBean bean = (WMyBean) ctx.getBean("expBean");
bean.doSomething();
}
}
But when checked the list of bean definition printed, i do not see my bean from external jar and also i get following error.
"Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'expBean' available"
I tried many options but not sure whats missing.
Option 1
#ComponentScan("com.my")
#SpringBootApplication
public class Application {
Option 2
#ComponentScan({"com.my"})
#SpringBootApplication
public class Application {
Need valuable inputs on step that i am missing out.
Question 1: Is it mandatory for me to do config change in client project to access my dependent jar entities?
Question 2: Can it be dynamic, load all entities without configuration as #bean in client project config?
Question 3: Do i need to build client jar every-time i change my dependent jar file with more entities?
Thanks
First I am assuming the build tool is Gradle, but you can import the jar as a local file like this for the sake of experiment:
dependencies {
implementation files('libs/something_local.jar')}
Then, assuming you use Eclipse you can right click the project folder and do a 'Gradle refresh'
In your config class you can either autowire in the bean for the class as a whole , or choose to create it whenever an instance of the class is created by declaring it within the class constructor:
import org.springframework.beans.factory.annotation.Autowired;
//I also have config in this jar
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class pBeanConfig {
//Autowire in the bean for the whole class.
#Autowired
ExpBean expBean;
public pBeanConfig() {
//Or, you may want to have a bean instance whenever the pBeanConfig class is instantiated.
ExBean exBean;
}
}
I'm making a number of assumptions for this answer like it's a Gradle project, but you can set up a similar dependency for Maven by modifying the POM file to include that file.
Build tools aside, in Eclipse you can simply add the jar file by right clicking the project and build path => configure build path => add external jar file.
UPDATE:
This pic describes how I set up the bean within a separate jar file via eclipse. The steps should show that the bean can be reached with this project even though it's in a jar and not in the project itself.

Spring Boot Test: #TestPropertySource not overriding #EnableAutoConfiguration

I am using Spring Data LDAP to get user data from an LDAP server.
My file structure looks like this:
main
java
com.test.ldap
Application.java
Person.java
PersonRepository.java
resources
application.yml
schema.ldif
test
java
Tests.java
resources
test.yml
test_schema.ldif
And here is my test class:
import com.test.ldap.Person;
import com.test.ldap.PersonRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {PersonRepository.class})
#TestPropertySource(locations = "classpath:test.yml")
#EnableAutoConfiguration
public class Tests {
#Autowired
private PersonRepository personRepository;
#Test
public void testGetPersonByLastName() {
List<Person> names = personRepository.getPersonNamesByLastName("Bachman");
assert(names.size() > 0);
}
}
The problem is, Spring Boot is loading the application.yml and schema.ldif files instead of my test YAML and LDIF files, despite the fact that my #TestPropertySource annotation is explicitly listing test.yml. This seems to be due to the auto configuration, which I would prefer to use for convenience.
I would expect #TestPropertySource to take higher precedence than the auto configuration, but that does not seem to be the case. Is this a bug in Spring, or am I misunderstanding something?
For the record, here is my test.yml file (it does specify test_schema.ldif):
spring:
ldap:
# Embedded Spring LDAP
embedded:
base-dn: dc=test,dc=com
credential:
username: uid=admin
password: secret
ldif: classpath:test_schema.ldif
port: 12345
validation:
enabled: false
So I was able to work around this by manually specifying the properties needed to make use of the LDIF file. This is because, according to the #TestPropertySource documentation, inlined properties have higher preferences than property files.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {PersonRepository.class})
#TestPropertySource(properties =
{"spring.ldap.embedded.ldif=test_schema.ldif", "spring.ldap.embedded.base-dn=dc=test,dc=com"})
#EnableAutoConfiguration
public class Tests {
//...
}
This is not the best workaround, however: what if I had more than just two properties I needed to define? It would be impractical to list them all there.
Edit:
Renaming my test.yml file to application.yml so it overrides the production file that way did the trick. As it turns out, the TestPropertySource annotation only works for .properties files.
I discovered that YML files DO NOT work with #TestPropertySource annotation.
A clean way around this is to use #ActiveProfile. Assuming that your YML file with test properties is called
application-integration-test.yml
then you should use the annotation like this
#ActiveProfile("integration-test")

FindBugs null issue with Spring #Autowired in Eclipse

I am using the findbugs Eclipse plugin (3.0.1.20150306-5afe4d1), spring (4.2.2.RELEASE), and eclipse (Mars.1 (4.5.1)) together and I am receiving the following FindBugs bug in Eclipse.
Non-null field env is not initialized by new
org.test.app.config.AppConfiguration() [Scary(8), Normal confidence]
I am using using the default constructor and using autowiring to initialize the env variable. I also have a PostConstruct annotation which gets called after everything is wired and accesses the env variable to make sure it was initialized correctly.
How can I make this error disappear without turning off the FindBugs plugin and still using the #Autowired annotation?
package org.test.app.config;
import java.util.Arrays;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
#Configuration
#ComponentScan(basePackages = { "org.test.app" })
#PropertySource("classpath:/${spring.profiles.active:local}.properties")
public class AppConfiguration {
private static final Logger log = LoggerFactory.getLogger(AppConfiguration.class);
#Autowired
private Environment env;
/**
* Dump profile info.
*/
#PostConstruct
public void details() {
log.debug("** App application context, active profile(s)={}", Arrays.toString(env.getActiveProfiles()));
}
}
Update
I tried using a constructor per #spoonybard896 suggestion, but it did not work. I received the following error.
java.lang.NoSuchMethodException: org.test.app.config. AppConfiguration $$EnhancerBySpringCGLIB$$cbece1d7.<init>()
[STDOUT] at java.lang.Class.getConstructor0(Class.java:3082) ~[na:1.8.0_60]
[STDOUT] at java.lang.Class.getDeclaredConstructor(Class.java:2178) ~[na:1.8.0_60]
[STDOUT] at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiat‌​e(SimpleInstantiationStrategy.java:80) ~[na:na]
How about using a non-default constructor instead?
private final Environment env;
#Autowired
public AppConfiguration(final Environment env) {
this.env = env;
}
EDIT
The above approach would work for a #Controller instance, but will not work for #Configuration. After doing some quick research it turns out that:
#Configuration is meta-annotated with #Component, therefore #Configuration classes are candidates for component scanning (typically using Spring XML's <context:component-scan/> element) and therefore may also take advantage of #Autowired/#Inject at the field and method level (but not at the constructor level).
I'm thinking that unless there is some kind of addon for FindBugs that understands Spring annotations (I do not know of one) then you may just need to apply a filter to the FindBugs plugin and have it ignore that particular error in that particular file(or in any Configuration class in general). In Eclipse check out Preferences -> Java -> Findbugs -> Filter Files, and check out this link which describes a similar problem and resolution, but just make sure to filter out only the error you want. The goal would not be turn FindBugs off, but instead have it just ignore this one case.
EDIT 2
Adding an annotation to the class will suppress the FindBugs error for this file only.
#SuppressFBWarnings(
value="NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR",
justification="Overriding the check on the env variable because Spring will automatically initialize the variable after the constructor is called and before any public methods are called.")

How it works together these 2 Spring Java configuration classes?

I am studying for the Spring Core certification and I have the followind doubt with an exercice related to the beans configuration using the Java configuration way.
So I have the following RewardsConfig class that configure my beans (this class is into the application folder src/main/java):
package config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import rewards.RewardNetwork;
import rewards.internal.RewardNetworkImpl;
import rewards.internal.account.AccountRepository;
import rewards.internal.account.JdbcAccountRepository;
import rewards.internal.restaurant.JdbcRestaurantRepository;
import rewards.internal.restaurant.RestaurantRepository;
import rewards.internal.reward.JdbcRewardRepository;
import rewards.internal.reward.RewardRepository;
#Configuration
public class RewardsConfig {
#Autowired
DataSource dataSource;
#Bean
public RewardNetwork rewardNetwork(){
return new RewardNetworkImpl(accountRepository(), restaurantRepository(), rewardRepository());
}
#Bean
public AccountRepository accountRepository(){
JdbcAccountRepository repository = new JdbcAccountRepository();
repository.setDataSource(dataSource);
return repository;
}
#Bean
public RestaurantRepository restaurantRepository(){
JdbcRestaurantRepository repository = new JdbcRestaurantRepository();
repository.setDataSource(dataSource);
return repository;
}
#Bean
public RewardRepository rewardRepository(){
JdbcRewardRepository repository = new JdbcRewardRepository();
repository.setDataSource(dataSource);
return repository;
}
}
As you can see I declare 4 methods that are used to create 4 beans and that specify the dependency that occurs among these beans.
So I have a RewardNetwork bean that is implemented by RewardNetworkImpl class that depends from the following 3 beans: AccountRepository, RestaurantRepository and RewardRepository.
Is it the correct interpretation of the Java configuration is Spring?
Can I say for example that RewardNetwork is the declared bean and that RewardNetworkImpl its the current implementation of this bean?
All the 3beans (AccountRepository, RestaurantRepository and RewardRepository) depends by another bean dataSource that, as you can see in the previous code snippet, is declared as #Autowired:
#Autowired
DataSource dataSource;
This bean is not declared in this configuration class because it changes according to the environment (test, developt, production).
So, in my case it is declared into the unit test folder src/test/java:
package rewards;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
#Configuration
public class TestInfrastructureConfig {
/**
* Creates an in-memory "rewards" database populated
* with test data for fast testing
*/
#Bean
public DataSource dataSource(){
return
(new EmbeddedDatabaseBuilder())
.addScript("classpath:rewards/testdb/schema.sql")
.addScript("classpath:rewards/testdb/test-data.sql")
.build();
}
}
So the dataSource bean define a datasource that is valid only for the test environment (used when I perform a unit test).
Now my doubt is: I have 2 different configuration classes and the dataSource bean is not definied into the RewardsConfig configuration class that contains the 3 beans that use it. Why I can't not use the #Import annotation to use it into RewardsConfig?
Something like it:
#Import(TestInfrastructureConfig.class)
How it work exactly?
Tnx
You don't have to import beans to make them available for autowiring. #Import is used to add extra configuration classes.
You really don't want to hard-import a test configuration class, because then your production code is referring to test-only code (and, in this case, always activating it). Instead, think of your configuration class more like an abstract class: declare autowired beans, but don't worry about how they get there. The downstream (runtime) configuration will supply them, and you don't need to know how. Maybe you're supplying an in-memory H2 for testing and using Spring Cloud Connectors for actual runs, doesn't matter.

Categories

Resources