Mock database driver - java

Is there some kind of JDBC driver which simply ignores database calls?
For the development I am migrating an application to a virtual machine. Here I want to work on the GUI part only. But the application makes several requests to a database which doesn't let the application even start. I don't want to change the application code at this time since the database is pretty much coupled.
So I was thinking there could be a JDBC driver which just returns empty results for queries.

I decided to write an own simple mock driver. This was pretty much straight forward and did what I want. I can switch the database driver of the application by a configuration file so I could let the application use my driver on a simple way.
Then I extended the driver to return data which it parses from CSV files. I published the code on google code maybe someone else can get use of it: dummyjdbc

There are some "void" JDBC drivers as part of Mocking framewroks, for example MockDriver from Mockrunner.
But using it requires some coding.
That's because when Java application connects to a database it provides a JDBC URL in form jdbc:mysql://localhost. The system is searching which driver is registered in it to handle this kind of URL and chooses the right driver. The info about which URL type driver supports is contained in the driver itself, and it's impossible for a mock driver to hold all known URL types in it - there's no such thing as wildcarding there and any list would not be full.
So, if you're able to call JDBCMockObjectFactory.registerMockDriver() in the application before it connects to the database - it will do the job. If not - I don't think it's possible. However, slight modification of the driver code would do it... but again - coding is required.

jOOQ ships with a MockConnection that can be provided with a MockDataProvider, which is much easier to implement than the complete JDBC API. This blog post shows how to use the MockConnection:
http://blog.jooq.org/2013/02/20/easy-mocking-of-your-database/
An example:
MockDataProvider provider = new MockDataProvider() {
// Your contract is to return execution results, given a context
// object, which contains SQL statement(s), bind values, and some
// other context values
#Override
public MockResult[] execute(MockExecuteContext context)
throws SQLException {
// Use ordinary jOOQ API to create an org.jooq.Result object.
// You can also use ordinary jOOQ API to load CSV files or
// other formats, here!
DSLContext create = DSL.using(...);
Result<MyTableRecord> result = create.newResult(MY_TABLE);
result.add(create.newRecord(MY_TABLE));
// Now, return 1-many results, depending on whether this is
// a batch/multi-result context
return new MockResult[] {
new MockResult(1, result)
};
}
};
// Put your provider into a MockConnection and use that connection
// in your application. In this case, with a jOOQ DSLContext:
Connection connection = new MockConnection(provider);
DSLContext create = DSL.using(connection, dialect);
// Done! just use regular jOOQ API. It will return the values
// that you've specified in your MockDataProvider
assertEquals(1, create.selectOne().fetch().size());
There is also the MockFileDatabase, which helps you matching dummy results with SQL strings by writing a text file like this:
# This is a sample test database for MockFileDatabase
# Its syntax is inspired from H2's test script files
# When this query is executed...
select 'A' from dual;
# ... then, return the following result
> A
> -
> A
# rows: 1
# Just list all possible query / result combinations
select 'A', 'B' from dual;
> A B
> - -
> A B
# rows: 1
select "TABLE1"."ID1", "TABLE1"."NAME1" from "TABLE1";
> ID1 NAME1
> --- -----
> 1 X
> 2 Y
# rows: 2

My framework Acolyte is a tested JDBC driver designed for such purposes (mock up, testing, ...): https://github.com/cchantep/acolyte
It already used in several open source projects, either in vanilla Java, or using its Scala DSL:
// Register prepared handler with expected ID 'my-unique-id'
acolyte.Driver.register("my-unique-id", handler);
// then ...
Connection con = DriverManager.getConnection(jdbcUrl);
// ... Connection |con| is managed through |handler|

Never heard of such a driver myself. If you don't find one, you could instead use a DB like HSQLDB. You can configure it to use in-memory tables, so nothing else gets written to disk. You would have to use a different connection string, though.

