How to get username from connection pool declaration at runtime - java

My connection pool is declared in context.xml of tomcat server as follows :
<Resource name="jdbc/codesign" auth="Container"
type="javax.sql.DataSource" driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:#abc.fjd.com"
username="tiger" password="tiger123" maxActive="30" maxIdle="10"
poolPreparedStatements="true" maxOpenPreparedStatements="100"
validationQuery="SELECT SYSDATE FROM DUAL" maxWait="10000"/>
I am initializing datasource in usual way as :
Context initContext = new InitialContext();
Context envContext = (Context) initContext.lookup("java:/comp/env");
ds = (DataSource) envContext.lookup("jdbc/codesign");
What I want is to access the username ("tiger" in this case) at runtime. I am not seeing any such method in javax.sql.DataSource class. And when I try to get schema from connection using
Connection conn = DataSourceConnectionPool.getConnection()
conn.getSchema();
I get following error :
java.lang.AbstractMethodError: org.apache.tomcat.dbcp.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.getSchema()
And I get the same error if unwrap the connection first :
conn.unwrap(OracleConnection.class).getSchema()
java.lang.AbstractMethodError: oracle.jdbc.driver.OracleConnection.getSchema()
Is ther a way to get schema name or username from datasource or Connection?

If you are using Tomcat, your DataSoruce implementation is most likely BasicDataSource https://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/tomcat/dbcp/dbcp2/BasicDataSource.html
You can cast your ds to BasicDataSource, and access the user by getUsername().
But of course try first to print the db.getClass().getName() to see what is the actual implementation.

Related

JNDI Lookup with Resource annotation always NULL

