I am trying hard to inject the default datasource but I have the following error:
javax.enterprise.inject.IllegalProductException: Normal scoped producer method may not return null:
io.quarkus.agroal.runtime.DataSourceProducer.createDefaultDataSource()
This is my current situation:
application.properties
quarkus.datasource.driver = com.mysql.cj.jdbc.Driver
quarkus.datasource.jdbc.url=jdbc:mysql://localhost:3307/sandbox
quarkus.datasource.username=root
quarkus.datasource.password=password
quarkus.datasource.jdbc.min-size=0
quarkus.datasource.jdbc.max-size=11
MyClass.java
#ApplicationScoped
public class MyClass {
#Inject
AgroalDataSource dataSource;
void methodUsingDataSource() {...}
}
However I made it works with a named datasource with the SAME configuration as the default one:
application.properties
quarkus.datasource.users.driver = com.mysql.cj.jdbc.Driver
quarkus.datasource.users.url=jdbc:mysql://localhost:3307/sandbox
quarkus.datasource.users.username=root
quarkus.datasource.users.password=password
quarkus.datasource.users.min-size=0
quarkus.datasource.users.max-size=11
MyClass.java
#ApplicationScoped
public class MyClass {
#Inject
#DataSource("users")
AgroalDataSource dataSource;
void methodUsingDataSource() {...}
}
Do you have any idea about how to fix this behaviour? This causes me issues when I want to setup Hibernate.
Remove this line:
quarkus.datasource.driver = com.mysql.cj.jdbc.Driver
and use this instead:
quarkus.datasource.db-kind = mysql
You're mixing deprecated and new configuration properties in the default datasource and your combination of it doesn't work.
The example you gave with the named datasource is only using the old configuration properties so it works OK.
Now I need to understand why you didn't have a proper error message. I'll go work on improving that.
Related
Consider the following basic Spring Boot application:
#SpringBootApplication
#ComponentScan(basePackages = "webmvctestproblem.foo")
public class Application {
public static void main(String[] args) {
run(Application.class, args);
}
}
It contains only two other beans. One controller:
#RestController
class Greeter {
#GetMapping("/")
String greet() {
return "Hello, world!";
}
}
And one configuration in webmvctestproblem.foo containing a DataSource dependency:
#Configuration
class Bar {
#Autowired
private DataSource dataSource;
}
Running the application normally (through gradlew bootrun, e.g.) succeeds. Thus, confirming that the app is configured correctly under normal conditions.
However, running the following test causes a runtime error because Spring still attempts to resolve the data source bean dependency on the configuration class:
#RunWith(SpringRunner.class)
#WebMvcTest
public class GreeterTest {
#Test
public void test() throws Exception {
}
}
Of course, there isn't one to resolve because the test is a #WebMvcTest that is designed to create only MVC-related beans.
How do I get rid of the error? I have already tried excluding the configuration class using the excludeFilters attribute of the existing #WebMvcTest annotation and a new #ComponentScan annotation on the test itself. I don't want to resort to turning it into an integration test with #SpringBootTest.
(The project is also available on GitHub for convenience.)
If the DataSource is not mandatory for the test run, simply mock the DataSource with #MockBean in the test class.
#RunWith(SpringRunner.class)
#WebMvcTest
public class GreeterTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private DataSource dataSource;
#Test
public void shouldGreet() throws Exception {
mockMvc
.perform(get("/"))
.andExpect(content().string("Hello, world!"));
}
}
Spring will automatically create a Mock for DataSource and inject it into the running test application context.
Based on your source code it works.
(Btw: Your source code has a minor issue. The Greeter controller class is in the base package but the component scan only scans on the "foo" package. So there will be no Greeter controller on the test run if this isn't fixed.)
#WebMvcTest creates a "slice" of all the beans relevant to WebMvc Testing (Controllers, Json conversion related stuff and so forth).
You can examine the defaults in org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTypeExcludeFilter
In order to find which beans are actually supposed to be Run Spring must resolve them somehow, right?
So spring test tries to understand what should be loaded and what not by passing through these filters.
Now, if you mark anything with #Configuration spring "knows" that this is the place where the place should be found. So it will load the configuration and then will check which beans defined in this configuration must actually be loaded. However the object of configuration itself must be loaded anyway.
Now the process of loading the configuration object includes injecting stuff into these configurations - this is lifecycle of object creation of spring.
And this is a source of mistake here:
#Configuration
class Bar {
#Autowired
private DataSource dataSource;
}
Spring loads Bar and tries as a part of loading this object to autowire the data source. This fails since the DataSource Bean itself is excluded by filters.
Now in terms of solution:
First of all, why do you need this DataSource to be autowired in the Configuration object? Probably you have the bean that uses it, lets call it "MyDao", otherwise I don't see a point of such a construction, since #Configuration-s are basically a place to define bean and you shouldn't put business logic there (if you do - ask a separate question and me/our colleagues will try to help and suggest better implementation).
So I assume you have something like this:
public class MyDao {
private final DataSource dataSource;
public MyDao(DataSource dataSource) {
this.dataSource = dataSource;
}
}
#Configuration
class Bar {
#Autowired
private DataSource dataSource;
#Bean
public MyDao myDao() {
return new MyDao(dataSource);
}
}
In this case however you can rewrite the configuration in a different way:
#Configuration
class Bar {
// Note, that now there is no autowired datasource and I inject the parameter in the bean instead - so that the DataSource will be required only if Spring will have to create that MyDao bean (which it won't obviously)
#Bean
public MyDao myDao(DataSource dataSource) {
return new MyDao(dataSource);
}
}
Now the Bar object will still be created - as I've explained above, but it beans including MyDao of course won't be created, problem solved!
The solution with #Autowired(required=false) provided by #Anish B. should also work - spring will attempt to autowire but won't fail because the data source is unavailable, however you should think whether its an appropriate way to deal with this issue, your decision...
Before you can #Autowire the DataSource bean you need to define the DataSource in some config class or in the properties file. Something like this
spring.datasource.url = jdbc:mysql://localhost/abc
spring.datasource.name=testme
spring.datasource.username=xxxx
spring.datasource.password=xxxx
spring.datasource.driver-class-name= com.mysql.jdbc.Driver
spring.jpa.database=mysql
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
Or
#Configuration
public class JpaConfig {
#Bean
public DataSource getDataSource()
{
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName("org.h2.Driver");
dataSourceBuilder.url("jdbc:h2:file:C:/temp/test");
dataSourceBuilder.username("sa");
dataSourceBuilder.password("");
return dataSourceBuilder.build();
}
You should use
#AutoConfigureMockMvc
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#RunWith(SpringRunner.class)
on your test class, then you can inject
#Autowired
private MockMvc mockMvc;
and use it in test
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andReturn();
i suggest you read the documentation about testing. You can test a spring boot application in 100s of different ways.
WebMvcTest
as suggested by the documentation try, defining what controller class that you want to test in the #WebMvcTest annotation.
#RunWith(SpringRunner.class)
#WebMvcTest(Greeter.class)
public class GreeterTest {
#Autowired
private MockMvc mockMvc;
#Test
public void shouldGreet() throws Exception {
mockMvc
.perform(get("/"))
.andExpect(content().string("Hello, world!"));
}
}
I found postgresql-embedded and write a spring-boot-starter for tests porpoises based on this project. After adding starter dependency in project on run it fails with next error:
org.postgresql.util.PSQLException: FATAL: password authentication failed for user "user"
application.properties
embedded.postgres.database-name=test
embedded.postgres.username=user
embedded.postgres.password=user
embedded.postgres.port=5433
spring.datasource.url=jdbc:postgresql://localhost:5433/test
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.username=user
spring.datasource.password=user
spring.jpa.database=POSTGRESQL
Reason is that spring-boot-starter-data-jpa runs before my starter that startup an embedded PostgreSQL. It is possible to set priority for starter or any other ways ?
Starter sources: https://github.com/esempla/spring-boot-starter-embedded-postgres
You need to set up a dependency on your bean that starts Postgres from the DataSource bean. You can do so with a BeanFactoryPostProcessor in your starter. You might like to take inspiration from Boot's own AbstractDependsOnBeanFactoryPostProcessor, its concrete subclasses such as MongoClientDependsOnBeanFactoryPostProcessor, and how it's used in auto-configuration.
Spring Boot Autoconfiguration is working as it supposed to be as it assumes the Postgresql is already running when the datasource is being auto configured. Since this is a Special Case where you are using an Embedded Postgresql that starts from within the same application context, you will not be able to use Datasource Auto Configuration.
#Configuration
#AutoConfigureAfter({PostgresAutoConfiguration.class})
public class CustomDataSourceConfiguration {
#Value("${datasource.url}")
private String dataSourceUrl;
#Value("${datasource.driver-class}")
private String dataSourceDriverClass;
#Value("${datasource.username}")
private String dataSourceUsername;
#Value("${datasource.password}")
private String dataSourcePassword;
#Bean
public DataSource dataSource() {
// Logic to create the DataSource
}
}
The #AutoConfigureAfter will make sure that the embedded Postgresql is loaded before the datasource is created.
Already done thanks Andy Wilkinson for advises. Problem was solved by creating creating DependsOnBeanFactoryPostProcessor as follow:
#Order
public class DataSourceDependsOnBeanFactoryPostProcessor extends AbstractDependsOnBeanFactoryPostProcessor {
public DataSourceDependsOnBeanFactoryPostProcessor(String... dependsOn) {
super(DataSource.class, LocalContainerEntityManagerFactoryBean.class, dependsOn);
}
}
And adding this class as a configuration in AutoConfiguration class
#Configuration
protected static class EmbeddedPostgresDependencyConfiguration extends DataSourceDependsOnBeanFactoryPostProcessor{
public EmbeddedPostgresDependencyConfiguration(){
super("embeddedPostgres");
}
}
Now this starter works fine for my test cases. Sources can by find there:
https://github.com/esempla/spring-boot-starter-embedded-postgres
I am absolutely new to TestNG, Spring framework etc. and I'm trying to use the annotation #Value access to configuration file via the #Configuration annotation.
All I'm trying to achieve here is to make the console write out "hi" from the config file accessing the value via #Value. I must be obviously missing the whole point of the #Value annotation (or #Autowired or some other annotations) as all I'm gettting is java.lang.NullPointerException.
I have the following three files (reduced to the absolute minimum):
config.properties
a="hi"
TestConfiguration.java
#Configuration
#PropertySource("config.properties")
public class TestConfiguration {
#Value("${a}")
public String A;
}
TrialTest.java
public class TrialTest {
#Autowired
private TestConfiguration testConfiguration;
#Test
public void test() {
System.out.println(testConfiguration.A);
}
}
Thanks a lot.
Try annotate your test class with these:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes={TestConfiguration.class})
[Edit] sorry I didn't see that OP was using TestNG. The essential point is still that the problem is caused by Spring not being bootstrapped. In TestNG that can be done via extending AbstractTestNGSpringContextTests.
Make sure that in your config, you are declaring the PropertySourcesPlaceholderConfigurer bean which can resolve the #Value expressions. Declare this bean:
#Configuration
#PropertySource("config.properties")
public class TestConfiguration {
#Value("${a}")
public String A;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer()
{
return new PropertySourcesPlaceholderConfigurer();
}
}
Note that you do not have to do anything with this bean, just by declaring it, it will allow the #Value annotation expressions to work as expected.
You can either redundantly declare this bean in every class that uses a #Value annotation, but that would be bad practice/style as it would keep overwriting the bean in each new declaration. Instead, place this bean at the top most config which imports other configs using #Value and you can recycle the PropertySourcesPlaceholderConfigurer bean from the one place.
I have tried every option on web but not able to set the values in following method:
#Configuration
#PropertySource("classpath:application.properties")
public class MyDataSource {
#Value("${db.driver}")
private String DB_DRIVER;
#Value("${db.url}")
private String DB_URL;
#Value("${db.username}")
private String DB_USERNAME;
#Value("${db.password}")
private String DB_PASSWORD;
#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(DB_DRIVER);
dataSource.setUrl(DB_URL);
dataSource.setUsername(DB_USERNAME);
dataSource.setPassword(DB_PASSWORD);
return dataSource;
}
}
My application.properties is in main/resources folder and values can be seen in variables in debug mode. But on running app, it shows Property ' ' must not be empty.
EDIT: I am not sure what can be the issue in first case?
So changed the application.property file as suggested and code as below :
#Autowired
protected JdbcTemplate jdbcTemp;
public List<> getData(String id) {
return jdbcTemp.query("SELECT ........,new RowMapper());
}
But getting java.lang.NullPointerException:
If you're using Spring Boot, you can leverage application.properties file by declaring some entries:
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
In this way there is no need to implement a #Configuration class to setup database connection in Spring Boot.
You can deepen more here:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html
By the way, take a look at spring.io
For the java configuration, using Environment instance to obtain the properties seems to be the preferred way, as by default ${..} placeholders are not resolved.
You may use something like this:
#Autowired
private Environment env;
#Bean
public DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("db.driver");
.....
return dataSource;
}
Reasons from the Spring Jira:
it's inconsistent. #PropertySource is the declarative counterpart to ConfigurableEnvironment#addPropertySource. We do not add a
PropertySourcesPlaceholderConfigurer in the latter case, and it would
be inconsistent to do so in the former. it will not be what the user
intended in every (or even most) cases.
It is entirely possible, and even recommended that #Configuration class users forego $ {...} property replacement entirely, in favor of
Environment#getProperty lookups within #Bean methods. For users
following this recommendation, the automatic registration of a
PropertySorucesPlaceholderConfigurer would be confusing when noticed,
and generally undesirable as it's one more moving part. Yes, it's
presence is benign, but not cost-free. a PSPC must visit every bean
definition in the container to interrogate PropertyValues, only to do
nothing in cases where users are going with the
Environment#getProperty approach.
it is solvable (and already solved) by documentation. Proper use of #PropertySource, PropertySourcesPlaceholderConfigurer and other
components is pretty comprehensively documented in the Javadoc for
#Configuration already, and reference documentation is soon to follow.
Me too was getting the error when tried to switch from MySQL to MSSQL. The actual issue was I forgot to put the MSSQL dependency in the service. I used mssql-jdbc
I'm trying to use property files with Spring. Files injection works fine and I have access to my bean.
For instance, this code
#Autowired
private Properties properties;
gives me this error : No qualifying bean of type [java.util.Properties] is defined: expected single matching bean but found 2: csvHeaderProperties,systemProperties.
When I inject properties using resource, I get no error and everything seems fine :
#Component
public Class MyClass {
#Resource(name="csvHeaderProperties")
private Properties properties;
}
However, this code gives me a NPE :
#Component
public class DynCSVService {
#Autowired
private DynCSVDictionnary headerDico;
public void processFile() {
System.out.println(headerDico);
}
}
Both files are located in the same package.
Is there something wrong with this code ?
How should Spring deduce which one to insert?
You have to name it if there are two or more possible matches for injection.
You should use #Named annotation
Here is an example
#Named("userDAO")
public class EfaUserDAOImpl
And inject it where you need;
#Inject
private #Named("userDAO")
EfaUserDAO userDAO;
I found the error : it was in another file where I didn't inject DynCSVService, I used the default constructor.