I have the below configuration class definition in java springboot. However, it fails for reference to property values.
#org.springframework.context.annotation.Configuration
public class HbaseConfig {
#Value("${keytab.user.name}")
private String username;
#Value("${keytab.path}")
private String keytabpath;
#Bean
public Connection getHbaseConnect() throws IOException {
Configuration conf = HBaseConfiguration.create();
Connection connection = ConnectionFactory.createConnection(conf);
UserGroupInformation.setConfiguration(conf);
System.out.println("hbase connect..is connection closed..." + connection.isClosed());
UserGroupInformation.loginUserFromKeytabAndReturnUGI(username, keytabpath);
return connection;
}
#Bean
public Admin getHbaseAdmin(Connection connection) throws IOException{
Admin admin = connection.getAdmin();
return admin;
}
}
application.properties
keytab.user.name="username"
keytab.path="pathtokeytab"
To put it simple, I need the above keytab username and path read from a property file in my HbaseConfig class.
Can you please try out the below method,
#ConfigurationProperties(prefix = "keytab")
public class KeyTabConfig {
private String username;
private String path;
public String getUsername(){ return this.username; }
public String getPath(){ return this.path;}
}
Habseconfig class as follows
#Configuration
#EnableConfigurationProperties({ KeyTabConfig.class})
public class HbaseConfig {
#Bean
public Connection getHbaseConnect(KeyTabConfig keyTabConfig) throws IOException {
Configuration conf = HBaseConfiguration.create();
Connection connection =
ConnectionFactory.createConnection(conf);
UserGroupInformation.setConfiguration(conf);
System.out.println("hbase connect..is connection closed..." +
connection.isClosed());
UserGroupInformation.loginUserFromKeytabAndReturnUGI(keyTabConfig.getUsername(), keyTabConfig.getPath());
return connection;
}
#Bean
public Admin getHbaseAdmin(Connection connection) throws IOException{
Admin admin = connection.getAdmin();
return admin;
}
}
application.properties file as
keytab.username=uname
keytab.path=path
Make sure your property file name should application.properties and location should in
src/main/resources/application.properties
used only #Configuration at the class level and also use #PropertySource to define the location of our properties file.#PropertySource("classpath:configprops.properties") if your property file name is different.
Otherwise, Spring uses the default location (classpath:application.properties).
The actual processing of #Value annotation is performed by BeanPostProcessor and so we cannot use #Value within BeanPostProcessor types.
Examples: https://www.concretepage.com/spring-5/spring-value#placeholder
Link: How exactly does the Spring BeanPostProcessor work?
Related
I was trying to insert data into database in my cucumber tests module in spring-boot application.
When start spring app with test profile (mvn spring-boot:run -Dspring-boot.run.profiles=test) it start up the application and run properly. The issue is during cucumber test execution when try to setup the datasource (as pointed out ** line in the code below) it comes as null. So should I setup the datasource again? If so how.
It's not cucumber test related issue, The issue is I can't access the datasource which have set in the main app.
Below is the code
#ContextConfiguration(classes = MainApp.class, loader = SpringBootContextLoader.class)
#ActiveProfiles("test")
#Configuration
#PropertySource({"classpath:create-sql.xml"})
public class TestHelper {
#Value("${CreateSql}")
private String CreateSql;
#Autowired
private SqlQueryBuilder sqlQueryBuilder;
#Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
#Autowired
private UserPreferenceFormatter formatter;
#Autowired
private DataSource dataSource;
public static void getDataList() throws IOException {
MapSqlParameterSource sqlParamSource = new MapSqlParameterSource();
sqlQueryBuilder = new SqlQueryBuilder();
jdbcTemplate = new NamedParameterJdbcTemplate(dataSource); ****
String parsedSql = sqlQueryBuilder.parseSql(CreateSql,null,null,null);
List<DataSummary> dataSummaries = jdbcTemplate.query(parsedSql, sqlParamSource, new DataSummaryRowMapper(null,formatter));
}
application-test.yml file under resources folder with all spring datasources within test module
app-db-url: jdbc:oracle:....
app-db-user: USERNAME
spring:
datasource:
password: PWD
I went through below solution as well
Solution-1
Solution-2
Deployment module app-config.yml
....
data:
# Database
app-db-url : ##app-db-url##
app-db-user: ##app-db-user##
......
It looks like you are missing code that defines that DataSource bean.
You should have something like this:
#Configuration
public class DataSourceConfig {
#Bean
public DataSource getDataSource() {
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName("org.h2.Driver");
dataSourceBuilder.url("jdbc:h2:mem:test");
dataSourceBuilder.username("SA");
dataSourceBuilder.password("");
return dataSourceBuilder.build();
}
}
or something like that:
#Bean
public DataSource getDataSource() {
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.username("SA");
dataSourceBuilder.password("");
return dataSourceBuilder.build();
}
and the rest of the propertied can go into a property file.
I have spring boot admin project and now I hardcoded the username and passwords in application.properties file like this.
spring.boot.admin.client.username=user
spring.boot.admin.client.password=pass
spring.boot.admin.client.instance.metadata.user.name=user
spring.boot.admin.client.instance.metadata.user.password=pass
But want to get that values from database not hardcoded like this.I want to configs to connect to self register the admin server as a client.I am beginner to SpringBoot. How can I do it? Thanks.
So every configuration in an application.properties file can be configured via Javacode. First you have to create a Datasource for your project. Add the spring-data-jpa dependency to your project and config the datasource.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
more you can find here: A Guide to JPA with Spring
To configure for example the two properties spring.boot.admin.client.username=user and spring.boot.admin.client.password=pass you need to create a #Configuration class which creates a ClientProperties Bean.
#Configuration
public class AdminClientConfig {
private final JdbcTemplate jdbcTemplate;
private final Environment environment;
public AdminClientConfig(JdbcTemplate jdbcTemplate,
Environment environment) {
super();
this.jdbcTemplate = jdbcTemplate;
this.environment = environment;
}
#Bean
public ClientProperties clientProperties() {
ClientProperties cp = new ClientProperties(environment);
cp.setUsername(getUsername());
cp.setPassword(getPassword());
return cp;
}
private String getUsername() {
String username = jdbcTemplate.queryForObject(
"select username from AnyTable where id = ?",
new Object[] { "123" }, String.class);
return username;
}
private String getPassword() {
String password = jdbcTemplate.queryForObject(
"select password from AnyTable where id = ?",
new Object[] { "123" }, String.class);
return password;
}
}
So the JdbcTemplate has already a Database connection and creates the query to get the Username and Password from the Database. The ClientProperties Bean can then be set.
P.S.: This code is not tested but gives you a some hints to get the job done.
I want to use 2 or more jdbcTemplate in my project using application.properties.I try but got runtime exception.
########## My application.properties:-
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/ccm_new
spring.datasource.username=test
spring.datasource.password=test
spring.oracledatasource.url=jdbc:oracle:thin:#localhost:1521:mastera
spring.oracledatasource.password=test
spring.oracledatasource.username=test
spring.oracledatasource.driver-class-name=oracle.jdbc.driver.OracleDriver
#Bean(name = "dsMaster") ############
#Primary
#ConfigurationProperties(prefix="spring.oracledatasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "jdbcMaster") #############
public JdbcTemplate masterJdbcTemplate(#Qualifier("dsMaster") DataSource dsMaster)
{
return new JdbcTemplate(dsMaster);
}
################I use the mysql connection normally but on use of oracle connection i got
org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is java.sql.SQLException: Cannot create JDBC driver of class '' for connect URL 'null'
at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:81)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:371)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:446)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:456)
at enter code here
I got it where i am wrong,I want to make mysql connection through application.properties without #bean configuration.If you want to take 2 or more connection you just need to define all the datasource with their #ConfigurationProperties(prefix="spring.mysqldatasource") different prifix other than "spring.datasource".prifix " spring.datasource" use only when we need to make connection from only one database.Here is the final working code example:-
application.properties
spring.mysqldatasource.driver-class-name=com.mysql.jdbc.Driver
spring.mysqldatasource.url=jdbc:mysql://localhost:3306/ccm_new
spring.mysqldatasource.username=test
spring.mysqldatasource.password=test
spring.mysqldatasource.dbcp2.initial-size=5
spring.mysqldatasource.dbcp2.max-total=15
spring.mysqldatasource.dbcp2.pool-prepared-statements=true
spring.oracledatasource.url=jdbc:oracle:thin:#localhost:1521:mastera
spring.oracledatasource.password=test
spring.oracledatasource.username=test
spring.oracledatasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.oracledatasource.dbcp2.initial-size=5
spring.oracledatasource.dbcp2.max-total=15
spring.oracledatasource.dbcp2.pool-prepared-statements=true
#Configuration
public class PrototypeUtility {
#Bean(name = "dsMaster")
#Primary
#ConfigurationProperties(prefix="spring.oracledatasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "jdbcMaster")
public JdbcTemplate masterJdbcTemplate(#Qualifier("dsMaster") DataSource dsMaster) {
return new JdbcTemplate(dsMaster);
}
#Bean(name = "dsMasterMysql")
#ConfigurationProperties(prefix="spring.mysqldatasource")
public DataSource primaryDataSourceMysql() {
return DataSourceBuilder.create().build();
}
#Bean(name = "jdbcMasterMysql")
public JdbcTemplate masterMysqlJdbcTemplate(#Qualifier("dsMasterMysql") DataSource dsMasterMysql) {
return new JdbcTemplate(dsMasterMysql);
}
}
and then i autowired the both connection :-
#Autowired
private JdbcTemplate jdbcMasterMysql;
#Autowired
public JdbcTemplate jdbcMaster;
This code run successfully for me .
If any one have doubt,Don't hesitate to ask.
I got it where i am wrong,I want to make mysql connection through application.properties without #bean configuration.If you want to take 2 or more connection you just need to define all the datasource with their #ConfigurationProperties(prefix="spring.mysqldatasource") different prifix other than "spring.datasource".prifix " spring.datasource" use only when we need to make connection from only one database.Here is the final working code example:-
application.properties
spring.mysqldatasource.driver-class-name=com.mysql.jdbc.Driver
spring.mysqldatasource.url=jdbc:mysql://localhost:3306/ccm_new
spring.mysqldatasource.username=test
spring.mysqldatasource.password=test
spring.mysqldatasource.dbcp2.initial-size=5
spring.mysqldatasource.dbcp2.max-total=15
spring.mysqldatasource.dbcp2.pool-prepared-statements=true
spring.oracledatasource.url=jdbc:oracle:thin:#localhost:1521:mastera
spring.oracledatasource.password=test
spring.oracledatasource.username=test
spring.oracledatasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.oracledatasource.dbcp2.initial-size=5
spring.oracledatasource.dbcp2.max-total=15
spring.oracledatasource.dbcp2.pool-prepared-statements=true
#Configuration
public class PrototypeUtility {
#Bean(name = "dsMaster")
#Primary
#ConfigurationProperties(prefix="spring.oracledatasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "jdbcMaster")
public JdbcTemplate masterJdbcTemplate(#Qualifier("dsMaster") DataSource dsMaster) {
return new JdbcTemplate(dsMaster);
}
#Bean(name = "dsMasterMysql")
#ConfigurationProperties(prefix="spring.mysqldatasource")
public DataSource primaryDataSourceMysql() {
return DataSourceBuilder.create().build();
}
#Bean(name = "jdbcMasterMysql")
public JdbcTemplate masterMysqlJdbcTemplate(#Qualifier("dsMasterMysql") DataSource dsMasterMysql) {
return new JdbcTemplate(dsMasterMysql);
}
}
In my application.properties i have some spring.datasource fields
spring.jpa.database-platform=org.hibernate.dialect.Oracle10gDialect
spring.datasource.url=jdbc:oracle:thin:#localhost:1521:XE
spring.datasource.username=talon
spring.datasource.password=talon
These should be retrieved from a #Configuration annotated class
#Configuration
public class Db {
#NotNull
private String username;
#NotNull
private String password;
#NotNull
private String url;
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setUrl(String url) {
this.url = url;
}
public Db() {
OracleDataSource dataSource = null;
try {
dataSource = new OracleDataSource();
dataSource.setUser(username);
dataSource.setPassword(password);
dataSource.setURL(url);
dataSource.setImplicitCachingEnabled(true);
dataSource.setFastConnectionFailoverEnabled(true);
Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery("select * from BOOK");
rs.next();
System.out.println(rs.getString(2));
} catch (SQLException e) {
e.printStackTrace();
}
}
}
But I'm unable to retrieve the username password and url from there, should I add another annotation somewhere or what am I doing
Like this I have the error:
java.sql.SQLException: Invalid Oracle URL specified: OracleDataSource.makeURL
If I set the proper url with dataSource.setURL("jdbc:oracle:thin:#localhost:1521:XE"); it can't read the username and password since they are null and I have java.sql.SQLException: ORA-01017: invalid username/password; logon denied
You have two issues:
You need to use another annotation in order for your fields to be populated. So annotate the class with #ConfigurationProperties("spring.datasource")
You cannot initialize your OracleDataSource directly in your constructor (i.e. Db()) because the properties (your username/password/url fields) are not populated during the call of the constructor, so one thing that you can do is create another method and annotate that with #PostContruct in order for your dataSource to be correctly created.
An example:
#PostConstruct
private void init() {
// your data source initialization
}
One advice would be to change the way to initialize your dataSource and instead of trying to create it inside your constructor, you can rather create a new method which you can annotate with #Bean and make it return your dataSource and you can use it later using #Autowired.
I'm developing a webservice using Dropwizard JDBI framework.
Now, instead of having a db configurations in yaml file, I want to use 'user specified params' what i mean to say is, the db configs will be provided through the endpoint url.
Is having custom creds possible through dropwizard jdbi?
if yes, what changes should i be thinking to do in the code while referring this ? ->
http://dropwizard.readthedocs.org/en/latest/manual/jdbi.html
I understand, in normal flow, the service method gets the config details in the run method -
-- Config Class
public class ExampleConfiguration extends Configuration {
#Valid
#NotNull
#JsonProperty
private DatabaseConfiguration database = new DatabaseConfiguration();
public DatabaseConfiguration getDatabaseConfiguration() {
return database;
}
}
-- Service Class
#Override
public void run(ExampleConfiguration config,
Environment environment) throws ClassNotFoundException {
final DBIFactory factory = new DBIFactory();
final DBI jdbi = factory.build(environment, config.getDatabaseConfiguration(), "postgresql");
final UserDAO dao = jdbi.onDemand(UserDAO.class);
environment.addResource(new UserResource(dao));
}
-- and yaml
database:
# the name of your JDBC driver
driverClass: org.postgresql.Driver
# the username
user: pg-user
# the password
password: iAMs00perSecrEET
# the JDBC URL
url: jdbc:postgresql://db.example.com/db-prod
But in this case, I might get the config details in the Resource level...
smthing like -
#GET
#Path(value = "/getProduct/{Id}/{dbUrl}/{dbUname}/{dbPass}")
#Produces(MediaType.APPLICATION_JSON)
public Product getProductById(#PathParam(value = "Id") int Id,
#PathParam(value = "dbUrl") String dbUrl,
#PathParam(value = "dbUname") String dbUname,
#PathParam(value = "dbPath") String dbPass) {
//I have to connect to the DB here! using the params i have.
return new Product(); //should return the Product
}
I'd appreciate if someone can point me a direction.
Why not just use JDBI directly?
#GET
#Path(value = "/getProduct/{Id}/{dbUrl}/{dbUname}/{dbPass}")
#Produces(MediaType.APPLICATION_JSON)
public Product getProductById(#PathParam(value = "Id") int id,
#PathParam(value = "dbUrl") String dbUrl,
#PathParam(value = "dbUname") String dbUname,
#PathParam(value = "dbPass") String dbPass) {
DataSource ds = JdbcConnectionPool.create(dbUrl, dbUname, dbPass);
DBI dbi = new DBI(ds);
ProductDAO dao = dbi.open(ProductDao.class);
Product product = dao.findById(id);
dao.close();
ds.dispose();
return product;
}
#RegisterMapper(ProductMapper.class)
static interface ProductDao {
#SqlQuery("select id from product_table where id = :id") // Whatever SQL query you need to product the product
Product findById(#Bind("id") int id);
#SqlQuery("select * from product_table")
Iterator<Product> findAllProducts();
}
static class ProductMapper implements ResultSetMapper<Product> {
public Product map(int index, ResultSet r, StatementContext ctx) throws SQLException {
return new Product(r.getInt("id")); // Whatever product constructor you need
}
}
There's a notion in the spring world of using a database router (reference: https://spring.io/blog/2007/01/23/dynamic-datasource-routing/).
You likely could setup a proxy for the database connection factory passed to DBI. That proxy would then get the credentials from a thread local (perhaps) and return the real connection giving you what you're after and still let you use the run type proxies.