I've got a WebApp with Tomcat 10, Java11 using Jersey3.
I defined a ConnectionPool in my context.xml for handling the connection to my OracleDB and now I'm trying to access the DataSource within my controller through a #Resource annotation. This should invoke a JNDI-lookup. Unfortunately, I always get a NPE as it seems not to find the resource while running... What am I doing wrong? Or what would the correct mappedName / lookup be?
#Path("/data")
public class DataController {
#Context
ServletContext context;
#Resource(lookup = "java:/jdbc/myDB") //also tried java:/comp/env/jdbc/myDB and mappedName="jdbc/myDB"
protected DataSource ds; //always null
<Context name="myapp">
<Resource type="javax.sql.DataSource"
name="jdbc/myDB"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:#//localhost:1521/orcl"
username="xy"
password="xy"/>
According to the tutorials, a ref-link is optional when I define the resource directly in the context.xml.
Thanks for any input!
This link is about jboss, but would seem relevant to you too. It says that according to the specification, Resource annotation to do JNDI lookups would only work with EJBs, so it wouldn't work for your case.
A workaround is to do the programmatic way to see if your datasource is working:
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
DataSource bean = (DataSource ) envCtx.lookup("jdbc/myDB");
If you can find your datasource you can then try to optimize to avoid "manual" lookup above.

How to switch between two databses, based on user request in Java web application?

I am using Spring restful web services in my project, here I have two categories of users
1) secondary (Student studying class between 6 to 10th)
2) inter (Student studying class between 11th and 12th).
In each URI, we specify the user type, for example see below:
(http://localhost:8080/TestProject/login/secondary/authenticate)
For above request, I need to fetch the data from 'secondary' d.b tables.
Similarly for other user type request, need to communicate with other d.b(Inter).
In 'DAO' class:
NamedParameterJdbcTemplate jdbcTemplate = new NamedParameterJdbcTemplate(
getDataSource());
jdbcTemplate.getJdbcOperations().execute(
"SET SCHEMA " + **UriUtils.getSchema()**);
In above UriUtils.getSchema(), method returns the 'DataBase' name.
private DataSource getDataSource() {
String db = UriUtils.getDataBaseName();
DataSource dataSource = null;
try {
InitialContext initialContext = new InitialContext();
Context environmentContext = (Context) initialContext
.lookup("java:comp/env");
dataSource = (DataSource) environmentContext.lookup(db);
} catch (NamingException e) {
logger.error(e.getMessage());
logger.info(db + " resource is not available in server.xml file");
e.printStackTrace();
}
return dataSource;
}
In Tomcat server I configured the connection pooling.
server.xml
<Resource auth="Container" driverClassName="org.postgresql.Driver"
logAbandoned="true" maxActive="20" maxIdle="10" maxWait="-1"
name="secondary" password="admin" removeAbandoned="true"
removeAbandonedTimeout="90" type="javax.sql.DataSource"
url="jdbc:postgresql://localhost:5432/postgres?currentSchema=secondary"
username="postgres" />
<Resource auth="Container" driverClassName="org.postgresql.Driver"
logAbandoned="true" maxActive="20" maxIdle="10" maxWait="-1"
name="inter" password="admin" removeAbandoned="true"
removeAbandonedTimeout="90" type="javax.sql.DataSource"
url="jdbc:postgresql://localhost:5432/postgres?currentSchema=inter"
username="postgres" />
context.xml
<ResourceLink name="secondary" global="secondary"
type="org.postgresql.Driver" />
<ResourceLink name="inter" global="inter"
type="org.postgresql.Driver" />
Is loading the datasource object every time is a good practice ?
Please suggest if any better approach is available.
Is loading the datasource object every time is a good practice ?
NO, IMV.
Define two datasources (secondaryDS, interDS) as spring beans default to singleton, and inject corresponding datasource to JDBCTemplateclass as per your requirement.
You do not loading database every time. Lookup operation is not loading database. It is OK perform lookup on every request. Also I do not see two databases in your sample. You have two data sources over one postgresql database. You can use one data source and perform SET SCHEMA on each client request for schema switching.

Pattern for JDNI datasource

I'm using a JNDI ressource in Tomcat8 for connecting to a MS-SQL database (Azure). Randomly I experience Connection closed exception, eventually preceeded by Connection peer reset events. When this happens, the service is dead (running into Connection closed for every request) and restarting the tomcat (redploying) is the only chance to get it up again.
On my way trying to solve this I double(triple)-checked every method for unclosed connections, I assure that every connection is opened as try-with-ressource.
Currently I'm trying to get a better understanding about JNDI ressources and the connection pooling, I'm asking what is the preferred pattern to implement a service class which is injected into other services. E.g. questions are
Where should the DataSource be allocated by calling ctx.lookup()? On method level or class scope? E.g. when using the hk2 #Service annotation it seems that a service is instantiated only once and not per request. Currently ctx.lookup() is invoced once (in the constructor) and the DataSource is stored in a class field and later on accessed by the methods using this.dataSource. Does this make sense ? Or should the DataSource be retrieved on every request (=method call)
How can I verify the execution of several options of the DataSource, e.g. testOnBorrow and removeAbandoned (see complete configuration below) are executed correctly? There is an option logAbandoned but I can not see anything in my logs. Where should this appear anyhow? Can I somehow specifiy a certain log level for the pool? I only found org.apache.tomcat.jdbc.pool, but this class seems only to be called when creating the pool (at least this is the only moment when logs appear, even on level FINEST).
Are there general patterns which I'm not aware of?
Here my current configuration:
<Resource name="jdbc/mssql"
auth="Container"
type="javax.sql.XADataSource"
driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"
username="***"
password="***"
url="jdbc:sqlserver://***.database.windows.net:1433;database=***;encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30;"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
removeAbandonedOnBorrow="true"
removeAbandonedTimeout="55"
removeAbandonedOnMaintenance="true"
timeBetweenEvictionRunsMillis="34000"
minEvictableIdleTimeMillis="55000"
logAbandoned="true"
validationQuery="SELECT 1"
validationInterval="34000"
/>
Thx, gapvision
Gapvision, you can check this link - What is the good design pattern for connection pooling?
Probably, you would want to go with the object pool pattern.
Hope this helps !!
I'm asking what is the preferred pattern to implement a service class which is injected into other services.
Try spring data. It is very helpful in organizing resources to access data.
Without spring, without tomcat built-in features, you indeed need create your own singletons to allocate DataSource or ConnectionPool.
Without spring(assuming you build web app for tomcat), you can add to web.xml:
<resource-ref>
<description>H2DB</description>
<res-ref-name>jdbc/project1</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
And in context.xml of tomcat:
<Resource name="jdbc/project1" auth="Container" type="javax.sql.DataSource" driverClassName="org.h2.Driver" url="jdbc:h2:tcp://localhost/~/project1" username="sa" password="" maxActive="20" maxIdle="10" maxWait="-1"/>
And then you can lookup data source in each request:
Context ctx = new InitialContext();
Context envContext = (Context) ctx.lookup("java:comp/env");
DataSource ds = (DataSource) envContext.lookup("jdbc/project1");
Connection conn = null;
try {
conn = ds.getConnection();
PreparedStatement ps = conn.prepareStatement("INSERT INTO USERS (NAME) VALUES (?)");
ps.setString(1,name);
ps.executeUpdate();
response.getWriter().write("added user "+name);
response.getWriter().write("\n");
} finally {
if (conn!=null) { conn.close(); }
}
Or you can create a singleton class and lookup DataSource once , on start or lazy, as singletons do.
But better try spring data.

Accessing attributes of resource from context.xml in program

Is there a way in java to access the values of the resource tag given in the context.xml from the program?
<Context ...>
...
<Resource name="jdbc/EmployeeDB" auth="Container"
type="javax.sql.DataSource" username="dbusername" password="dbpassword"
driverClassName="org.hsql.jdbcDriver" url="jdbc:HypersonicSQL:database"
maxActive="8" maxIdle="4"/>
...
</Context>
I know that we can get the the datasource the following way:
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
DataSource ds = (DataSource)
envCtx.lookup("jdbc/EmployeeDB");
Connection conn = ds.getConnection();
... use this connection to access the database ...
conn.close();
But is there a way to print the userName and password in the program that i give in the resource tag?
You can try to cast it to actual implementation of the resource (print out the name with .getClass().getName()) and use the methods provided there. For example tomcat's BasicDataSource has getPassword getUser methods.

TomEE Exception when trying to access datasource from JAX-WS

I have class called ConnectionManager
public class ConnectionManager{
public static getDBConnection()
{
Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/mydb");
return ds.getConnection();
}
}
When it tried to call this class from jsp page, servlet, jax-rs restful service, it just works fine.
However, I go the following exception when i try to call ConnectionManager.getDBConnection() from JAX-WS web service:
Name [comp/env/jdbc/mydb] is not bound in this Context. Unable to find [comp].
Please note that i defined the Resource inside TomEE_HOME/conf/context.xml:
<Resource name="jdbc/mydb" auth="Container" type="javax.sql.DataSource"
username="user" password="pass"
driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"
url="jdbc:sqlserver://10.x.x.x:xxxx;databaseName=MyDBName"/>
Please advice.

Categories