How Jmx bean of Hikari cp can be used in spring boot when boot autoconfigure pooling - spring-boot

How Jmx bean of Hikari cp can be used in spring boot when boot autoconfigure pooling?
i tried following this instruction in below link.
https://github.com/brettwooldridge/HikariCP/wiki/MBean-(JMX)-Monitoring-and-Management.
Hre is my mbean class
import javax.management.JMX;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
#ManagedResource(
objectName="PD:category=MBeans,name=testBean",
description="Managed Bean")
#Component("testMbean")
public class HikariJmx {
private String message = "Simple Message";
private int size=0;
public HikariJmx(){
System.out.println("......TestMbean........");
}
#ManagedOperation
public void resetMessageViaMBean(){
this.message = "Message RESET";
}
#ManagedAttribute
public int getSize() throws Exception{
try {
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName poolName = new ObjectName("com.zaxxer.hikari:type=Pool (HikariPool-1)");
HikariPoolMXBean poolProxy = JMX.newMXBeanProxy(mBeanServer, poolName, HikariPoolMXBean.class);
return poolProxy.getIdleConnections();
}catch(Exception e){e.printStackTrace();}
return 0;
}
}
Iam able to see the bean in visual vm. But when i take the attribute SIZE i am getting instanceNotFound exception in below line.
ObjectName poolName = new ObjectName("com.zaxxer.hikari:type=Pool (HikariPool-1)");
I am not creating datasource manually, just gave the properties in the application.properties and boot do the rest.

I had the same problem.
You can get the HikariPoolMXBean from the HikariDataSource itself.
In order to do that, I injected the HikariDataSource to the class I needed the HikariPoolMXBean, and called this method :
HikariPoolMXBean poolProxy = hikariDataSource.getHikariPoolMXBean();

Related

Implementing JNDI with Springboot and JPA

I try to configure JNDI in Tomcat. I tried it using 2 springboot applications.
The first application is using springboot starter jdbc
The second is using springboot starter jpa
On both applications controllers are the same:
#RequestMapping(
value = "/employees",
method = RequestMethod.GET)
public ResponseEntity<Object> getEmployees() {
System.out.println("inside getEmployees() method ....");
List<Employee> employeeList = repository.findAll();
return new ResponseEntity<>(employeeList, HttpStatus.OK);
}
I also configured jndi in my application properties:
spring.datasource.jndi-name=java:comp/env/jdbc/TestDB
In my repository class in the application that uses springboot starter jdbc:
#Repository
public class EmployeeRepository {
#Autowired
private JdbcTemplate jdbcTemplate;
#Transactional(readOnly = true)
public List<Employee> findAll() {
return jdbcTemplate.query("select * from EMPLOYEE", new EmployeeRowMapper());
}
}
On the other I use JpaRepository:
#Repository
public interface EmployeeRepository extends JpaRepository<EmployeeEntity, Long> {
#Override
#Transactional(readOnly = true)
List<EmployeeEntity> findAll();
}
So far so good. server.xml and context.xml are correctly configured in tomcat. For this test I am using MySQL v.5...
The application containing the springboot starter jdbc, i do access the database data using the get method. In the other application, the war is deployed but not started.
I have actually 3 issues:
I try using JNDI to handle a connection pool
Most of my developments are using JPA (i have many webservices for my application ...)
the last issue should be a new question: is there a way to use the Tomcat connexion pool to the database from an external application ? For example having a jar using the same connexion pool. For this point i didn't find any sample
I solved this issue doing the following:
I created a configuration class
#Configuration
#EnableJpaRepositories(basePackages = "fr.company.jndi.data.repository")
public class Config {
#Bean
public DataSource dataSource() {
DataSource dataSource = null;
Context ctx = null;
try {
ctx = new InitialContext();
dataSource = (DataSource) ctx.lookup("java:comp/env/jdbc/TestDB");
} catch (NamingException e) {
e.printStackTrace();
}
return dataSource;
}
}
And i also downgraded my spring boot version from version 3.0.0 to version 2.7.4-SNAPSHOT for example. I also added the following dependency:
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>

How to use TestContainers + Spring Boot + oracle-xe