If you want to do unit tests, not an integration tests, than
you can use a very basic and simple approach, using Mockito only, like this:
public class JDBCLowLevelTest {
private TestedClass tested;
private Connection connection;
private static Driver driver;
#BeforeClass
public static void setUpClass() throws Exception {
// (Optional) Print DriverManager logs to system out
DriverManager.setLogWriter(new PrintWriter((System.out)));
// (Optional) Sometimes you need to get rid of a driver (e.g JDBC-ODBC Bridge)
Driver configuredDriver = DriverManager.getDriver("jdbc:odbc:url");
System.out.println("De-registering the configured driver: " + configuredDriver);
DriverManager.deregisterDriver(configuredDriver);
// Register the mocked driver
driver = mock(Driver.class);
System.out.println("Registering the mock driver: " + driver);
DriverManager.registerDriver(driver);
}
#AfterClass
public static void tearDown() throws Exception {
// Let's cleanup the global state
System.out.println("De-registering the mock driver: " + driver);
DriverManager.deregisterDriver(driver);
}
#Before
public void setUp() throws Exception {
// given
tested = new TestedClass();
connection = mock(Connection.class);
given(driver.acceptsURL(anyString())).willReturn(true);
given(driver.connect(anyString(), Matchers.<Properties>any()))
.willReturn(connection);
}
}
Than you can test various scenarios, like in any other Mockito test e.g.
#Test
public void shouldHandleDoubleException() throws Exception {
// given
SomeData someData = new SomeData();
given(connection.prepareCall(anyString()))
.willThrow(new SQLException("Prepare call"));
willThrow(new SQLException("Close exception")).given(connection).close();
// when
SomeResponse response = testClass.someMethod(someData);
// then
assertThat(response, is(SOME_ERROR));
}

If you're using Spring, make your own class that implements Datasource and have the methods do nothing.

Related

Oracle JDBC Driver loaded in one class not accessible in another class in same package

I am working on an EAR application(deployed on WLS 12c) that has a feature to configure and test jdbc connection for Oracle. So, in my jsp page the user selects the driver as oracle.jdbc.OracleDriver, enter the connectionurl, username and password and clicks on testConnection. And on doing this, the applicaiton gives back an error stating java.sql.SQLException: No suitable driver found for
jdbc:oracle:thin:#XXX-xx:1525/YYYY
A little brief of the architecture used in this application :
On application startup, we are referring to an xml that has all the JDBC defaults and try to load all the supported jdbc drivers that are present in that xml. And oracle.jdbc.OracleDriver is also one of them. This registering is done through a util class JDBCUtil.java which has 2 static methods : registerDriver(String driverName) and isDriverRegistered(String driverName).
So, the first time that JDBCUtil.registerDriver("oracle.jdbc.OracleDriver") is called, isDriverRegistered returns false and within registerDriver, we are making a call to class.forName(driverName) to load the driver.
The logic within isDriverRegistered gets an enumeration of all the drivers registered with DriverManager by calling DriverManager.getDrivers and checks if the driverName string passed to it equals driver.getClass.getName.
After the 1st call to JDBCUtil for OracleDriver, any subsequent calls to registerDriver for OracleDriver is expected to skip the class.forName because the isDriverRegistered is expected to return true. But upon adding logs statement and debugging, i noticed that everytime registerDriver->isDriverRegistered is called for OracleDriver, it is always returning false. The enumearation obtained by calling DriverManager getDrivers, never contains OracleDriver.
Secondly, In another class JDBCDS.java, we are calling JDBCUtil.registerDriver("oracle.jdbc.OracleDriver") and as expected, in registerDriver function exceute Class.forName("oracle.jdbc.OracleDriver") because isDriverRegistered returns false . In JDBCDS.java, after the call to registerDriver, we call DriverManager.getConnection by passing in the url,usename and password. And this call is throwing SQLException saying NoSuitableDriverFound.
So, as another debug test, i called Class.forName("oracle.jdbc.OracleDriver") in JDBCDS.java and then made a call to DriverManager.getConnection and this time i was able to get a connection successfully.
I have gone through all the question related to NoSuitableDriverFound exception on this forum, but nothing is fitting my issue.
I have tried to call create a new instanc of the driver by calling new oracle.jdbc.OracleDriver() within JDBCDS.java and see if it accepts my url and it does. Moreover, this has also ruled out any classpath problem. The jar is certainly in the classpath. Our classpath includes the following:
com.oracle.db.jdbc7-dms.jar and also weblogic.jar
com.oracle.db.jdbc7-dms.jar internally has a link to ojdbc8dms.jar and weblogic.jar internally has ojdbc8.jar included through the Class-Path attribute of its MANIFEST.MF files in both the cases.
Also, this issue is isolated to only a particular environment. In other environments with the same setup, everything works fine without issues.
From JDBCUtil.java :
static Util Method that is used to register the driver :
public static void registerDriver(String driverName)
{
// Calls isDriverRegistered,isDriverRegistered returns false, then call Class.forName(driverName)
}
static method to see if driver is alreay registered :
public static boolean isDriverRegistered(String driverName)
{
//call DriverManager.getDrivers to get all the registered drivers
if(driver.getClass().getName().equals(driverName))
{
return true;
}
return false;
}
Code in JDBCDS.java that is used to perform the test connection:
try {
JDBCUtil.registerDriver(driver);
DriverManager.setLoginTimeout(300);
conn = DriverManager.getConnection(url, username, password); //This code throws No Suitable Driver Found Exception
} catch (SQLException e) {
// tried the below as a test
try
{
Class.forName(driver);
}
catch(Exception ex)
{
Logger.log("Could not load the driver with the current classloader", 4);
throw e;
}
conn = DriverManager.getConnection(url, username, password);//At this step the connection is successfully obtained
}
Both JDBCUtil.java and JDBCDS.java are in the same package.
Can anybody point me towards what i can do further to resolve this issue?

