Dealing with DB instance IAM authentication token expiry - java

As per here after you generate an authentication token, it's valid for 15 minutes before it expires.
Consequently, if you don't hold onto the connection it will expire after 15 mins.
Upon application startup, we have code a bit like this:
Jdbi.create(dbProperties().getDbUrl(), jdbiProperties());
part of the jdbiProperties() method calls:
generator.getAuthToken(GetIamAuthTokenRequest.builder()
.hostname(url).port(PORT).userName(username)
.build());
We have a repo method that uses the jdbi (wrappper of a JDBC DataSource) like so:
jdbi.withHandle(handle ->
handle.createQuery("select name from contacts")
.mapTo(String.class)
.list());
The problem here is that since we generated the token upon startup it stop working after 15 min.
Is the some standard pattern for dealing with this? Seems like we could refresh the token upon every call (seems inefficient). Or we could request a new token upon receipt of expiry (through an exception).
Any suggestions appreciated.
TIA

Please see https://github.com/team-carepay/carepool-aws-jdbc
This library supports different ways of providing IAM authentication for RDS:
Wrapper for Tomcat Datasource
Wrapper for Hikari Datasource
Plugin for MariaDB driver
Plugin for MySQL driver
Depending on your project, you can pick any of the above options. For Jdbi, you can use Jdbc.create with takes a DataSource, e.g. Hikari or MySQL.
Please note that some versions of MariaDB driver support native AWS IAM (assuming that you have the AWS Java SDK in your classpath). Since that support was removed, you can now use https://github.com/awslabs/aws-mysql-jdbc which also supports IAM authentication. The MariaDB and AWS-MySQL drivers need a few libraries on the classpath (jackson, commons-collections, etc), so make sure you include these. The carepool-aws-jdbc plugin does not require any 3rd party libraries.

Related

Class.forName("org.sqlite.JDBC") vs JDBC.PREFIX to load JDBC class

We had recently moved to building projects using Maven. Earlier, all the JARs were added in libs folder and added to classpath. While moving to the Maven build, I missed to add a dependency to the 'sqlite-jdbc'. This library is required to read data from a local .db3 file.
As the 'org.sqlite.JBDC' class was loaded by calling the code Class.forName("org.sqlite.JDBC"), there was no compilation error and I deployed the WAR file and servlet failed in the server. I was thinking of a way to find the issue at the compilation time itself to avoid any such mistakes in the future. Can I simply call the JDBC.PREFIX to load the JDBC, so that, If I forget to add the dependency to the pom.xml file, I can find the issue at the compile time, itself?
Is there was difference between Class.forName("org.sqlite.JDBC") vs JDBC.PREFIX to load JDBC class?
No need for Class.forName
There is no generally no need to call Class.forName.
Such calls were used in the early years. Modern Java was changed so that JDBC drivers are automatically loaded and registered with the JVM via the Java Service Provider Interface (SPI) facility.
If you are using books or tutorials advising Class.forName, you may want to obtain more up-to-date learning materials.
DataSource
Furthermore, in Servlet work you generally should not be explicitly accessing the JDBC driver.
Setting the database server address, username, and password would require hard-coding text. When the deployment sysadmins change the IP address, or rotate passwords, your code breaks. You would then have to modify your source code, re-compile, and re-deploy.
Instead, you should externalize such configuration details.
For SQLite, see Using DataSource to connect to SQLite with (Xerial) sqlite-jdbc driver.
JNDI
You can externalize database configuration by using the the DataSource interface. After obtaining a DataSource object at runtime, make database connections by calling its getConnection method. That DataSource object holds the database server address, username, password, and all other settings needed to make a connection to the database.
Obtain a DataSource object at runtime by using JNDI. Your Servlet container may act as the naming/directory server to provide the DataSource object, if your sysadmin so configures it. Or the DataSource can be obtained via JNDI from an external server such as an LDAP server.
Again, the beauty of using DataSource and JNDI is that you as the Servlet programmer need not be involved, or even informed, when the deployment details change.
JDBC driver location
For Servlet work, you generally do not bundle the JDBC driver with your app.
Instead, the JDBC driver goes into a folder managed by your Servlet container. Study the documentation for your particular Servlet container. For Apache Tomcat, see this Answer.
In development, your IDE may need access to the JDBC driver to compile. If so, in your Maven POM, mark the dependency with a <scope>provided</scope> element. This tag tells Maven to omit that dependency from the final build because the dependency will already be present (provided) at runtime.
If you insist on bundling your JDBC driver within the WAR file of your web app, then see the important Comment by Mark Rotteveel.
This topic has been addressed many times on Stack Overflow. Search to learn more.

