I've set up JNDI resources before, but I'm coming across an issue I'm not sure how to correct on my apache-tomcat-8.0.36 server.
My context.xml file contains the following:
<ResourceLink name="jdbc/FsEDBAdmin" global="jdbc/FsEDBAdmin" type="javax.sql.DataSource" />
<ResourceLink name="jdbc/FsEDBUser" global="jdbc/FsEDBUser" type="javax.sql.DataSource" />
<Resource name="jdbc/FsEDBAdmin" auth="Container"
type="javax.sql.DataSource" driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://location"
username="user_admin" password="pass"
maxActive="20" maxIdle="10" maxWait="-1"/>
<Resource name="jdbc/FsEDBUser" auth="Container"
type="javax.sql.DataSource" driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://location"
username="user_user" password="pass"
maxActive="20" maxIdle="10" maxWait="-1"/>
My web.xml :
<resource-ref>
<description>Admin Connection</description>
<res-ref-name>jdbc/FsEDBAdmin</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<resource-ref>
<description>User Connection</description>
<res-ref-name>jdbc/FsEDBUser</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
I'm also using these connections to define a Realm for authentication:
server.xml:
<Realm className="org.apache.catalina.realm.LockOutRealm">
<!-- This Realm uses the UserDatabase configured in the global JNDI
resources under the key "UserDatabase". Any edits
that are performed against this UserDatabase are immediately
available for use by the Realm. -->
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
<Realm className="org.apache.catalina.realm.DataSourceRealm"
digest="digest_method" dataSourceName="jdbc/FsEDBAdmin" userTable="schema.table_name"
userNameCol="name_col" userCredCol="pass_col"
userRoleTable="schema.table_name" roleNameCol="rolename_col"
localDataSource="true"/>
</Realm>
But when I launch my application I'm getting the error mentioned in the title. I can give a full stack trace if requested.
I'd like to add on, that this was working at one point and the most recent change was the use of the Realm for authentication. Obviously I've made a mistake in defining these resources in one location or another, so another set of eyes to tell me where would be greatly appreciated. Thank you.
EDIT: This is how I'm calling the resource:
import path.to.CommonTools;
import java.sql.Connection;
import java.sql.SQLException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.apache.tomcat.dbcp.dbcp2.BasicDataSource;
/**
*
* #author Michael Potts
*/
public enum Resource {
INSTANCE;
private static BasicDataSource basicAdmin = null;
private static BasicDataSource basicUser = null;
private static final String JNDI_PREFIX = "java:/comp/env";
private static final String ADMIN_DB_NAME = "jdbc/FsEDBAdmin";
private static final String USER_DB_NAME = "jdbc/FsEDBUser";
/**
* Secure method that gives a connection from the pool for an Admin user
* #return java.sql.Connection
* #throws Exception Throws if Context isn't properly defined on Server
*/
public static Connection getAdminConnection() throws Exception {
try {
if(basicAdmin == null) { //1st time load
Context dbContext = (Context) new InitialContext().lookup(JNDI_PREFIX);
basicAdmin = (BasicDataSource) dbContext.lookup(ADMIN_DB_NAME);
}
return basicAdmin.getConnection();
} catch (NamingException | SQLException e) {
throw new RuntimeException("\nInvalid JNDI resource: " + ADMIN_DB_NAME + CommonTools.getStackTrace(e));
}
}
/**
* Secure method that gives a connection from the pool for a standard user
* #return java.sql.Connection
* #throws Exception Throws if Context isn't properly defined on Server
*/
public static Connection getUserConnection() throws Exception {
try {
if(basicUser == null) { //1st time load
Context dbContext = (Context) new InitialContext().lookup(JNDI_PREFIX);
basicUser = (BasicDataSource) dbContext.lookup(USER_DB_NAME);
}
return basicUser.getConnection();
} catch (NamingException | SQLException e) {
throw new RuntimeException("\nInvalid JNDI resource: " + USER_DB_NAME + CommonTools.getStackTrace(e));
}
}
}
So the solution for me was to remove ALL ENTRIES except the base Resource definitions in context.xml. This includes removing ResourceLink entries. So if you've tried everything and you're getting this error like I am just ignore all documentation and just define in Context.xml.
Related
I'm making a webapp using JSP and MYSQL.
I have set up a connection pool and used it in a Servlet, but I cannot access it.
I had to create manually the "lib" folder inside WEB-INF to put the database connector and the META-INF folder to put the context.xml file, because those folders were not there at the begining.
I'm following a tutorial from 2016 so I don't know how different it is now.
Thanks in advance for your help.
Src folder:
Main files:
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
</web-app>
context.xml
<Context>
<Resource name="jdbc/wishes" auth="Container" type="javax.sql.DataSource" maxActive="15" maxIdle="3" maxWait="5000" username="root" password="" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/jee">
</Resource>
</Context>
Servlet
package com.gabit.dev.makeawish.controllers;
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
#WebServlet(name = "ServletDatabase", value = "/ServletDatabase")
public class ServletDatabase extends HttpServlet {
private static final long serialVersionUID = 1L;
#Resource(name = "jdbc/wishes")
private DataSource myPool;
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter output = response.getWriter();
response.setContentType("text/plain");
Connection myConnection = null;
Statement myStatement = null;
ResultSet myResult = null;
try {
myConnection = myPool.getConnection();
String query = "SELECT * FROM wishes";
myStatement = myConnection.createStatement();
myResult = myStatement.executeQuery(query);
while (myResult.next()) {
String title = myResult.getString(2);
output.println(title);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Error
java.lang.NullPointerException: Cannot invoke "javax.sql.DataSource.getConnection()" because "this.myPool" is null
at com.gabit.dev.makeawish.controllers.ServletDatabase.doGet(ServletDatabase.java:31)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:683)
...
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:833)
Comment on the edit: I think I had already fixed my original issue (driver could not be found) and now I have the following problem:
EDIT 2: when I replace the url in context.xml with the full path (url=jdbc:sqlite:C:/Dev/workspace/jerseyrestdemo/resources/greetings.sqlite) it works. How can I now make that path relative??
EDIT 3: I followed the instructions inhow to configure JNDI datasource for db connection in tomcat: 1. Delete my project-specific context.xml and put the content into tomcat7/conf/context.xml. There I specified the namespace name=jdbc/greetings.sqlite
<Resource name="jdbc/greetings.sqlite"
auth="Container"
type="javax.sql.DataSource"
maxActive="100"
maxIdle="30"
maxWait="10000"
driverClassName="org.sqlite.JDBC"
url="jdbc:sqlite:resources/greetings.sqlite"
factory="org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory">
</Resource>
In the DAO class I get the connection via
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
DataSource ds = (DataSource) envCtx.lookup("jdbc/greetings.sqlite");
conn = ds.getConnection();
But I still get the error org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (path to 'resources/greetings.sqlite': 'C:\Dev\eclipse\resources' does not exist)
EDIT 4: In the end, I go for storing the db file in the catalina home
/${catalina.home}/databases/greetings.sqlite as explained here. I guess you shouldn't store your db files in the web app project anyways
I have a SQLite db file in resources/greetings.sqlite under my project folder but when I run my webapp on tomcat it cannot find the db file:
java.sql.SQLException: path to 'resources/greetings.sqlite': 'C:\Dev\eclipse\resources' does not exist
at org.sqlite.core.CoreConnection.open(CoreConnection.java:192)
at org.sqlite.core.CoreConnection.<init>(CoreConnection.java:76)
at org.sqlite.jdbc3.JDBC3Connection.<init>(JDBC3Connection.java:25)
at org.sqlite.jdbc4.JDBC4Connection.<init>(JDBC4Connection.java:24)
at org.sqlite.SQLiteConnection.<init>(SQLiteConnection.java:45)
at org.sqlite.JDBC.createConnection(JDBC.java:114)
at org.sqlite.JDBC.connect(JDBC.java:88)
at java.sql.DriverManager.getConnection(DriverManager.java:664)
at java.sql.DriverManager.getConnection(DriverManager.java:270)
at demo.rest.jersey.spring.dao.JdbcGreetingDAO.getRowCount(JdbcGreetingDAO.java:29)
at demo.rest.jersey.spring.GreetingService.randomGreeting(GreetingService.java:29)
at demo.rest.jersey.spring.GreetingService.greet(GreetingService.java:18)
at demo.rest.jersey.spring.SpringRequestResource.getHello(SpringRequestResource.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
I have copied the jdbc jar into the tomcat/lib directory and added the code below in META-INF/context.xml and WEB-INF/web.xml.
This is what I put into my web.xml
<resource-ref>
<description>Greetings Database</description>
<res-ref-name>jdbc/greetings.sqlite</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
and I have created a file in WebContent/META-INF/context.xml. I assume the relative path in the url field of the Resource is not how it should be.
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Resource name="jdbc/greetings.sqlite"
auth="Container"
type="javax.sql.DataSource"
driverClassName="org.sqlite.JDBC"
url="jdbc:sqlite:resources/greetings.sqlite"
factory="org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory">
</Resource>
</Context>
What I would like to avoid is putting the db file in a tomcat subdir but just make it part of the project, so others don't have to bother moving the file to their tomcat dir.
Here is the DAO class
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JdbcGreetingDAO implements GreetingDAO
{
private Connection getConn() throws SQLException
{
Connection conn = null;
try
{
Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/greetings.sqlite");
conn = ds.getConnection();
}
catch(NamingException ne)
{
System.out.println("Cannot get connection");
ne.printStackTrace();
}
return conn;
}
#Override
public void insert(String greeting) throws SQLException
{
Connection conn = getConn();
Statement st = conn.createStatement();
st.executeUpdate(String.format("INSERT INTO greetings (greeting) VALUES (%s)", greeting));
}
}
The DAO is used by a GreetingsService
public class GreetingService
{
#Autowired
private JdbcGreetingDAO dataSource;
public String addGreeting(String greeting) throws SQLException
{
dataSource.insert(greeting);
return greeting;
}
}
Thanks for the help
Using JUnit to test a class that connects to Database Java print this error:
Error: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
Using the same class in a Main Class i have the same error.
Using the same class in a servlet, testing it from the browser, it works with no problems.
My database class to connect with DB (the error is on "ds.getConnection();"):
import java.sql.Connection;
import java.sql.SQLException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
public class Database {
private static DataSource ds;
static {
try {
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
ds = (DataSource) envCtx.lookup("jdbc/mua");
} catch (NamingException e) {
System.out.println("Error:" + e.getMessage());
}
}
public static Connection getConnessione() throws SQLException {
return ds.getConnection();
}
}
This is the context.xml file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<Context>
<Resource name="jdbc/mua"
auth="Container"
driverClassName="com.mysql.jdbc.Driver"
type="javax.sql.DataSource"
username="root"
password="admin"
url="jdbc:mysql://localhost:3306/mua?useSSL=false"/>
</Context>
I am using tomcat datasource connection pool for my app. I have set the max number of connections to 20. I am running 5 concurrent requests at a time. However some of my calls are timing out because no available connection is found. How long does it take for a connection to get back to the pool once is completed? Are there any properties that would help with this?
I was looking at different attributes in this link
The default connection timeout 60sec.
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
public class SimplePOJOExample {
public static void main(String[] args) throws Exception {
PoolProperties p = new PoolProperties();
p.setUrl("jdbc:mysql://localhost:3306/mysql");
p.setDriverClassName("com.mysql.jdbc.Driver");
p.setUsername("root");
p.setPassword("password");
p.setJmxEnabled(true);
p.setTestWhileIdle(false);
p.setTestOnBorrow(true);
p.setValidationQuery("SELECT 1");
p.setTestOnReturn(false);
p.setValidationInterval(30000);
p.setTimeBetweenEvictionRunsMillis(30000);
p.setMaxActive(100);
p.setInitialSize(10);
p.setMaxWait(10000);
p.setRemoveAbandonedTimeout(60);
p.setMinEvictableIdleTimeMillis(30000);
p.setMinIdle(10);
p.setLogAbandoned(true);
p.setRemoveAbandoned(true);
p.setJdbcInterceptors(
"org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;"+
"org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer");
DataSource datasource = new DataSource();
datasource.setPoolProperties(p);
Connection con = null;
try {
con = datasource.getConnection();
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("select * from user");
int cnt = 1;
while (rs.next()) {
System.out.println((cnt++)+". Host:" +rs.getString("Host")+
" User:"+rs.getString("User")+" Password:"+rs.getString("Password"));
}
rs.close();
st.close();
} finally {
if (con!=null) try {con.close();}catch (Exception ignore) {}
}
}
}
And here is an example on how to configure a resource for JNDI lookups
<Resource name="jdbc/TestDB"
auth="Container"
type="javax.sql.DataSource"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
testWhileIdle="true"
testOnBorrow="true"
testOnReturn="false"
validationQuery="SELECT 1"
validationInterval="30000"
timeBetweenEvictionRunsMillis="30000"
maxActive="100"
minIdle="10"
maxWait="10000"
initialSize="10"
removeAbandonedTimeout="60"
removeAbandoned="true"
logAbandoned="true"
minEvictableIdleTimeMillis="30000"
jmxEnabled="true"
jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;
org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"
username="root"
password="password"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mysql"/>
If you find any more difficulty refer this
https://tomcat.apache.org/tomcat-7.0-doc/jdbc-pool.html
I've been trying to find a solution here but I cant...
I have the following code and i get this error.
Am I missing something? Thank you :)
Code
package src;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Wrapper;
import java.util.Hashtable;
import java.util.Properties;
import java.io.*;
import javax.*;
import javax.activation.DataSource;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import oracle.jdbc.pool.OracleDataSource;
public class TestServlet {
#SuppressWarnings("unused")
public static void main(String[] argv) throws SQLException, NamingException {
Context initialContext = new InitialContext();
if ( initialContext == null){System.out.println("initialContext null");}
else {System.out.println("initialContext");}
// Get DataSource
Context environmentContext = (Context)initialContext.lookup("java:/comp/env");
if ( environmentContext == null){System.out.println("envContext null.");}
else {System.out.println("envContext");}
DataSource ds = (DataSource)environmentContext.lookup("jdbc/testdb");
System.out.println("\n -------- Oracle JDBC Connection Testing ------");
try {
Connection jdbcConnection = ((Statement) ds).getConnection();
OracleDataSource ods = ((Wrapper) ds).unwrap(OracleDataSource.class);
jdbcConnection.close();
} catch (SQLException e) {
System.out.println("Connection Failed! Check output console");
e.printStackTrace();
return;
}
String message = "You are connected!";
System.out.println(message);
}
}
context.xml
<Context>
<Resource name="jdbc/testdb"
auth="Container"
type="javax.sql.DataSource"
maxActive="100"
maxIdle="30"
maxWait="10000"
username="dba01"
password="qvE-g7Cacontext.xml"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:oracle:thin:#10.0.1.6:1521:xe"/>
</Context>
Error
Exception in thread "main" javax.naming.NoInitialContextException: 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
at javax.naming.spi.NamingManager.getInitialContext(Unknown Source)
at javax.naming.InitialContext.getDefaultInitCtx(Unknown Source)
at javax.naming.InitialContext.getURLOrDefaultInitCtx(Unknown Source)
at javax.naming.InitialContext.lookup(Unknown Source)
at src.TestServlet.main(TestServlet.java:34)
Please let me know if you need more information!
You need an initial context factory. For Tomcat it is org.apache.naming.java.javaURLContextFactory:
System.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.naming.java.javaURLContextFactory");
So is it the code deployed on the server? It seems you try to access the Context using standard java as you call static main (so I guess you are calling TeestServlet.main from your runtime). The context will be setup by the web server (Tomcat), so it is availble only after you deploy your web application to it.
Please note that driverClassName that you are using is incorrect.
It should be "oracle.jdbc.OracleDriver" if you are trying to connect to Oracle Database.
<Context>
<Resource name="jdbc/testdb"
auth="Container"
type="javax.sql.DataSource"
maxActive="100"
maxIdle="30"
maxWait="10000"
username="dba01"
password="qvE-g7Cacontext.xml"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:oracle:thin:#10.0.1.6:1521:xe"/>
</Context>