Spring Data JPA with ssh tunnel to a remote MySQL server - java

I'm using Spring Data JPA with Hibernate as persistence provider in conjunction with a remote MySQL5 Server for a job that periodically replicates a subset of internal data. The job (i.e. a quartz-scheduled java application) runs once per dai and needs approx. 30seconds to complete the synchronization). For safety reasons we don't want to open the remote server for direct connections from outside (i.e. other than localhost).
I've seen examples with Jsch to programmatically set up an ssh tunnel, but could not finde any resources on how to integrate Jsch with spring data. One problem I'm seeing is that certain of my spring beans (i.e. org.apache.commons.configuration.DatabaseConfiguration) are created at application startup and already needs access to the datasource.
I could open the ssh tunnel outside of the application, but then it would be opened all the time, but I wanted to avoid that as I only need it opened 30seconds per day.
EDIT:
After some research I found several ways to get a ssh tunnel
A) Implementing my own DataSource (I extended org.springframework.jdbc.datasource.DriverManagerDataSource) and then used PostContruct and Predestroy to setup / close the ssh tunnel with Jsch
--> Problem: The ssh tunnel remains open for the lifetime of the application, what is not what I want
B) Implementing my own Driver (I extended com.mysql.jdbc.Driver) and overwrite "connect" to create the ssh tunnel before the connection
--> Problem: I'm not able to close the ssh tunnel connection
Any more suggestions are welcome

If you have a DataSource bean in your Spring configuration, you can create your own DataSource implementation that opens an SSH tunnel before attempting to make a connection using the provided JDBC URL. As an example, consider the following configuration that uses a HikariDataSource:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource">
<bean class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">...</bean>
</property>
</bean>
You can extend the class HikariDataSource to provide your own implementation. An example below:
class TunneledHikariDataSource extends HikariDataSource implements InitializingBean {
private boolean createTunnel = true;
private int tunnelPort = 3306;
public void afterPropertiesSet() {
if(createTunnel) {
// 1. Extract remote host name from the JDBC URL.
// 2. Extract/infer remote tunnel port (e.g. 3306)
// from the JDBC URL.
// 3. Create a tunnel using Jsch and sample code
// at http://www.jcraft.com/jsch/examples/PortForwardingL.java.html
...
}
}
}
Then, instantiate a bean instance for the custom class instead of HikariDataSource.

Expanding on #manish's answer here is my solution that works with Spring Boot in 2022:
#Configuration
class DataSourceInitializer {
#Bean
fun dataSource(properties: DataSourceProperties): DataSource {
return properties.initializeDataSourceBuilder().type(SshTunnelingHikariDataSource::class.java).build()
}
}
class SshTunnelingHikariDataSource : HikariDataSource(), InitializingBean {
override fun afterPropertiesSet() {
val jsch = JSch()
val filePath = javaClass.classLoader.getResource("id_rsa")?.toURI()?.path
jsch.addIdentity(filePath, "optional_key_file_passphrase")
val session = jsch.getSession("root", "remote-host.com")
val config = Properties()
config["StrictHostKeyChecking"] = "no";
session.setConfig(config)
session.connect()
session.setPortForwardingL(3307, "db-host.com", 3306)
}
}
Depending on your connection you may not need the identity file (i. e. a private key) but instead you may need username + password which you can provide at getSession.
remote-host.com is the host you want to have your SSH session to terminate in, having access to the database
db-host.com is the host that your remote-host can resolve. It may as well be localhost if the database is running locally on your remote-host

Related

Not able to create a connection to Janus to define the schema

Not able to create a connection to Janus to define the schema.
JanusGraph graph = JanusGraphFactory.build().set("storage.backend", "cql")//.set("storage.cql.keyspace", "janusgraph")
.set("storage.hostname", "url").open();
Error:
java.lang.IllegalArgumentException: Could not find implementation class: org.janusgraph.diskstorage.cql.CQLStoreManager
at org.janusgraph.util.system.ConfigurationUtil.instantiate(ConfigurationUtil.java:75)
at org.janusgraph.diskstorage.Backend.getImplementationClass(Backend.java:530)
at org.janusgraph.diskstorage.Backend.getStorageManager(Backend.java:494)
Able to do normal tinkerpop gremlin query with the following config
#Bean
public Cluster cluster() {
return Cluster.build()
.addContactPoint(dbUrl)
.port(dbPort)
.serializer(new GraphBinaryMessageSerializerV1())
.maxConnectionPoolSize(5)
.maxInProcessPerConnection(1)
.maxSimultaneousUsagePerConnection(10)
.create();
}
#Bean
public GraphTraversalSource g(Cluster cluster) throws Exception {
//return traversal().withRemote(DriverRemoteConnection.using(cluster));
return traversal().withRemote("conf/remote-graph.properties");
}
Want to define the schema during the application start up, Trying to use openManagement
When writing a java application using janusgraph, you can choose between embedding janusgraph in your application or connecting to a janusgraph server. Your code suggests you are attempting the embedded option, so you can start from the example in the provided link.