(GAE-Standard+Java11) Sessions with multiple instances running

I have deployed my spring boot application on GAE, Java 11, Standard Environment. As per the documentation for Java11 we need to use app.yaml for configuring the instances.
I wanted to know as to how I can enable sharing of sessions between instances. As per my research, Earlier we could simply solve this problem by setting sessions-enabled and async-session-persistence in appengine-web.xml. With appengine-web.xml gone, what is the equivalent way of doing this in app.yaml.
Use case that i am trying to achieve is :
Using spring security (Unfortunately i get logged out when according to me the request of the same user goes to another instance.)
Storing the user retrieved from DB in a #SessionScoped variable so as to avoid multiple DB calls.
Any help here would be really appreciated. Thanks!
I went through a lot of documentation, but I believe that this is not inside the app.yaml configuration reference.
Alternatively, I could find that you could use session affinity in order to use a instance to reply always the requests of a same user, this can be enabled in your app you can use the next tag in your app.yaml according to this documentation.
network:
session_affinity: true
Hope this works for you.

Multi-Keyspace with Cassandra's Java Driver on fly

Currently, We use Cassandra's Java driver to configure session based on the attributes defined in properties file which looks like this:
Cluster cluster = Cluster.builder()
.addContactPoints(contactPoints)
.withPort(environment.getRequiredProperty("cassandra.port", Integer.class))
.withCredentials(username, password)
.build();
Session session = cluster.connect(environment.getRequiredProperty("cassandra.keyspace"));
What we want to achieve now is to use multi-keyspace on fly, if there is a way to do it, being able to detect the cassandra connection for a given request without changing much on the existing logic except the configuration. Any suggestion or help on this would be a great way to start
The best way to achieve this is to specify table as keyspace.table in all queries, and don't rely on the keyspace set in the session. You can of course have multiple session objects - one per keyspace, but it's not recommended, as every session is quite heavyweight, and open at least 2 connections to every node in the cluster.
Recent versions of the Java driver support setting the keyspace via setKeyspace function of the SimpleStatement class, but it requires support on the Cassandra side as well (don't remember which version although, at least DSE 6.0 supports this).
(Update): The setting keyspace on query level isn't merged into OSS Java driver, and available only in DSE Java driver.

How to configure connection timeout in hibernate?

I'm using Hibernate v4.1.4.final.jar using Java 1.7 to connect to Oracle 10g server. And this is a standalone java program.
Unfortunately, my query takes more than 30 minutes to run. I want to know where I can configure connection time out & read time out so that while running this standalone program, it will not time out and throw error?
Thanks!
There are a few options that you can try:
If the jdbc driver that you use support timeout function and can be configured through property, then you can pass on the property using: hibernate.connection.<propertyname>
Use external connection provider such as c3p0 or DBCP, and control the timeout as those external provider support.
Configure your hibernate to use DataSource instead of plain Connection and control timeout through that.
The closest property that I can find for Oracle driver is oracle.jdbc.ReadTimeout property. So in your hibernate configuration, the whole name will be hibernate.connection.oracle.jdbc.ReadTimeout..hope this works for you.
By default it won't throw any kind of error, FWIW...if you run a query, it'll just work, AFAIK.
In terms of connect timeout, you may be able to specify in your
hibernate.connection.url a particular option for your DB, ex: Postgres I'd add &connectTimeout=0

Datasource creation failed using PCF User Provided Service

I have a SpringBoot application and deploying it in PCF where app is trying to connect Oracle 12c Database using PCF User Provided Services but it failing with this error Failed to determine a suitable driver class
build.gradle code:
and here are the configuration that I used in CUP service:
Service binding is happening properly. I can see the same details under VCAP_SERVICES in Environment Variables.
Error:
Short Answer: I think you want the uri to be oracle://... Strip off the jdbc: part. The Spring Autoreconfiguration code that gets injected by the Java buildpack will look at the prefix on the URI, so it needs oracle:// to know it's an Oracle link.
Long Answer: You probably don't want to depend on the injected Spring Autoreconfiguration. When it just works, it's great, but it can be difficult to understand what it's doing when it doesn't work.
It is better to use Spring Cloud Connector or even better, as all signs point to this replacing Spring Cloud Connector, use java-cfenv. For details on java-cfenv, see this blog post.
Spring Cloud Connector has the same issue I mentioned above as the Spring Autoreconfiguration, except that it will pretty clearly tell you when it doesn't recognize a bound service. Anyway, if you decide to use SCC, make sure you prefix the URI with oracle://.
If you use java-cfenv, it's more flexible so it's really up to you what properties and values you inject through the service.
Hope that helps!

Categories