What to Do when JDBC Driver is Invalid?

I'm working on a Java project that uses an Oracle database. When I create a new Oracle driver:
jdbcDriver driver = new jdbcDriver();
...I check it to see if it is valid using:
if (!driver.isValid())
{
throw new UncheckedSqlException("jdbcDriver not valid");
}
What is the correct procedure if the driver is invalid? That is, there is no way to store the data and retry later asynchronously (i.e. without locking up the app), since the database is unavailable. Is the assumption that if I just retry a few times, I'll get a valid driver?
UPDATE
Thanks to EJP for correctly pointing out that jdbcDriver is a class in my code. That is correct -- it is a class in a code base I am working on. EJP requests to have the relevant details posted. Here they are.
package us.mydev.jdbc.util;
import java.sql.*;
import javax.naming.*;
import javax.sql.*;
import org.apache.log4j.Logger;
import us.mydev.data.exception.UncheckedSqlException;
public class jdbcDriver
{
[.....]
public jdbcDriver()
{
try
{
m_iActiveConnections++;
if (showLogMessages)
{
log.debug("jdbcDriver() created, hashcode = " + this.hashCode()
+ ",Active Connection Count is " + m_iActiveConnections);
log.debug("Data Source is " + DS_STANDARDUSER);
}
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup(DS_STANDARDUSER);
mydevGetJDBCConn getConn = new sqlConn(ds);
m_conn = getConn.getConnection();
m_iDatabaseType = getConn.getDBType();
}
catch (NamingException _exp)
{ // handle any errors
log.error("failed", _exp);
}
}
public boolean isValid() throws SQLException
{
return m_conn != null && m_conn.isValid(10);
}
[.....]
}
I am finding that after this code:
jdbcDriver driver = new jdbcDriver();
boolean driverIsValid = driver.isValid();
...driverIsValid is false about once in every 1000 or so database accesses. I would imagine that this is an artifact of my current dev system -- I have Oracle running in a virtual machine.
Would it be unusual (or unheard of) for driverIsValid to be false in production in a situation like this? And if it is not unheard-of -- how is it usually handled, given that I can't save the relevant data to the database -- that is, can I just retry getting a database connection a few times and expect to get one?
UPDATE #2
Based on what I've learned from the responses, I need to ask this question using no code from the objects defined in the code base I am working on. I'm going to accept the answer provided by Wen-Bin Luo and re-ask the question more properly.
I think you are meaning if a connection is still valid at time of checking. The isValid method returns whether or not a connection is usable. This method can be invoked at any time you like and allows you to set a timeout.
According to the official manual, the method is
particularly useful in combination with a retry mechanism.
helpful when used in conjunction with the connection timeout and connection harvesting features.
You of course can retry the method in the hope of the database issue resolves afterwards, but more generally you set a timeout and after that you may have to take appropriate action and close the connection.
try { conn = poolDataSouorce.getConnection ... } catch (SQLException sqlexc)
{
if (conn == null || !((ValidConnection) conn).isValid())
// take the appropriate action
conn.close
}
For more information, please check https://docs.oracle.com/cd/B28359_01/java.111/e10788/connect.htm#CHDIDEEB

How does JDBC know where to look for driver class?

