I have an application that i use for buiding reports, now I need to move this application to Web environment.
For that I am using Tomcat 8.0.15 and an Oracle Database 11g Enterprise Edition.
In my TOMCAT_HOME\conf\server.xml i have the following code:
<Resource auth="Container"
driverClassName="oracle.jdbc.OracleDriver"
maxIdle="10"
maxTotal="20"
maxWaitMillis="-1"
name="jdbc/reportDataSource"
username="some_username"
password="some_pass"
type="javax.sql.DataSource"
url="jdbc:oracle:thin:#(DESCRIPTION =(ADDRESS_LIST =(ADDRESS = (PROTOCOL = TCP)(HOST = some.host)(PORT = some.port)))(CONNECT_DATA =(SID = SOME_SID)(SERVICE_NAME = SOME_SERVICE)))"/>
Therefore in my PROJECT_HOME\WebContent\WEB-INF\web.xml I have the following:
<resource-ref>
<description>Oracle Datasource definition</description>
<res-ref-name>jdbc/reportDataSource</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
Finally in my code I have a Java class with the following:
private void init() throws NamingException, SQLException {
try {
InitialContext initialContext = new InitialContext(); // JNDI initial context
Context eventContext = (Context) initialContext.lookup("java:comp/env/jdbc/reportDataSource"); // Event context
dataSource = (DataSource) eventContext.lookup("jdbc/reportDataSource"); // JNDI lookup
databaseConnection = dataSource.getConnection(); // database connection through data source
} catch (SQLException se) {
throw new SQLException("Connection object was not created. Rejected by host or not found.");
} catch (NamingException ne) {
throw new NamingException(ne.getMessage());
}
}
Finally in my project root I have have the following test setup:
#Before
public void setUp() throws Exception {
dbConnectorManager = new DatabaseConnectorManager();
assertNotNull(dbConnectorManager);
}
When I call the DatabaseConnectorManager() it calls the init() method shown in this question. However when I execute my test I got the following error related with line:
entContext eventContext = (Context) initialContext.lookup("java:comp/env/jdbc/reportDataSource"); // Event context
Hence, it is not possible to setup JNDI due the following error:
javax.naming.NamingException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
Can you please advise if I can create these JUnit tests for testing the connectivity, or if I can only test through a servlet?
Is there anything wrong with my configuration?
Update
I made the desiganted changes but now I get the following error:
javax.naming.NamingException: Name [jdbc/reportDataSource] is not bound in this Context. Unable to find [jdbc].
My data source now looks like the following:
InitialContext initialContext = new InitialContext(); // JNDI initial context
Context eventContext = (Context) initialContext.lookup("java:comp/env"); // Event context
dataSource = (DataSource) eventContext.lookup("jdbc/reportDataSource"); // JNDI lookup
databaseConnection = dataSource.getConnection(); // database connection through data source
'not sure this is the error, but you probably have a typo here:
Context eventContext =
(Context) initialContext.lookup("java:comp/env/jdbc/reportDataSource"); // Event context
// ^^^^^^^^^^^^^^^^^^^^^^
dataSource = (DataSource) eventContext.lookup("jdbc/reportDataSource"); // JNDI lookup
// ^^^^^^^^^^^^^^^^^^^^^^
Either you want to do a direct lookup:
dataSource =
(DataSource) initialContext.lookup("java:comp/env/jdbc/reportDataSource");
Or you want to get the context first, but in that case, you only requests java:comp/env:
Context eventContext =
(Context) initialContext.lookup("java:comp/env");
dataSource = (DataSource) eventContext.lookup("jdbc/reportDataSource");
In Server.xml, provide a name to your resource and do the lookup based on that name. Another point is, you can add a resource as a new context.xml under META-INF folder under webapps. This is done if you don't want to change your server.xml
<Resource name="tomcat/JDBCdatasource" auth="Container" ... />
Context ctx;
ctx = new InitialContext();
Context envContext = (Context) ctx.lookup("java:/comp/env");
// Look up a data source
javax.sql.DataSource ds
= (javax.sql.DataSource) envContext.lookup ("tomcat/JDBCdatasource");
Related
I am using jdbc connection pooling and i am getting the connection more than 100 place in the web application. how can i write the common code in single class and call, where required.
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
DataSource ds = (DataSource)envContext.lookup("jdbc/UserDB");
Connection con = ds.getConnection();
I have created Datasource and try to get connection object using the below code,
Hashtable ht = new Hashtable();
ht.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
ht.put(Context.PROVIDER_URL, "t3://localhost:7001");
java.sql.Connection vendorConn = null;
try {
ctx = new InitialContext(ht);
javax.sql.DataSource ds
= (javax.sql.DataSource) ctx.lookup("jdbc/myDataSource");
conn = ds.getConnection();
} catch (SQLException e) {
LOGGER.error(e.getMessage());
}
Below I have mentioned connection object and callable object,
weblogic.jdbc.rmi.SerialConnection_weblogic_jdbc_rmi_internal_ConnectionImpl_weblogic_jdbc_wrapper_JTAConnection_weblogic_jdbc_wrapper_XAConnection_oracle_jdbc_driver_LogicalConnection_12130_WLStub#d4
cstmt = (weblogic.jdbc.rmi.SerialCallableStatement_weblogic_jdbc_rmi_internal_CallableStatementStub_weblogic_jdbc_rmi_internal_CallableStatementImpl_weblogic_jdbc_wrapper_CallableStatement_oracle_jdbc_driver_OracleCallableStatementWrapper_12130_WLStub) weblogic.jdbc.rmi.SerialCallableStatement_weblogic_jdbc_rmi_internal_CallableStatementStub_weblogic_jdbc_rmi_internal_CallableStatementImpl_weblogic_jdbc_wrapper_CallableStatement_oracle_jdbc_driver_OracleCallableStatementWrapper_12130_WLStub#145
I am getting the below exception when i called a store procedure using callable
java.sql.SQLException: weblogic.rmi.extensions.RemoteRuntimeException:
Unexpected Exception
at weblogic.jdbc.rmi.SerialStatement.close(SerialStatement.java:126)
at weblogic.jdbc.rmi.SerialStatement.close(SerialStatement.java:110)
at weblogic.ejb.container.internal.MDListener.execute(MDListener.java:451)
at weblogic.ejb.container.internal.MDListener.transactionalOnMessage(MDListener.java:375)
at weblogic.ejb.container.internal.MDListener.onMessage(MDListener.java:310)
at weblogic.jms.client.JMSSession.onMessage(JMSSession.java:4855)
at weblogic.jms.client.JMSSession.execute(JMSSession.java:4529)
at weblogic.jms.client.JMSSession.executeMessage(JMSSession.java:3976)
at weblogic.jms.client.JMSSession.access$000(JMSSession.java:120)
at weblogic.jms.client.JMSSession$UseForRunnable.run(JMSSession.java:5375)
at weblogic.work.SelfTuningWorkManagerImpl$WorkAdapterImpl.run(SelfTuningWorkManagerImpl.java:548)
at weblogic.work.ExecuteThread.execute(ExecuteThread.java:311)
at weblogic.work.ExecuteThread.run(ExecuteThread.java:263)
java.sql.SQLException: prepareStatement, Exception = Unexpected Exception
at weblogic.jdbc.rmi.RMIWrapperImpl.invocationExceptionHandler(RMIWrapperImpl.java:102)
at weblogic.jdbc.rmi.RMIStubWrapperImpl.invocationExceptionHandler(RMIStubWrapperImpl.java:34)
at weblogic.jdbc.rmi.SerialConnection.prepareStatement(SerialConnection.java:236)
at weblogic.ejb.container.internal.MDListener.execute(MDListener.java:451)
Please suggest is this the right way to use Datasource connection where all my sql statements are working but procedure is not getting called and also I need Is it required to typecast the SerialConnection to sql.Connection.
CallableStatement cst = null;
try {
cst = conn
.prepareCall("{call myProc(?,?,?,?,?,?,?,?)}");
final String typeTableName = "studentdetails";
cst.setInt(1, student.getEmpid());
cst.setInt(2, student.getOrgid());
cst.setInt(3, student.getYearid());
cst.setString(4, student.getClassType());
cst.setInt(5, student.getStudentid());
cst.registerOutParameter(6, Types.ARRAY, typeTableName);
cst.registerOutParameter(7, java.sql.Types.VARCHAR);
cst.registerOutParameter(8, java.sql.Types.VARCHAR);
long startTime=System.currentTimeMillis();
cst.execute();
String dat=cst.getString(7);
//Array arr = cst.getArray(6);
long endTime=System.currentTimeMillis();
if (null != cst.getObject(6)) {
data = (Object[]) ((Array) cst.getObject(6)).getArray();
}
If I use datasource, I am getting cst.getObject(6) as null, but if use normal jdbc connection it is working fine by providing the object.
If you are using Weblogic you must add the following element to your weblogic.xml
<resource-description>
<res-ref-name>datasource_ref</res-ref-name>
<jndi-name>jdbc/myDataSource</jndi-name>
</resource-description>
Then you must reference it from your deployment descriptor web.xml by adding following element
<resource-ref>
<res-ref-name>datasource_ref</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
Then you can reference your Data Src from your Java code like follows,
Context cntxt= (Context)new InitialContext().lookup("java:comp/env");
DataSource ds= (DataSource)cntxt.lookup("datasource_ref");
Or by using resource injection like follows,
#ApplicationScoped
public class DBHandler{
#Resource(name="datasource_ref")
private javax.sql.DataSource myDB;
public Connection getConnection(){
return myDB.getConnection();
}
}
And you can use it on demand anywhere using CDI
DBHandler handler = CDI.current().select(DBHandler.class).get();
Or by field injection
#Inject
javax.enterprise.inject.Instance<DBHandler> instance;
....
void persist(){
DBHandler handler=instance.get();
Connection con= handler.getConnection();
}
I am learning Spring MVC. I am trying to use #Resource to inject DataSource. It is like this:
web.xml of Tomcat:
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/TestDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
context.xml:
Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000"
username="sa" password="" driverClassName="org.h2.Driver"
url="jdbc:h2:tcp://localhost/~/test"/>
The controller code (using Spring MVC framework):
#Controller
public class SimpleControllerAnnotation {
//#Resource(name="dataSource")
#Resource(name="jdbc/TestDB")
private DataSource dataSource;
public DataSource getDataSource() {
return dataSource;
}
//#Resource(name="dataSource")
#Resource(name="jdbc/TestDB")
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
#RequestMapping("/testDataSource")
public ModelAndView testDataSource() {
Connection con = null;
Statement stmt = null;
ResultSet rs = null;
String name = null;
String ID = null;
try {
con = dataSource.getConnection();
stmt = con.createStatement();
rs = stmt.executeQuery("select ID, name from STUDENT");
while(rs.next()){
name = rs.getString("name");
ID = rs.getString("ID");
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
try {
if(rs != null) rs.close();
if(stmt != null) stmt.close();
if(con != null) con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
ModelAndView mw = new ModelAndView("TestDataSourceForm");
mw.addObject("DataSourceValue",dataSource);
mw.addObject("Name",name);
mw.addObject("ID",ID);
return mw;
}
In this code, I am using #Resource to inject the DataSource, which I intend to "get" from Tomcat, which I set up in Tomcat (the web.xml and context.xml shared above).
When I run this program, I get the following exception:
SEVERE: Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'simpleControllerAnnotation': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'jdbc/TestDB' is defined
The jdbc/TestDB is the DataSource which I set up in Tomcat.
I have following queries:
1) Is it possible to have DataSource which we create in Tomcat to be injected this way? Or we have to use JNDI lookup. In one of the posts that I read on internet, it was said that JNDI lookup is sort of outdated and these days Dependency injection is preferred way.
2) In general, is it best practice to set-up the DataSources in App server/Web Container or to manage in the application itself. From what I read over the posts, it is preferred let App server/Container to manage this.
Any help to past this error really appreciated.
Apache Tomcat processes #Resource annotations only on classes that it itself loads (such as Filters, Servlets and Listeners).
In your case your controller class is loaded by Spring Framework and Spring is responsible for processing the #Resource annotation. Read the Spring documentation (Reference guide).
According to Spring Reference Guide [1], the value in #Resource annotation is the name of a Spring bean.
It says that the name can be used for JDNI lookup if you configure a SimpleJndiBeanFactory, but recommends against it and advices to configure referenced beans explicitly. -> [2]
[1] http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-resource-annotation
[2] http://docs.spring.io/spring/docs/current/spring-framework-reference/html/xsd-config.html#xsd-config-body-schemas-jee
I'm trying to register a new datasource before the server starts but on lookup execution I'm getting
javax.naming.NameNotFoundException: Name [jdbc/db] is not bound in this Context. Unable to find [jdbc].
This is how I start tomcat:
Tomcat tomcat = new Tomcat();
//...
ContextResource resource = new ContextResource();
resource.setName("jdbc/db");
resource.setAuth("Container");
resource.setType("javax.sql.DataSource");
resource.setScope("Sharable");
resource.setProperty("driverClassName", "org.hsqldb.jdbc.JDBCDriver");
resource.setProperty("url", "jdbc:hsqldb:hsql://localhost:1234/mydb1");
tomcat.getServer().getGlobalNamingResources().addResource(resource);
tomcat.start();
tomcat.getServer().await();
The lookup:
Connection conn = null;
try {
Context initContext = new InitialContext();
Context envContext = (Context) initContext.lookup("java:/comp/env");
DataSource ds = (DataSource) envContext.lookup("jdbc/db");
conn = ds.getConnection();
conn.createStatement()....
} catch (Exception e) {
e.printStackTrace();
}
What am I missing here?
Oh well i figured it out! Instead of adding it in the GlobalNamingResources
tomcat.getServer().getGlobalNamingResources().addResource(resource);
I added it in the NamingResources
Context rootCtx = tomcat.addContext("", base.getAbsolutePath());
//...
rootCtx.getNamingResources().addResource(resource);
and it works!
If someone can tell me the difference between globalNamingResources and (local)NamingResources and how to lookup a globalNamingResource then please leave me a comment!
You don't show us how you look up the JNDI resource. However, in any case the full JNDI name to your resource is java:comp/env/jdbc/db i.e. that's what you need for lookup.
There's further reading here: https://stackoverflow.com/a/4099163/131929
I have installed Websphere Network deployment server 7.0.0.0
I have configured a cluster on it.
I have configured a data source on it say ORA_DS this data source using "JAAS - J2C authentication data"
When i test the ORA_DS by clicking on "Test connection" button, the test connection is success.
The issue comes when i try to access this data source using my java code.
Here is my code to access data source and create a connection:
public class DSTester
{
/**
* Return the data source.
* #return the data source
*/
private DataSource getDataSource()
{
DataSource dataSource = null;
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.ibm.websphere.naming.WsnInitialContextFactory");
env.put(Context.PROVIDER_URL, "iiop://localhost:9811");
// Retrieve datasource name
String dataSourceName = "EPLA1";
if (dataSource == null)
{
try
{
Context initialContext = new InitialContext(env);
dataSource = (DataSource) initialContext.lookup(dataSourceName);
}
catch (NamingException e1)
{
e1.printStackTrace();
return null;
}
}
return dataSource;
}
public static void main(String[] args)
throws Exception
{
DSTester dsTester = new DSTester();
DataSource ds = dsTester.getDataSource();
System.out.println(ds);
System.out.println(ds.getConnection());
}
}
Here is the output:
com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource#17e40be6
Exception in thread "P=792041:O=0:CT" java.sql.SQLException: ORA-01017: invalid username/password; logon denied
DSRA0010E: SQL State = 72000, Error Code = 1,017
at oracle.jdbc.driver.SQLStateMapping.newSQLException(SQLStateMapping.java:70)
at oracle.jdbc.driver.DatabaseError.newSQLException(DatabaseError.java:133)
at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:206)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:455)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:406)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:399)
at oracle.jdbc.driver.T4CTTIoauthenticate.receiveOauth(T4CTTIoauthenticate.java:799)
at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:368)
at oracle.jdbc.driver.PhysicalConnection.<init>(PhysicalConnection.java:508)
at oracle.jdbc.driver.T4CConnection.<init>(T4CConnection.java:203)
at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:33)
at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:510)
at oracle.jdbc.pool.OracleDataSource.getPhysicalConnection(OracleDataSource.java:275)
at oracle.jdbc.pool.OracleDataSource.getConnection(OracleDataSource.java:206)
at oracle.jdbc.pool.OracleConnectionPoolDataSource.getPhysicalConnection(OracleConnectionPoolDataSource.java:139)
at oracle.jdbc.pool.OracleConnectionPoolDataSource.getPooledConnection(OracleConnectionPoolDataSource.java:88)
at oracle.jdbc.pool.OracleConnectionPoolDataSource.getPooledConnection(OracleConnectionPoolDataSource.java:70)
at com.ibm.ws.rsadapter.spi.InternalGenericDataStoreHelper$1.run(InternalGenericDataStoreHelper.java:1175)
at com.ibm.ws.security.util.AccessController.doPrivileged(AccessController.java:118)
at com.ibm.ws.rsadapter.spi.InternalGenericDataStoreHelper.getPooledConnection(InternalGenericDataStoreHelper.java:1212)
at com.ibm.ws.rsadapter.spi.WSRdbDataSource.getPooledConnection(WSRdbDataSource.java:2019)
at com.ibm.ws.rsadapter.spi.WSManagedConnectionFactoryImpl.createManagedConnection(WSManagedConnectionFactoryImpl.java:1422)
at com.ibm.ws.rsadapter.spi.WSDefaultConnectionManagerImpl.allocateConnection(WSDefaultConnectionManagerImpl.java:81)
at com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource.getConnection(WSJdbcDataSource.java:646)
at com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource.getConnection(WSJdbcDataSource.java:613)
at com.test.DSTester.main(DSTester.java:70)
The code works fine if i replace
ds.getConnection()
with
ds.getConnection("ora_user", "ora_password")
My issue is i need to get the connection without specifying login details for Oracle.
Please help me on this issue.
Any clue will be appreciated.
Thanks
I'd guess it would work if you retrieved the datasource from an application running on the WAS.
Try creating a servlet.
Context initialContext = new InitialContext();
DataSource dataSource = (DataSource) initialContext.lookup("EPLA1");
Connection con = dataSource.getConnection();
As within a servlet it is running within WAS it should be fine, if the "Test Connection" works. Running it outside is probably a different context.
I think you need to check all your configuration:
1) Is it application deplyed on cluster or into only one of cluster member?
2) JAAS - J2C authentication data - what is the scope?
Sometimes you need restar all your WAS environment. It depends on resource configuration scope
I'd recomend to you add resource refences for better configuration options.
SeeIBM Tech note