I try to use Test Containers with Oracle-XE module and Spring Boot and so far, when I launch my test, I am confronted to exception :
Caused by: java.lang.IllegalArgumentException: JDBC URL matches jdbc:tc: prefix but the database or tag name could not be identified
In my src/test/application.properties, I declared the url datatasource as :
spring.datasource.url=jdbc:tc:oracle-xe://somehostname:someport/databasename?TC_INITSCRIPT=schema-test.sql
To indicate the docker image to pull for oracle-xe, I created the file testcontainers.properties in src/test/resources :
oracle.container.image=oracleinanutshell/oracle-xe-11g:1.0.0
Do you have any idea how to make this work ?
It works flawlessly with MySQL, with the datasource url :
spring.datasource.url=jdbc:tc:mysql:5.6.23://somehostname:someport/databasename?TC_INITSCRIPT=schema-test.sql
You can make a test configuration class that redefine datasource bean with oracle xe container configuration.
public class OracleIT {
#ClassRule
public static OracleContainer oracleContainer = new OracleContainer();
#BeforeAll
public static void startup() {
oracleContainer.start();
}
#TestConfiguration
static class OracleTestConfiguration {
#Bean
DataSource dataSource() {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setJdbcUrl(oracleContainer.getJdbcUrl());
hikariConfig.setUsername(oracleContainer.getUsername());
hikariConfig.setPassword(oracleContainer.getPassword());
return new HikariDataSource(hikariConfig);
}
}
}

"No bean named 'cassandraTemplate' available"