I am wondering how JDBC knows which database driver class it should use.
Example:
Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
Connection verbindung = DriverManager.getConnection("jdbc:derby:d:/memory/onlineshop;create=true");
The first line takes Care, that a driver (EmbeddedDriver) will be loaded into to class loader (and so be available, e.g. for using with reflections, right?).
So, the next line is my connection string. It starts with:
jdbc:derby:...
I expected something like this instead:
jdbc:ConcreteDriverClassForInit
As you can see i am missing a link between the class loaded in the class loader and the connection string call of that class.
I searched in the derby archive for a class named "Derby.Class" - but there is no such class.
Even when I try sth. like this, JDBC still knows, what to do:
Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
Class.forName("org.something.anyotherDBDriver1");
Class.forName("org.something.anyotherDBDriver2");
Connection verbindung = DriverManager.getConnection("jdbc:derby:d:/memory/onlineshop;create=true");
But why?
Thanks for your help!
That EmbeddedDriver class has a static block executed when you load the class that adds an handler for the specific JDBC type:
static {
EmbeddedDriver.boot();
}
Check the code of the boot method here, and you'll see where the protocol is registered:
new JDBCBoot().boot(Attribute.PROTOCOL, ps);
That specific string is located in org.apache.derby.iapi.reference:
String PROTOCOL = "jdbc:derby:";
That's a common pattern that is followed by all JDBC drivers, i don't particularly like the code of this driver, if you want a cleaner example look at the SQLite driver, way more straightforward implementation:
static {
try {
DriverManager.registerDriver(new JDBC());
}
catch (SQLException e) {
e.printStackTrace();
}
}
org.sqlite.JDBC will register itself to the java.sql.DriverManager that will invoke JDBC.isValidURL(String url) to know if this class is a valid driver for a specific JDBC url, the SQLite driver will return true only if the url contains the PREFIX jdbc:sqlite:.
In previous versions of JDBC, to obtain a connection, you first need to load your JDBC driver by calling the method Class.forName().
Currently any JDBC 4.0 drivers that are found in your class path are automatically loaded. So, there is not even the need for Class.forName().
The gist of it is to be found in the documentation for java.sql.Driver and java.sql.DriverManager.
Basically, starting with JDBC 4, all you have to do is create a META-INF/services/java.sql.Driver file for your SQL driver implementation and the JRE will load it automatically. Which means that you can directly try and do:
DriverManager.getConnection("yourUrlHere")
If one driver recognizes the URL, it will be used automatically.

How to use CachedRowSet in Google App Engine?

I am using Google App Engine (Endpoints) to generate a simple API service.
I'm currently trying to make my queries to the database faster and more efficient, so I decided to double check where I was calling ResultSet and Connections and closing them.
However I want to use CachedRowSet and it almost seems impossible given that when I try to access CachedRowSetImpl() the logger returns:
java.lang.NoClassDefFoundError: com.sun.rowset.CachedRowSetImpl is a
restricted class. Please see the Google App Engine developer's guide
for more details
So I did some investigation and Google App Engine's JRE whitelist includes:
javax.sql.rowset.CachedRowSet
but not
com.sun.rowset.CachedRowSetImpl
Does that make any sense? How can I initialize/use CachedRowSet otherwise?
My code:
public ResultSet getRecordsWhereInt(String table, String where, int condition) throws SQLException {
c = DatabaseConnect();
preStatement = c.prepareStatement("SELECT * FROM " + table + " WHERE " + where + " = ?");
preStatement.setInt(1, condition);
CachedRowSet rowset = new CachedRowSetImpl();
rowset.populate(preStatement.executeQuery());
c.close();
return rowset;
}
Any help would/guidance would be appreciated.
Update 1 (09/06/2015):
Investigating further, I found that you can implement CachedRowSetImpl's functionality using RowSetProvider.newFactory().createCachedRowSet();
However, Google has also restricted use to RowSetProvider().
That left me with the only other whitelisted library RowSetFactory. So I made my own, implementing RowSetFactory according to Oracle's implementation:
public class CachedRowSetFactory implements RowSetFactory {
public CachedRowSet createCachedRowSet() throws SQLException {
return new CachedRowSetImpl();
}
public FilteredRowSet createFilteredRowSet() throws SQLException {
return null;
}
public JdbcRowSet createJdbcRowSet() throws SQLException {
return null;
}
public JoinRowSet createJoinRowSet() throws SQLException {
return null;
}
public WebRowSet createWebRowSet() throws SQLException {
return null;
}
}
And then used it like this:
CachedRowSet crs = new CachedRowSetFactory().createCachedRowSet();
However, even this fails. The above example returns a Null Pointer Exception when I try to populate it with a working result set. I'm guessing the factory implementation isn't 100% correct or that I'm skipping over something obvious.
Update 2 (11/06/2015):
I tried rolling my own implementation of CachedRowSetImpl, and of the RowSetProvider by duplicating some of the libraries by hand and eliminating the libraries not covered in App Engines Whitelist. Was not successful. I know I need to give up on this but I'm determined to make this work.
CahecRowSet is an interface so obviously this is whitelisted.
Interface CachedRowSet
I am guessing you are connecting a Google CloudSQL instance.
from the javadoc :
A CachedRowSet object is a container for rows of data that caches its
rows in memory, which makes it possible to operate without always
being connected to its data source.
What you are trying to do does not make sense IMO. AppEngine does not keep state between different requests and certainly not when your application auto-scales and new instances spin up.
Why don't you cache your results in Memcache, this will persist over subsequent requests and different instances.

