I am having problem understanding the usage of #ConfigurationProperties. In my application properties file, all of the variables "is an unknown property" with yellow underline. I run the application but the same thing happens. Is there a way to link the configuration at application.properties to my datasource function?
If I use
spring.datasource.url = jdbc:mysql://127.0.0.1/trs?useSSL=false
it works just fine but it i use app.datasource.url it wouldn't work
My application.properties file
# Primary DataSource configuration
app.datasource.url=jdbc:mysql://127.0.0.1/trs?useSSL=false
app.datasource.username=user
app.datasource.password=pass
my primary .java file
#ConfigurationProperties(prefix="app.datasource")
#Primary
#Bean
public DataSource dataSource() {
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://127.0.0.1/trs?useSSL=false");
dataSource.setUsername("user");
dataSource.setPassword("pass");
return dataSource;
}
For your application.properties, you need a corresponding java class to load the configuration in a bean. Like this -
#ConfigurationProperties(prefix="app.datasource")
#Component
public class DataSourceConfig{
private String url;
private String username;
private String password;
...
}
Now, where you want to use the DataSourceConfig bean, you can inject like this,
#Autowired
DataSourceConfig dataSourceConfig;
And once you have the object just do,
dataSourceConfig.getUrl();
dataSourceConfig.getUsername();
dataSourceConfig.getPassword();
Related
I am trying to run the test class below with MockBean BaaisnEvcIdMSRepository. It's connecting to the real database (I am getting an exception). Please could you explain why it is not using the mock?
Test class
#RunWith(SpringRunner.class)
#SpringBootTest
public class BaaisnEvcIdMSServiceTest {
#Autowired
BaaisnEvcIdMSService basBaaisnEvcIdMSService;
#MockBean
BaaisnEvcIdMSRepository baaisnEvcIdMSRepository;
#Test
public void getQueryEvcidServiceTest() {
BaaisnEvcIdRequest baaisnEvcIdRequest = new BaaisnEvcIdRequest();
baaisnEvcIdRequest.setLata(650);
baaisnEvcIdRequest.setProduct_type("abc");
baaisnEvcIdRequest.setSvc_type("xyz");
RowMapperServerResponse rowMapperServerResponse = new RowMapperServerResponse();
rowMapperServerResponse.setId(1);
rowMapperServerResponse.setName("sample");
Mockito.when(baaisnEvcIdMSRepository.getQueryEvcidRepository(baaisnEvcIdRequest)).thenReturn(rowMapperServerResponse);
assertEquals(rowMapperServerResponse, basBaaisnEvcIdMSService.getQueryEvcidService(baaisnEvcIdRequest));
}
}
Repository
#Repository
public class BaaisnEvcIdMSRepository {
#Autowired
private JdbcTemplate jdbcTemplate;
#Transactional
public RowMapperServerResponse getQueryEvcidRepository(BaaisnEvcIdRequest baaisnEvcIdRequest) {
RowMapperServerResponse rowMapperServerResponse = jdbcTemplate.queryForObject(
"select * from Master_Circuit WHERE master_ckt_id = ( select max(master_ckt_id) from master_circuit WHERE product = ? AND id_type = ?)",
new Object[]{baaisnEvcIdRequest.getProduct_type(),baaisnEvcIdRequest.getLata()}, new BaaisnRowMapper());
return rowMapperServerResponse;
}
}
exception
java.sql.SQLException: com.informix.asf.IfxASFException: Attempt to connect to database server (tdclsvi1vd002_tcp_ldap) failed.
at com.informix.jdbc.IfxSqliConnect.<init>(IfxSqliConnect.java:1691) ~[jdbc-4.10.8.1.jar:4.10.8.1]
As this is unit test, you do not need spring context. So you will need something like
#RunWith(MockitoJUnitRunner.class)
public class BaaisnEvcIdMSServiceTest {
#InjectMocks
BaaisnEvcIdMSService basBaaisnEvcIdMSService;
#Mock
BaaisnEvcIdMSRepository baaisnEvcIdMSRepository;
#Test
public void getQueryEvcidServiceTest() {
BaaisnEvcIdRequest baaisnEvcIdRequest = new BaaisnEvcIdRequest();
baaisnEvcIdRequest.setLata(650);
baaisnEvcIdRequest.setProduct_type("abc");
baaisnEvcIdRequest.setSvc_type("xyz");
RowMapperServerResponse rowMapperServerResponse = new RowMapperServerResponse();
rowMapperServerResponse.setId(1);
rowMapperServerResponse.setName("sample");
Mockito.when(baaisnEvcIdMSRepository.getQueryEvcidRepository(baaisnEvcIdRequest)).thenReturn(rowMapperServerResponse);
assertEquals(rowMapperServerResponse, basBaaisnEvcIdMSService.getQueryEvcidService(baaisnEvcIdRequest));
}
}
Assuming you indeed aim for the integration test and not for the unit test (in this case, an answer provided by #Yogesh Badke is the way to go), here are some points for consideration:
Place the breakpoint in the test and check the type of the baaisnEvcIdMSRepository. If should be some sort of proxy generated by Mockito.
If you have a proxy indeed, check the reference to the repository in BaaisnEvcIdMSService instance. It also should point on proxy and not on the real class.
Make sure BaaisnEvcIdRequest implements the equals method otherwise Mockito might not find the expectation when the real service runs the code against the repository proxy (assuming that its a proxy indeed as I've described in "2")
Provide the stacktrace of how exactly it fails with exception, it might also contain some beneficial information...
I'm having the following code:
#Data
#Validated
#ConfigurationProperties
public class Keys {
private final Key key = new Key();
#Data
#Validated
#ConfigurationProperties(prefix = "key")
public class Key {
private final Client client = new Client();
private final IntentToken intentToken = new IntentToken();
private final Intent intent = new Intent();
private final OAuth oauth = new OAuth();
private final ResourceToken resourceToken = new ResourceToken();
#Valid #NotNull private String authorization;
#Valid #NotNull private String bearer;
...
}
}
That is an instance representing a properties file such as:
key.authorization=Authorization
key.bearer=Bearer
..
As I can have different sources for the properties (properties file, MongoDB, etc), I have a client that inherit from Keys as follow:
Properties files source
#Component
#Configuration
#Primary
#PropertySource("classpath:${product}-keys.${env}.properties")
//#JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class CustomerKeysProperties extends Keys {
}
Mongo source
#Data
#EqualsAndHashCode(callSuper=true)
#Component
//#Primary
#Document(collection = "customerKeys")
public class CustomerKeysMongo extends Keys {
#Id
private String id;
}
I just select the source I want to use annotating the class with #Primary. In the example above, CustomerKeysProperties is the active source.
All this work fine.
The issue I have is when I try to convert an instance of CustomerKeysProperties into JSON, as in the code below:
#SpringBootApplication
public class ConverterUtil {
public static void main(String[] args) throws Exception {
SpringApplication.run(ConverterUtil.class, args);
}
#Component
class CustomerInitializer implements CommandLineRunner {
#Autowired
private Keys k;
private final ObjectMapper mapper = new ObjectMapper();
#Override
public void run(String... args) throws Exception {
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
//mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
String jsonInString = mapper.writeValueAsString(k);
System.out.println(jsonInString);
}
}
}
While k contains all the properties set, the conversion fails:
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: x.client.customer.properties.CustomerKeysProperties$$EnhancerBySpringCGLIB$$eda308bd["CGLIB$CALLBACK_0"]->org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor["advised"]->org.springframework.aop.framework.ProxyFactory["targetSource"]->org.springframework.aop.target.SingletonTargetSource["target"]->x.client.customer.properties.CustomerKeysProperties$$EnhancerBySpringCGLIB$$4fd6c568["CGLIB$CALLBACK_0"])
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77)
at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1191)
And if I uncomment
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
as suggested in the logs, I have an infinite loop happening in Jackson causing a stackoverflow:
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:727)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:719)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:119)
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:79)
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:18)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:727)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:719)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
..
Questions
At the end, I just want to provide an Util class than can convert a properties file in a JSON format that will be stored in MongoDB.
How can I solve this problem ?
Without passing through the object above, how can I transform a properties file into JSON ?
Can I save an arbitrary Java bean in MongoDB, with the conversion to JSON automagically done ?
The answer to any of the 3 questions above would be helpful.
Notes
To be noted that I use lombok. Not sure if this is the problem.
Another guess is that I'm trying to serialize a Spring managed bean and the proxy it involve cause jackson to not be able to do the serialization ? If so, what can be the turn-around ?
Thanks!
So found the problem:
jackson can't process managed bean.
The turn around was
try (InputStream input = getClass().getClassLoader().getResourceAsStream("foo.properties")) {
JavaPropsMapper mapper = new JavaPropsMapper();
Keys keys = mapper.readValue(input, Keys.class);
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
String res = ow.writeValueAsString(keys);
System.out.println(res);
} catch (IOException e) {
e.printStackTrace();
}
where Keys was the Spring managed bean I was injecting.
And:
JavaPropsMapper come from:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-properties</artifactId>
</dependency>
I'm testing a service layer and not sure how to mock ObjectMapper().readValue in that class. I'm fairly new to mockito and could figure out how to do it.
The following is my code,
service.java
private configDetail fetchConfigDetail(String configId) throws IOException {
final String response = restTemplate.getForObject(config.getUrl(), String.class);
return new ObjectMapper().readValue(response, ConfigDetail.class);
}
ServiceTest.java
#Test
public void testgetConfigDetailReturnsNull() throws Exception {
restTemplate = Mockito.mock(restTemplate.class);
Service service = new Service();
Config config = Mockito.mock(Config.class);
ObjectMapper objMapper = Mockito.mock(ObjectMapper.class);
Mockito.doReturn("").when(restTemplate).getForObject(anyString(), eq(String.class));
Mockito.doReturn(configDetail).when(objMapper).readValue(anyString(),eq(ConfigDetail.class));
assertEquals(configDetail, service.getConfigDetail("1234"));
}
I get the following results when I run this test,
com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input
at [Source: (String)""; line: 1, column: 0]
Posting ServiceTest.Java here
#RunWith(MockitoJUnitRunner.class)
public class ConfigServiceTest {
#Mock
private ConfigPersistenceService persistenceService;
#InjectMocks
private ConfigService configService;
#Mock
ConfigDetail configDetail;
#Mock
private RestTemplate restTemplate;
#Mock
private ObjectMapper objMapper;
#Mock
private Config config;
#Test
public void testgetConfigDetailReturnsNull() throws Exception {
ObjectMapper objMapper = Mockito.mock(ObjectMapper.class);
Mockito.doReturn(ucpConfig).when(persistenceService).findById("1234");
Mockito.doReturn("").when(restTemplate).getForObject(anyString(), eq(String.class));
Mockito.when((objMapper).readValue(“”,ConfigDetail.class)).thenReturn(configDetail);
assertEquals(ConfigDetail, ConfigService.getConfigDetail("1234"));
}
}
With your current Service class it would be difficult to mock ObjectMapper, ObjectMapper is tightly coupled to fetchConfigDetail method.
You have to change your service class as follows to mock ObjectMapper.
#Service
public class MyServiceImpl {
#Autowired
private ObjectMapper objectMapper;
private configDetail fetchConfigDetail(String configId) throws IOException {
final String response = restTemplate.getForObject(config.getUrl(), String.class);
return objectMapper.readValue(response, ConfigDetail.class);
}
}
Here what I did is instead of creating objectMapper inside the method I am injecting that from outside (objectMapper will be created by Spring in this case)
Once you change your service class, you can mock the objectMapper as follows.
ObjectMapper mockObjectMapper = Mockito.mock(ObjectMapper.class);
Mockito.when(mockObjectMapper.readValue(anyString(), any(ConfigDetail.class)).thenReturn(configDetail);
Problem is with the this line where you are mocking the call to objectmapper.
Mockito.when((objMapper).readValue(“”,ConfigDetail.class)).thenReturn(configDetail);
Correct syntax is
Mockito.when(objMapper.readValue(“”,ConfigDetail.class)).thenReturn(configDetail);
Notice the bracket position. When using Spy or Verify, the bracket position is diff. then when using when-then syntax.
Mocking objects created in a SUT is IMO the single biggest limitation of mockito. Use jmockit or powerMock or checkout the offical mockito way of handling this. https://github.com/mockito/mockito/wiki/Mocking-Object-Creation
I prepared website in Spring/Maven with use of MySQL db, Tomcat. I decided to use Heroku free account, because I have domain, db and Java in one place. Problem is that Heroku uses postgresql for free but mysql is payable. I would like to ask you help and explanation clearly what I should change in my application's code(pom.xml, hibernate.properties etc) to use postgresql instead of mysql. Below I show files I think should be changed. Do I have to add any new files? I beg your indulgence.
***POM.XML***
jdbc.driver.class.name =com.mysql.jdbc.Driver
jdbc.url =jdbc:mysql://localhost:3306/web_users?useSSL=false&characterEncoding=UTF-8
jdbc.user.name =root
jdbc.password =root
hibernate.hbm2ddl.auto =update
hibernate.show_sql =true
hibernate.format_sql =true
hibernate.generate_statistics=false
***HibernateConfig.java***
#Configuration
#PropertySource(value = {"classpath:hibernate.properties"})
#EnableJpaRepositories(basePackages = "com.website.dao")
public class HibernateConfig {
#Autowired
private Environment environment;
// 1. create DataSource
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(environment.getRequiredProperty("jdbc.driver.class.name"));
dataSource.setPassword(environment.getRequiredProperty("jdbc.password"));
dataSource.setUsername(environment.getRequiredProperty("jdbc.user.name"));
dataSource.setUrl(environment.getRequiredProperty("jdbc.url"));
return dataSource;
}
// 2. EntityManagerFactory
#Bean
public EntityManagerFactory entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
Properties properties = new Properties();
properties.put("hibernate.hbm2ddl.auto", environment.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.show_sql", environment.getProperty("hibernate.show_sql"));
properties.put("hibernate.format_sql", environment.getProperty("hibernate.format_sql"));
properties.put("hibernate.generate_statistics", environment.getProperty("hibernate.generate_statistics"));
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setPackagesToScan("com.website.model");
factoryBean.setJpaVendorAdapter(vendorAdapter);
factoryBean.setJpaProperties(properties);
factoryBean.setDataSource(dataSource());
factoryBean.afterPropertiesSet();
return factoryBean.getObject();
}
I am trying to implement multiple database with Spring Boot Hikari CP. I am getting
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] is defined: expected single matching bean but found 2: hikari_primary,hikari_secondary
For your reference I am attaching my spring boot datasource configuration files,
please dont go over primary and secondary naming conventions(they dont represent priorities), my requirement it to have two connection pool for two different databases.
Any help is appreciated
1.application.properties
spring.datasource.dataSourceClassName=com.microsoft.sqlserver.jdbc.SQLServerDataSource
primary.spring.datasource.url=jdbc:sqlserver://xxx.xxx.xxx.xxx:1433;DatabaseName=training
primary.spring.datasource.username=training
primary.spring.datasource.password=training
primary.spring.datasource.poolName=hikari_primary
primary.spring.datasource.maximumPoolSize=5
primary.spring.datasource.minimumIdle=3
primary.spring.datasource.maxLifetime=2000000
primary.spring.datasource.connectionTimeout=30000
primary.spring.datasource.idleTimeout=30000
primary.spring.datasource.pool-prepared-statements=true
primary.spring.datasource.max-open-prepared-statements=250
secondary.spring.datasource.url=jdbc:sqlserver://xxx.xxx.xxx.xxx:1433;DatabaseName=dev_xxxxx_core_v3
secondary.spring.datasource.username=developer
secondary.spring.datasource.password=Developer
secondary.spring.datasource.poolName=hikari_secondary
secondary.spring.datasource.maximumPoolSize=50
secondary.spring.datasource.minimumIdle=30
secondary.spring.datasource.maxLifetime=2000000
secondary.spring.datasource.connectionTimeout=30000
secondary.spring.datasource.idleTimeout=30000
secondary.spring.datasource.pool-prepared-statements=true
secondary.spring.datasource.max-open-prepared-statements=300
2. PrimaryDataSourceConfig.java
#Configuration
public class PrimaryDataSourceConfig {
#Value("${primary.spring.datasource.username}")
private String user;
#Value("${primary.spring.datasource.password}")
private String password;
#Value("${primary.spring.datasource.url}")
private String dataSourceUrl;
#Value("${spring.datasource.dataSourceClassName}")
private String dataSourceClassName;
#Value("${primary.spring.datasource.poolName}")
private String poolName;
#Value("${primary.spring.datasource.connectionTimeout}")
private int connectionTimeout;
#Value("${primary.spring.datasource.maxLifetime}")
private int maxLifetime;
#Value("${primary.spring.datasource.maximumPoolSize}")
private int maximumPoolSize;
#Value("${primary.spring.datasource.minimumIdle}")
private int minimumIdle;
#Value("${primary.spring.datasource.idleTimeout}")
private int idleTimeout;
#Bean(name="hikari_primary")
public HikariDataSource getHikariDataSourcePrimary() {
Properties dsProps = new Properties();
dsProps.put("url", dataSourceUrl);
dsProps.put("user", user);
dsProps.put("password", password);
Properties configProps = new Properties();
configProps.put("dataSourceClassName", dataSourceClassName);
configProps.put("poolName", poolName);
configProps.put("maximumPoolSize", maximumPoolSize);
configProps.put("minimumIdle", minimumIdle);
configProps.put("minimumIdle", minimumIdle);
configProps.put("connectionTimeout", connectionTimeout);
configProps.put("idleTimeout", idleTimeout);
configProps.put("dataSourceProperties", dsProps);
HikariConfig hc = new HikariConfig(configProps);
HikariDataSource ds = new HikariDataSource(hc);
return ds;
}
}
3. SecondayDataSourceConfig.java
#Configuration
public class SecondaryDataSourceConfig {
#Value("${secondary.spring.datasource.username}")
private String user;
#Value("${secondary.spring.datasource.password}")
private String password;
#Value("${secondary.spring.datasource.url}")
private String dataSourceUrl;
#Value("${spring.datasource.dataSourceClassName}")
private String dataSourceClassName;
#Value("${secondary.spring.datasource.poolName}")
private String poolName;
#Value("${secondary.spring.datasource.connectionTimeout}")
private int connectionTimeout;
#Value("${secondary.spring.datasource.maxLifetime}")
private int maxLifetime;
#Value("${secondary.spring.datasource.maximumPoolSize}")
private int maximumPoolSize;
#Value("${secondary.spring.datasource.minimumIdle}")
private int minimumIdle;
#Value("${secondary.spring.datasource.idleTimeout}")
private int idleTimeout;
#Bean(name="hikari_secondary")
public HikariDataSource getHikariDataSourceSecondary() {
Properties dsProps = new Properties();
dsProps.put("url", dataSourceUrl);
dsProps.put("user", user);
dsProps.put("password", password);
Properties configProps = new Properties();
configProps.put("dataSourceClassName", dataSourceClassName);
configProps.put("poolName", poolName);
configProps.put("maximumPoolSize", maximumPoolSize);
configProps.put("minimumIdle", minimumIdle);
configProps.put("minimumIdle", minimumIdle);
configProps.put("connectionTimeout", connectionTimeout);
configProps.put("idleTimeout", idleTimeout);
configProps.put("dataSourceProperties", dsProps);
HikariConfig hc = new HikariConfig(configProps);
HikariDataSource ds = new HikariDataSource(hc);
return ds;
}
}
4. Application.java
#SpringBootApplication
#ComponentScan("com.xxxx.springsql2o")
#EnableAutoConfiguration
public class Application
{
public static void main( String[] args )
{
SpringApplication.run(Application.class, args);
}
#Autowired
#Qualifier("hikari_primary")
DataSource hikariDataSourcePrimary;
#Autowired
#Qualifier("hikari_secondary")
DataSource hikariDataSourceSecondary;
#Bean(name= "primary_db")
public Sql2o getPrimarySql2o()
{
return new Sql2o(hikariDataSourcePrimary);
}
#Bean(name= "secondary_db")
public Sql2o getSecondarySql2o()
{
return new Sql2o(hikariDataSourceSecondary);
}
}
Spring boot is auto-configuring your application via #EnableAutoConfiguration (note that this annotation is already included in the composed #SpringBootApplication annotation). So my guess would be that you have some dependency that spring is trying to auto-configure (e.g. JPA) which uses/needs DataSource. If you can live with this, you can add #Primary to on of your DataSource Bean provider methods in order to satisfy that dependency.
So, for instance:
#Bean(name="hikari_primary")
#Primary
public HikariDataSource getHikariDataSourcePrimary() {...
Even if this should work, it would be recommended to remove auto-configuration for e.g. JPA or whatever spring boot is trying to auto-configure but you don't use/need and configure everything manually as it suits your application needs. Have two databases is certainly a custom configuration and does not conform to the spring boot easy-out-of-the-box approach.