Here's the class
package db;
import java.io.File;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* #author Oshadha Gunawardena
*/
public class DBFacade {
private static Connection c;
public static void connect() throws Exception {
if (c == null) {
Properties prop = new Properties();
FileInputStream fis = new FileInputStream("props.xml");
prop.loadFromXML(fis);
String dbUrl = prop.getProperty("dburl");
String dbDriver = prop.getProperty("dbdriver");
String dbUser = prop.getProperty("username");
String dbPass = prop.getProperty("password");
Class.forName(dbDriver).newInstance();
c = DriverManager.getConnection(dbUrl, dbUser, dbPass);
}
}
public static ResultSet fetch(String sql) throws Exception {
connect();
synchronized (c) {
return c.createStatement().executeQuery(sql);
}
}
public static void save(String sql) throws Exception {
connect();
synchronized (c) {
c.createStatement().executeUpdate(sql);
}
}
}
I'm using this class as my database facade class,so my entire project is a web application I'm calling this fetch and save methods using a servlet, but when I try to run this It throws an exception (java.io.FileNotFoundException). All the paths are set correctly props.xml file is in my project home directory also it works when I try to print the data to the out put.
String dbUrl = prop.getProperty("dburl")
System.out.println(dbUrl);
Problem only occurs when I try to deploy and run the project.
Note: I'm using NetBeans 6.1 as my primary IDE.
Thanks
If you are loading from war file , you should be using
getClass().getResourceAsStream("props.xml");
As J-16 SDiZ says, your file is probably ending up in the war file - or if it's not, then your working directory probably isn't what you think it is.
You say that props.xml is in the "project home directory" - where is it after deployment? Is it in a war file (in which case you'll need to use getResourceAsStream(), although I suspect that Class.getClassLoader().getResourceAsStream("props.xml") is more likely to work:
InputStream input = null;
try
{
input YourClassName.class.getClassLoader.getResourceAsStream("props.xml");
if (input == null)
{
// Throw an appropriate exception here to show you can't find your file
}
prop.loadFromXML(input);
}
finally
{
if (input != null)
{
input.close();
}
}
If it really is a file, you'll need to find some way of working out the directory it's in programmatically, then use:
File file = new File(directory, "props.xml");
FileInputStream fis = new FileInputStream(file);
// And close it in a finally block as above
If you used the getResourceAsStream method, then your property file must be on the classpath. See the documentation for your servlet container to see what it sets the classpath to.
However, since we are talking about JDBC connections, this isn't the right approach to use. The preferred mechanism for obtaining connections is using a DataSource.
Instructions for configuring a DataSource in Tomcat can be found here
Using a DataSource object is the preferred alternative to using the DriverManager for establishing a connection to a data source. They are similar to the extent that the DriverManager class and DataSource interface both have methods for creating a connection, methods for getting and setting a timeout limit for making a connection, and methods for getting and setting a stream for logging.
Their differences are more significant than their similarities, however. Unlike the DriverManager, a DataSource object has properties that identify and describe the data source it represents. Also, a DataSource object works with a JavaTM Naming and Directory InterfaceTM (JNDI) naming service and is created, deployed, and managed separately from the applications that use it. A driver vendor will provide a class that is a basic implementation of the DataSource interface as part of its JDBC 2.0 or 3.0 driver product. What a system administrator does to register a DataSource object with a JNDI naming service and what an application does to get a connection to a data source using a DataSource object registered with a JNDI naming service are described later in this chapter.
Being registered with a JNDI naming service gives a DataSource object two major advantages over the DriverManager. First, an application does not need to hardcode driver information, as it does with the DriverManager. A programmer can choose a logical name for the data source and register the logical name with a JNDI naming service. The application uses the logical name, and the JNDI naming service will supply the DataSource object associated with the logical name. The DataSource object can then be used to create a connection to the data source it represents.
The second major advantage is that the DataSource facility allows developers to implement a DataSource class to take advantage of features like connection pooling and distributed transactions. Connection pooling can increase performance dramatically by reusing connections rather than creating a new physical connection each time a connection is requested. The ability to use distributed transactions enables an application to do the heavy duty database work of large enterprises.
Related
I am attempting to set up various PostgreSQL JDBC driver properties to my HikariCP pool, but for some reason, it's stating that those properties don't exist. Why so? Am I using the wrong parameter names?
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource
import java.sql.Connection;
import java.sql.SQLException;
public class HikariTest {
public static void main(String[] args) throws SQLException {
HikariConfig config = new HikariConfig();
config.setDataSourceClassName("org.postgresql.ds.PGSimpleDataSource");
config.setUsername("[REDACTED]");
config.setPassword("[REDACTED]");
config.addDataSourceProperty("host", "[REDACTED");
config.addDataSourceProperty("database", "[REDACTED]");
config.addDataSourceProperty("ssl", true);
config.addDataSourceProperty("sslcert", "[REDACTED]");
HikariDataSource ds = new HikariDataSource(config);
Connection conn = ds.getConnection();
}
}
Output:
Exception in thread "main" java.lang.RuntimeException: Property database does not exist on target class org.postgresql.ds.PGSimpleDataSource
at com.zaxxer.hikari.util.PropertyElf.setProperty(PropertyElf.java:127)
at com.zaxxer.hikari.util.PropertyElf.lambda$setTargetFromProperties$0(PropertyElf.java:51)
at java.base/java.util.concurrent.ConcurrentHashMap.forEach(ConcurrentHashMap.java:1603)
at java.base/java.util.Properties.forEach(Properties.java:1422)
at com.zaxxer.hikari.util.PropertyElf.setTargetFromProperties(PropertyElf.java:46)
at com.zaxxer.hikari.pool.PoolBase.initializeDataSource(PoolBase.java:323)
at com.zaxxer.hikari.pool.PoolBase.<init>(PoolBase.java:112)
at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:93)
at com.zaxxer.hikari.HikariDataSource.<init>(HikariDataSource.java:81)
at HikariTest.main(HikariTest.java:21)
It gives that error because PGSimpleDataSource does not have a property database (i.e. it doesn't have a setDatabase(String) method). It does have a property databaseName (setDatabaseName defined in BaseDataSource). This property is specified in section 9.6.1 DataSource Properties of the JDBC 4.3 specification.
Reading the comments, it looks like you're confusing the documentation of the JDBC URL format (and connection properties) with the properties that are available on the data source implementations provided by the driver. To be clear, that documentation doesn't specify there is a property database, it only uses database as a placeholder in the JDBC URL syntax (as in jdbc:postgresql://host/database.
Looking at answers from high reputation users such as this it seems that it's appropriate to get a new DataSource object by querying the JNDI naming service on every single connection request. E.g. with code like the following (adapted from the linked answer for more brevity):
public class ConnectionManager{
public static Connection getConnection() throws NamingException {
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
DataSource dataSource = (DataSource)envContext.lookup("jdbc/test");
return dataSource.getConnection();
}
}
Is this really, the suggested / idiomatic way? In some of my own "ConnectionManager" utilty classes I used to keep a reference to the DataSource object as an instance variable. Nothing wrong came out of it except when the JBoss administrator disabled and enabled the connection pool from the admin console and then my code was getting errors like the following:
java.sql.SQLException: javax.resource.ResourceException: IJ000451: The connection manager is shutdown
So, is it an anti-pattern to keep around instances of DataSource objects in JDBC?
A DataSource object can be cached and is thread-safe, although JNDI ought to be well-enough optimized that getting the DS out of JNDI every request is negligible (the same instance will be handed back from JNDI).
If you're working in a Java EE environment for example, it's spec standard to be able to inject a DataSource at the class level, such as:
public class MyServlet extends HttpServlet {
#Resource
DataSource ds;
public void processRequest() {
try(Connection con = ds.getConnection()) {
// ...
}
}
}
Also, it's completely safe to share DataSource objects across multiple threads. On the other hand, sharing Connection objects across multiple threads is a big mistake, because those are NOT threadsafe per spec.
In a very large project where do we set up the database connection so that it is available across all the modules?
Suppose the requirement is like this:
LoginPage.html -> LoginServlet.java -> LoginService.java ==> Takes DB help to check the credentials.
Now, since the actual credentials are stored in DB, where do we set up the database so that the connection is available to all the modules?
In big projects, is database connection made as and when needed or database connections setup at the time when application is run and made available across all the modules.
If DB connections are made available to all the modules (which need DB connectivity), how is this achieved?
Thanks for your help and inputs.
Since you're not using an IoC approach (Spring), the alternative would be to have a static class (or a singleton) that has a reference to the DataSource. Whenever you need a Connection you only have to get it from that class:
public class JdbcUtils{
private static DataSource dataSource;
static{
dataSource = new DB2SimpleDataSource();
dataSource.setDatabaseName("DBNAME");
dataSource.setServerName("xxx.xxx.xxx.xxx");
dataSource.setPortNumber(447);
dataSource.setUser("USER");
dataSource.setPassword("PASS");
dataSource.setDriverType(4);
dataSource.setCurrentSchema("SCHEMA");
//OR even better get the DataSource through JNDI lookup if defined on server
}
public static Connection getConnection() throws SQLException{
return dataSource.getConnection()
}
}
I have configured MysqlDataSource in tomcat using this link.I have written junit test cases.when am i calling below connection from junit it throws following errors.
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
I have used following code
class DataConnection {
private static DataSource dataSource;
public DataConnection() {
try {
Context ctx = new InitialContext();
dataSource = (DataSource)ctx.lookup("java:comp/env/jdbc/test");
} catch (NamingException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
new DataConnection();
Connection con=dataSource.getConnection();
return con;
}
}
How to call tomcat from junit? How to achieve this?
The code you give gets the database connection from JNDI, e.g. when running in tomcat from the container. However, for Unit Tests (assuming that's what you use JUnit for) I'd rather suggest to use "dependency injection" - e.g. explicitly pass a database connection to the code under test or manually set it up before the test runs.
There's no need to rely on JNDI for executing your tests: That's not what you want to test, instead, you want to just verify that your actual code is running correctly.
You don't need any fancy library (e.g. spring) for dependency injection, just a slightly adjusted architecture. This will greatly enhance the testability of your application and lower the execution time of your tests.
(This is based on my assumptions of your situation based on the little bit of information that you give in your question)
Give TomcatJNDI a try. It is based on embedded Tomcat but initializes only Tomcat's JNDI environment without starting a server. So you can access all your resources as configured in Tomcat's configuration files in tests or from within any Java SE application. The API is simple. For instance to get a DataSource declared in context.xml:
TomcatJNDI tomcatJNDI = new TomcatJNDI();
tomcatJNDI.processContextXml(contextXmlFile);
tomcatJNDI.start();
Then you can lookup the DataSource as usual
DataSource ds = (DataSource) InitialContext.doLookup("java:comp/env/path/to/datasource")
More information about TomcatJNDI can be found here.
Warning: New to Java
I have a simple Netbeans project - I wanted to just learn about interacting with DB's coming from php I thought I would have a go with a local one running on my computer.
Lots of the examples out there say to use the InitialContext() object to refer to the database resource.
After following the examples I get the following exception - Lots of Google stuff points to some .xml file - which I have no idea about or even where it exists in the Netbeans project? I'm not using a Webserver at this time so not Tomcat or anything like that, just local Java program to do this, I suspect this might be the problem. Could anyone shed some light on this?
Exception thrown 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.
package learningjava;
import com.mysql.jdbc.jdbc2.optional.*;
import com.mysql.jdbc.Driver;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.*;
public class LearningJava {
public static void main(String[] args) {
MysqlDataSource test_db = new MysqlDataSource();
test_db.setServerName("localhost");
test_db.setDatabaseName("dev");
try {
InitialContext test_db_context = new InitialContext();
test_db_context.bind("jcdb/testdb", test_db);
MysqlDataSource test_db_datasource = (MysqlDataSource)test_db_context.lookup("testdb");
} catch (NamingException e) {
System.out.println("Exception thrown " + e);
}
try {
test_db.getConnection("root","password");
} catch (SQLException e) {
System.out.println("Exception thrown " + e);
}
}
}
could you try to add this before the InitialContext test_db_context = new InitialContext();:
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.naming.java.javaURLContextFactory");
System.setProperty(Context.URL_PKG_PREFIXES, "org.apache.naming");
You should include the jar naming-common-4.1.34 and mysql-connector-java-5.1.6 in your classpath
This example works for me (not optimized but works!)
public static void main(String[] args) throws NamingException {
// Create initial context
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.naming.java.javaURLContextFactory");
System.setProperty(Context.URL_PKG_PREFIXES, "org.apache.naming");
InitialContext ic = new InitialContext();
ic.createSubcontext("java:");
ic.createSubcontext("java:comp");
ic.createSubcontext("java:comp/env");
ic.createSubcontext("java:comp/env/jdbc");
MysqlConnectionPoolDataSource mysqlConnectionPoolDataSource = new MysqlConnectionPoolDataSource();
mysqlConnectionPoolDataSource.setUser("root");
mysqlConnectionPoolDataSource.setPassword("root");
mysqlConnectionPoolDataSource.setURL("jdbc:mysql://localhost:3306/test_my_database");
ic.bind("java:comp/env/jdbc/test", mysqlConnectionPoolDataSource);
}
In general you should understand that JNDI should have a server. In a code snippet you've provided you're using a _CLIENT_SIDE_ part of JNDI technology when you're doing your lookup. There should be a JNDI server that should be accessible from your local client connection.
Once configured properly, the call to lookup should issue a connection with JNDI server and provide a way to obtain/bind resources to that server.
How to configure JNDI properly?
Usually you should supply a properties file that will contain a host name of this server, a port + some implementation specific information.
JNDI server is usually already provided when you're using application server (like JBoss or Web Sphere).
I think this is the root of misunderstanding here.
Hope, this helps
Usually JNDI is used inside an application server which is not your case.
For your needs you may use the following code:
Class.forName("com.mysql.jdbc.Driver")
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/dbname", "user", "password");
Also you need to download MySQL driver here and add corresponding JAR file (mysql-connector-java-5.1.21-bin.jar) to your application's class path here is described how to do it.