Running test for database adapter using main() Method

I have a problem in a system that I am working as back-end support for. I need to write a test that calls one of the classes handeling the communications with our database so I can log out what it actually returns.
System setup
Our system is developed in Java and deployed on a weblogic server. It consists of many parts that I will not go into detail on here. But the interesting part is that we have a class acting as an adapter for our database. We call it "CMAdapter" and it is an implementations of IBM Content Manager specific code to handle interaction with our database. In this class we have a methid called fetchAct() that take one object with search parameters as an argument and it returns the result of the search. In this case it returns one act. The code we have is running on a weblogic server, that has an IBM Information Integrator for Content installed so that it can communicate with IBM Content Manager that is installed and running on a different server. The system is deployed on the server using a .ejb and a few .jar files.
The problem
I have recieved a case stating that for some acts the users are not recieving the full act as expected but only parts of it. The system itself displays no errors and the documents are present in the database. So what I am trying to do is write a simple test program that calls this "CMAdapter" with a predetermined set of searchcriteria so that I may log out the return of the search.
My question
How can I make a freestading class with a main() method and run it on the server? I need to make a call to the CMAdapter.fetchAct() method in a way so it runs on the server like any normal query?
My test class
public class TestHamtaAkt
{
public static void main(String[] args) throws BasException
{
Log LOG = Log.getLog(TestHamtaAkt.class);
// Get the CMAdapter
CMAdapter cmadapter = new CMAdapter();
// Create empty instance of our query object
SokVO sokvo = new SokVO();
// Put a value to search for in our query object
AttributVO aktAttribut = new AttributVO();
aktAttribut.setNamn(DLAKonstanter.AKT_KORT_R_KOD);
aktAttribut.setVarde("090084831574");
sokvo.aktAttributLista().add(aktAttribut);
// do the search an recieve the answer
AktVO aktvo = cmadapter.hamtaAkt(sokvo);
// log out the result
LOG.debug("main", "Akten som hämtades: " + aktvo.toString(), null);
}
}
Thanks to all for reading my question. It appears I have found the answer to my own question. It was hiding with a collegue of mine. The answer to my problem was, to be able to access the server deployed code, I need to get the JNDI context from my webserver and from that do a jndi lookup for the class I need.
I still have some problems making the connection but that is problably just my configurations that are off. I now know how I get a simple java class to make a call to a deployed class on a server.
here is the code I am currently using to get the context from the WLS server:
private static InitialContext getWLSContext(String url) throws NamingException
{
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, WLS_CONTEXT_FACTORY);
//env.put(Context.PROVIDER_URL, "t3://" + host + ":" + port);
env.put(Context.PROVIDER_URL, url);
return new InitialContext(env);
}
This is my code for geting the class I need.
public static EJBObject getRemote(String url, String jndiname, Class homeClass, AppserverTyp typ) throws Exception
{
Object obj = getWLSContext(url).lookup(jndiname);
EJBHome home = (EJBHome) javax.rmi.PortableRemoteObject.narrow(obj, homeClass);
Class homeBase = home.getClass();
Method m = homeBase.getMethod("create", (Class[])null);
EJBObject remote = (EJBObject) m.invoke(home, (Object[])null);
return remote;
}
I hope this helps someone with a similar problem to move forward. Like I said I have still to actually get this code working for me but my this is the answer to my initial question on how to make a call to a deployed method from an external class.

Categories