I have not added cassandra template bean in my beandefinition class, but it works fine and gives me the required output while running, but while writing junit test class
it is throwing me a error "No bean named 'cassandraTemplate' available".
Why is this issue raising during running my junit test class.
This is my code:
#Configuration
#PropertySource("cassandra.properties")
#EnableCassandraRepositories(basePackages = "...repository")
public class Beandef
{
#Autowired
public Environment environment;
CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean();
#Bean(name = "clusterFactory")
public CassandraClusterFactoryBean getCluster() {
PoolingOptions poolingOptions = new PoolingOptions();
cluster.setContactPoints(environment.getProperty("cassandra.contactpoints"));
cluster.setPoolingOptions(poolingOptions);
cluster.setPort(Integer.parseInt(environment.getProperty("cassandra.port")));
poolingOptions.setNewConnectionThreshold(HostDistance.LOCAL, 50);
return cluster;
}
#Bean
#DependsOn("clusterFactory")
public CassandraSessionFactoryBean getSession() throws Exception {
CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
session.setCluster(cluster.getObject());
session.setKeyspaceName(environment.getProperty("cassandra.keyspace"));
session.setConverter(new MappingCassandraConverter(new CassandraMappingContextAware()));
session.setSchemaAction(SchemaAction.NONE);
return session;
}
If you're using Spring Boot, and if your test class doesn't have a #SpringBootApplication available in the classpath, you have to add #EnableAutoConfiguration to enable Spring Boot auto-config (even with spring-boot-starter-data-cassandra in your dependencies).

How to inject property values into Spring Boot component

In my Spring Boot Application, I implemented the following class with a method to call a stored procedure.
#Component
#ConfigurationProperties(prefix = "spring")
public class FmTrfUtil {
static int returnVal;
#Value("${spring.datasource.url}")
static String url;
public static int insertFmTrfs(List<String> trfs, String source) {
System.out.println(url);
EntityManager em = Persistence.createEntityManagerFactory("RIStore_FM").createEntityManager();
Session session = em.unwrap( Session.class );
final String[] trfArray = trfs.toArray(new String[trfs.size()]);
final String src = source;
session.doWork( new Work(){
public void execute(Connection conn) throws SQLException {
CallableStatement stmt = null;
OracleConnection oraCon = conn.unwrap(OracleConnection.class);
Array array = oraCon.createARRAY("VARCHAR2_TAB_T", trfArray);
stmt = conn.prepareCall("{? = call FM_TRF_UTIL.process_fm_trf(?,?)}");
stmt.registerOutParameter(1, Types.INTEGER);
stmt.setArray(2, array);
stmt.setString(3, src);
stmt.execute();
returnVal = stmt.getInt(1);
}
});
return returnVal;
}
}
Since calling stored procedure requires database connection, I need to load the these corresponding property values from application.properties:
spring.profiles.active=dev
spring.datasource.url=jdbc:oracle:thin:#ldap://xxx:389/risdev3, cn=OracleContext,dc=x,dc=net
spring.datasource.username=owner
spring.datasource.password=owner987
Based on the following articles about similar issue, Spring boot - custom variables in Application.properties and Using Spring-Boot configuration properties in your own classes and Spring Boot #ConfigurationProperties example, I added this annotation for my class #ConfigurationProperties(prefix = "spring") (properties for db connection all have "spring" as prefix). However when I run it with a test class as follows, I got error "the application must supply JDBC connection" which means properties in application.properties are not picked up.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = RistoreWebApplication.class, initializers = ConfigFileApplicationContextInitializer.class)
public class FmTrfUtilTest {
#Test
public void test() {
List<String> trfs = new ArrayList<String>();
trfs.add("TRF000001");
trfs.add("TRF000002");
int ret = FmTrfUtil.insertFmTrfs(trfs, "SARC");
assertTrue(ret > 0);
}
}
In order for #ConfigurationProperties to work, I added maven dependency spring-boot-configuration-processor too. Why is it still not working? What did I miss?
There are few things wrong here:
#Value does not work on static fields
#ConfigurationProperties are used to bind fields from application.properties or application.yml to Java object. Look at any #ConfigurationProperties annotated class from Spring Boot itself to easily understand how it should be used.
you should not use your own #ConfigurationProperties with prefix spring since it's already used by Spring Boot itself
spring-boot-configuration-processor is used only for nicer code completion in your IDE. You do not need this.
If you want to utilize Spring Boot configuration properties for database connection, instead of creating EntityManager like you do:
EntityManager em = Persistence.createEntityManagerFactory("RIStore_FM").createEntityManager();
You should just inject it assuming you have Spring Data JPA Starter in your dependency list.
I see you use lots of static methods and fields. That's not going to work with Spring. Use dependency injection instead and autowire what you need.

Custom spring property source does not resolve placeholders in #Value

I'm trying to build a Spring 3.1 PropertySource which reads its values from Zookeeper nodes. For connecting to Zookeeper I am using Curator from Netflix.
For that I've built a custom property source which reads the value of a property from Zookeeper and returns it. This works fine when I am resolving the property like this
ZookeeperPropertySource zkPropertySource = new ZookeeperPropertySource(zkClient);
ctx.getEnvironment().getPropertySources().addLast(zkPropertySource);
ctx.getEnvironment().getProperty("foo"); // returns 'from zookeeper'
However, when I try to instantiate a bean which has a field with an #Value annotation then this fails:
#Component
public class MyBean {
#Value("${foo}") public String foo;
}
MyBean b = ctx.getBean(MyBean.class); // fails with BeanCreationException
This problem has most likely nothing to do with Zookeeper but with the way I'm registering the property sources and creating the beans.
Any insight is highly appreciated.
Update 1:
I'm creating the app context from an XML file like this:
public class Main {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ctx.registerShutdownHook();
}
}
The class which connects to Zookeeper is a #Component.
#Component
public class Server {
CuratorFramework zkClient;
public void connectToZookeeper() {
zkClient = ... (curator magic) ...
}
public void registerPropertySource() {
ZookeeperPropertySource zkPropertySource = new ZookeeperPropertySource(zkClient);
ctx.getEnvironment().getPropertySources().addLast(zkPropertySource);
ctx.getEnvironment().getProperty("foo"); // returns 'from zookeeper'
}
#PostConstruct
public void start() {
connectToZookeeper();
registerPropertySource();
MyBean b = ctx.getBean(MyBean.class);
}
}
Update 2
This seems to work when I'm using XML-less configuration, i.e. #Configuration, #ComponentScan and #PropertySource in combination with an AnnotationConfigApplicationContext. Why doesn't it work with a ClassPathXmlApplicationContext?
#Configuration
#ComponentScan("com.goleft")
#PropertySource({"classpath:config.properties","classpath:version.properties"})
public class AppConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Answering to your Update 2: This does not work with your original configuration(registering a PropertySource using #PostConstruct) because the PropertySource is being registered very late, by this time your target bean has already been constructed and initialized.
Typically the injection of the placeholders happens via a BeanFactoryPostProcessor which is very early in the Spring lifecycle(beans have not been created at this stage) and if a PropertySource is registered at that stage, then placeholders should be resolved.
The best approach though is to use a ApplicationContextInitializer, get a handle on the applicationContext and to register the propertySource there:
public class CustomInitializer implements ApplicationContextInitializer<ConfigurableWebApplicationContext> {
public void initialize(ConfigurableWebApplicationContext ctx) {
ZookeeperPropertySource zkPropertySource = new ZookeeperPropertySource(zkClient);
ctx.getEnvironment().getPropertySources().addFirst(zkPropertySource);
}
}

Resources