Spring-jms. How to customize broker url for rabbitmq?

All examples I read related with activeMq and spring-boot has especial property to change the url of broker:
spring.activemq.broker-url=<SOME_URL>
By default it uses default settings: default url and default port.
But I use rabbirMq and I want to know how to change broker url
I've read this one
I've added application.properties to the src/main/resources
with following content(host absolutely wrong, I expected to see error):
spring.rabbitmq.host=olololo
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
But it doesn't affect application.
Looks like spring(boot) doesn't read these prioerties.
P.S.
Project structure looks like this:
Spring Boot does not have auto configuration support for rabbitmq-jms (the link you referenced is the native RabbitMQ AMQP auto configuration).
For the JMS connection factory, you will have to do the configuration yourself...
#Bean
public RMQConnectionFactory connectionFactory(#Value("${spring.rabbitmq.host}") String host,
#Value("${spring.rabbitmq.port}") int port) {
RMQConnectionFactory cf = new RMQConnectionFactory();
cf.setHost(host);
cf.setPort(port);
return cf;
}

Dynamically select catalog for Tomcat mysql connection pool in a Spring application

I need to create a connection pool from a spring application running in a tomcat server.
This application has many catalogs, the main catalog (its is static) called 'db' has just one table with all existing catalog names and a boolean flag for the "active" one.
When the application starts I need to choose from the main catalogs the active one, then I have to select it as default catalog.
How can I accomplish this?
Until now I used a custom class DataSourceSelector extends DriverManagerDataSource but now I need to improve the db connection using a pool, then I thought about a tomcat dbcp pool.
I would suggest the following steps:
Extend BasicDataSourceFactory to produce customized BasicDataSources.
Those customized BasicDataSources would already know which catalog is active and have the defaultCatalog property set accordingly.
Use your extended BasicDataSourceFactory in the Tomcat configuration.
#Configuration
public class DataAccessConfiguration {
#Bean(destroyMethod = "close")
public javax.sql.DataSource dataSource() {
org.apache.tomcat.jdbc.pool.DataSource ds = new org.apache.tomcat.jdbc.pool.DataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost/db");
ds.setUsername("javauser");
ds.setPassword("");
ds.setInitialSize(5);
ds.setMaxActive(10);
ds.setMaxIdle(5);
ds.setMinIdle(2);
ds.get
return ds;
}
}

How to read pool name from a Connection?

I'm using a Connection via severals pools :
DataSource ds = initialContext.lookup("poolname1");
Connection cn = ds.getConnection();
submethod1(cn);
submethod2(cn);
void submethod1(Connection cn)
{
// using connection
// ..
}
My question is: how to log "poolname" in submethods ? or similar informations about DataSource.
Perhaps this will help
getClientInfo()
or
getMetaData()
as mentionned in the Official Java Doc
The poolname in your example is actually a JNDI Name. This is generally a configuration which is configured in resource definition (e.q under in tomcat config).
For your problem as #sourlcheck mentioned, its not possible as connection are not aware of their datasource.
Once way to solve your problem is to give label to the datasource. Most of the Pooled datasource implementations (e.q. C3P0) offer a setter for setting up a name to the datasource. In C3P0 the datasournce class is ComboPooledDataSource and the method is getDataSourceName(). Once you get this name, it remains the same throughout its life cycle. But off course you need to introduce third party library
You can't log "poolname" in methods that receive Connection. Connections don't know if they are pooled or served directly from non-pooling DataSource.
Also you can create and access your pool in many different ways, not all of them guaranteeing existence of "poolname".

Jboss AP6 Transaction Manager Implementation

I'm just starting to learn Jboss AP6 and I have a few questions:
I created Local Tx Datasource (MySql Database)and can access it in my code using JNDI.
Now I would like to create kind of Transaction Management resource inside of my Jboss AP.
1) Is there any JTA feature built in Jboss AP6?
2) Can I apply it to my local DataSource which I created?
3) Can you please point me to any documentation which explains how to configure it and use it in my code, ot is there any article which coversthese topics in depth?
I googled it for some period of time, but haven't found any useful documentation. I don't want to use Spring/Hibernate out of the box solution just Mysql and plain JTA.
JBoss AP6 support JTA 1.1
Yes you can
If you declareLocalTxDatasource, this is mean, than whenever you get
connection from this datasource this connection will participate in "current" transaction.
If you want to manupulate transaction yourself, without EJB for example, you must enject TransactionManager from JNDI.
Example
TransactionManager tm = (TransactionManager)context.lookup("java:/TransactionManager");
tm.begin();
try{
DataSource ds = context.lookup("java:/testDS");
connection = ds.getConnection()
//do useful work
connection.close();
tm.commit();
}catch(Exception e){
tm.rollback()
}

Categories