How to use JMX MBean for HikariCP in Spring boot application? - java

How to use JMX MBean for HikariCP in Spring boot application? I have a code like this:
#SpringBootApplication
public class App() { ... }
And other class:
#Configuration
public class DatabaseCfg() {
#Bean
#ManagedOperation
public DataSource ds (#Value("${hikari.proprerties}") String config) {
HikariConfig hikariConfig = new HikariConfig(config);
return new HikariDataSource(hikariConfig);
}
In Java Mission Control (or JMX Console) a saw only Datasource managed bean, not JMX MBean for HikariCP (link). Is it possible to add it too?

In Spring Boot 2.0+ you can set the register-mbeans property in your application.properties file
spring.datasource.hikari.register-mbeans = true
If you are using an earlier version of Spring Boot you will also have to set the datasource
spring.datasource.type = com.zaxxer.hikari.HikariDataSource

I believe on your hikariConfig you need to set a few additional settings. You need to register the MBeans and set a pool name on the configuration.
HikariConfig hiakriConfig = new HikariConfig(config);
hikariConfig.setRegisterMbeans(true);
kikariConfig.setPoolName("my-pool-1");
Yes you obviously could drive these through the properties as well. I'm not sure if you are including these in your properties file as they are not listed. Also please note you are spelling properties wrong (#Value("${ds.proprerties}") should probably should be (#Value("${ds.properties}") but I'm not sure how you actually have named variables and property files. You may want to double check if that is where you want to set all of the properties.

Try this. Exclude your Hiakri DataSource Bean from being registered by Spring.
#Resource
private ObjectProvider<MBeanExporter> mBeanExporter;
#Bean("dataSource")
public DataSource createDataSource() {
String url = hikariDataSourceConfig.getUrl();
String username = hikariDataSourceConfig.getUsername();
String password = hikariDataSourceConfig.getPassword();
long idleTimeoutInMilliSeconds =
hikariDataSourceConfig.getIdleTimeOutInMilliseconds();
long maxLifetimeInMilliseconds =
hikariDataSourceConfig.getMaxLifetimeInMilliseconds();
int maximumPoolSize = hikariDataSourceConfig.getMaximumPoolSize();
int minimumIdle = hikariDataSourceConfig.getMinimumIdle();
String poolName = "HikariDataSource";
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setRegisterMbeans(true);
hikariConfig.setJdbcUrl(url);
hikariConfig.setUsername(username);
hikariConfig.setPassword(password);
hikariConfig.setIdleTimeout(idleTimeoutInMilliSeconds);
hikariConfig.setMaxLifetime(maxLifetimeInMilliseconds);
hikariConfig.setMaximumPoolSize(maximumPoolSize);
hikariConfig.setMinimumIdle(minimumIdle);
hikariConfig.setPoolName(poolName);
HikariDataSource dataSource = new HikariDataSource(hikariConfig);
mBeanExporter
.ifUnique((exporter) -> exporter.addExcludedBean("dataSource"));
return dataSource;
}

Related

Can i run liquibase database migrations after the app was initialized with spring boot?

Context
I am trying to start my spring app without a database(so when no database is available at initialization the app won't be stopped), i managed to do this with the following commands in app.prop:
#DB should not kill the app
spring.sql.init.continue-on-error=true //app should continue if a sql init error arrises
spring.liquibase.enabled=false // liquibase bean shouldn't be initialized at start up, without this command the app crashes anyway
spring.jpa.hibernate.ddl-auto=none
Now the only thing that i need to do is figure a way so when the app does make a successful connection with the db the liquibase migration files will get executed. For this task I understood I need to customize the liquibase bean, the following code shows my progress so far:
#Configuration
public class Config {
#Value("${postgres.host}")
private String host;
#Value("${postgres.port}")
private Integer port;
#Value("${postgres.database}")
private String database;
#Value("${postgres.user}")
private String user;
#Value("${postgres.password}")
private String password;
#Value("${spring.liquibase.change-log}")
private String changelog;
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.postgresql.Driver");
dataSource.setUrl(String.format("jdbc:postgresql://%s:%d/%s", host, port, database));
dataSource.setUsername(user);
dataSource.setPassword(password);
return dataSource;
}
#Bean
public SpringLiquibase liquibase() {
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setDataSource(dataSource());
liquibase.setChangeLog(changelog);
return liquibase;
}
}
Preferably if the database is down the bean should not be created and if the database is running/ the server established connection with the db at some point the bean will be brought in the context and execute the migration files, i don't know if that is possible as I am a newbie,but let me know if you have any suggestions.

Configure OracleDataSource programmatically in Spring Boot with a default schema

How to configure Oracle DataSource programmatically in Spring Boot with a default schema?
#Bean
public DataSource getDataSource() throws SQLException {
OracleDataSource d = new OracleDataSource();
d.setURL(Secrets.get("DB_URL"));
d.setUser(Secrets.get("DB_USER"));
d.setPassword(Secrets.get("DB_PASS"));
// d.setSchema(System.getenv("DB_SCHEMA")); ???
return d;
}
You can't change the schema in the OracleDataSource or using connection URL, you need to execute
ALTER SESSION SET CURRENT_SCHEMA=targetschema;
statement as explained in this answer. According to Connection Properties Recognized by Oracle JDBC Drivers there is no driver property for initial schema.
Full example:
#Bean
public DataSource getDataSource() throws SQLException {
OracleDataSource oracleDs = new OracleDataSource();
oracleDs.setURL(Secrets.get("DB_URL"));
oracleDs.setUser(Secrets.get("DB_USER"));
oracleDs.setPassword(Secrets.get("DB_PASS"));
// other Oracle related settings...
HikariDataSource hikariDs = new HikariDataSource();
hikariDs.setDataSource(oracleDs);
hikariDs.setConnectionInitSql("ALTER SESSION SET CURRENT_SCHEMA = MY_SCHEMA");
return hikariDs;
}
Try to add sql execution into datasources creation method
#Bean
public DataSource getDataSource() throws SQLException {
OracleDataSource d = new OracleDataSource();
d.setURL(Secrets.get("DB_URL"));
d.setUser(Secrets.get("DB_USER"));
d.setPassword(Secrets.get("DB_PASS"));
Resource initSchema = new ClassPathResource("scripts/schema-alter.sql");
DatabasePopulator databasePopulator = new ResourceDatabasePopulator(initSchema);
DatabasePopulatorUtils.execute(databasePopulator, dataSource);
return d;
}
In scripts/schema-alter.sql will be this code
ALTER SESSION SET CURRENT_SCHEMA=targetschema;
In Spring Boot 2 the wanted schema can be set in application.properties file with the following property:
spring.datasource.hikari.connection-init-sql=ALTER SESSION SET CURRENT_SCHEMA = MY_SCHEMA
HikariCP is the default connection pool in Spring Boot 2. To see all HikariCP settings (including "connectionInitSql") in you log file add also the following in application.properties:
logging.level.com.zaxxer.hikari=DEBUG

Quartz Scheduler create schedulerFactoryBean Beans without quartz.properties

I have running quartz scheduler with inside my spring app.
right now, i'm using quartz.properties to contain any properties value and use it to create schedulerFactoryBean Bean and it works fine.
this is my QuartzConfiguration..
#Configuration
public class QuartzConfiguration {
public static final String CONTEXT_KEY = "applicationContext";
//#Autowired
//private DataSource dataSource;
#Bean
public SchedulerFactoryBean schedulerFactoryBean() {
SchedulerFactoryBean scheduler = new SchedulerFactoryBean();
scheduler.setApplicationContextSchedulerContextKey(CONTEXT_KEY);
scheduler.setConfigLocation(new ClassPathResource("config/quartz.properties"));
//scheduler.setDataSource(dataSource);
//scheduler.setAutoStartup(true);
scheduler.setWaitForJobsToCompleteOnShutdown(true);
return scheduler;
}
}
My quartz.properties :
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
org.quartz.jobStore.useProperties=false
org.quartz.jobStore.dataSource=myDS
org.quartz.dataSource.myDS.driver =oracle.jdbc.OracleDriver
org.quartz.dataSource.myDS.URL = jdbc:oracle:thin:#example:1521:db
org.quartz.dataSource.myDS.user = user
org.quartz.dataSource.myDS.password = password
org.quartz.dataSource.myDS.maxConnections = 5
org.quartz.dataSource.myDS.validationQuery = select 1 from dual
org.quartz.jobStore.isClustered=false
org.quartz.jobStore.tablePrefix = DPPA.QUARTZ_
org.quartz.threadPool.threadCount=1
org.quartz.scheduler.skipUpdateCheck=true
org.quartz.plugin.jobHistory.class=id.co.fifgroup.dpa.batch.BatchHistoryListener
i want to create schedulerFactoryBean without any quartz.properties, because my client dont want to change any database connection inside the war archieve.
is it possible to create schedulerFactoryBean without any quartz.properties ?
You can configure it without properties file in this way;
Properties p = new Properties();
p.put("org.quartz.scheduler.instanceName", "Scheduler_test");
p.put("org.quartz.threadPool.threadCount", 2);
...
StdSchedulerFactory factory = new StdSchedulerFactory(p);

Properties file on HikariCP

I tried unsuccessfully configure hikaricp and I don't see error in the code please help.
public class DatabaseManager {
private DatabaseClient[] databaseClients;
private HikariDataSource hikariDataSource;
public DatabaseManager(String absoluteFilePath) {
final HikariConfig hikariConfig = new HikariConfig(absoluteFilePath);
this.hikariDataSource = new HikariDataSource(hikariConfig);
System.out.println(hikariConfig.getUsername()); // null u-u
}
}
Properties file:
## Database Settings
dataSourceClassName=org.mariadb.jdbc.MySQLDataSource
dataSource.user=root
dataSource.password=
dataSource.databaseName=imagine-db
dataSource.portNumber=3306
dataSource.serverName=localhost
You've set the username on the data source not on the config itself. This will still work fine but you can't access it using hikariConfig.getUsername().
Try adding this to your properties file if you really need to access the user like that, although I suspect you don't.
username=root
password=

How to programmatically use Spring's JdbcTemplate?

We use Spring's JdbcTemplate which is configured through Spring config as illustrated below. Is there a way to do this without injecting the data source? I'd like to just create the JdbcTemplate instance programmatically and "initalize" the datasource using TheOracleDS.
Our current config:
Java class
private JdbcTemplate jdbcTemplate;
#Resource(name = "myDataSource")
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
Spring config
<jee:jndi-lookup id="myDataSource" jndi-name="java:/TheOracleDS"/>
Oracle datasource config
<xa-datasource>
<jndi-name>TheOracleDS</jndi-name>
...
</xa-datasource>
Update: Reason I'm asking this is I'm not a total believer in dependency injection / having Spring manage beans..
Here's some sample code from a project I've written:
SimpleJdbcTemplate db;
DataSource dataSource = new SingleConnectionDataSource(System.getProperty(
"lingcog.db.connectstring"),
System.getProperty("lingcog.db.username"),
System.getProperty("lingcog.db.password"), false);
db = new SimpleJdbcTemplate(dataSource);
Maybe my code would be simpler if I used injection, but this is a good example of how to do this without using injection.
You can use an org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup object to find the data source you want by JDNI name.
DataSource dataSource = new JndiDataSourceLookup().getDataSource("java:/TheOracleDS")
SimpleJdbcTemplate db=new SimpleJdbcTemplate(dataSource);
Not sure why you want to do that but... you could lookup the JDNI datasource with Spring's JndiDataSourceLookup:
JndiDataSourceLookup lookup = new JndiDataSourceLookup();
lookup.setResourceRef(true); // if the lookup occurs in a J2EE container
DataSource ds = lookup.getDataSource(jndiName);
Or just perform a "manual" lookup using Sun's classes:
Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("jdbc/AcmeDB");
Then, just pass the datasource reference to the JdbcTemplate constructor or call setDataSource(ds).
But, as I said, I have no idea why you don't want to use injection.
Just use a raw JNDI lookup:
public void setDataSourceName(String name) {
InitialContext ctx = new InitialContext();
jdbcTemplate = new JdbcTemplate((DataSource) ctx.lookup(name));
}

Categories

Resources