I have a method in one of the classes in my code base that for the life of me, I cannot get into with my junit tests.
Basically this class is called when I request a database connection, if a stale connection is returned, a new connection is established
Here is the snippet of the mthod in my class (trimmed down for this purpose)
public class TCSOracleDataSourceWrapper extends OracleDataSource {
private static final int STALE_CONNECTION_EX_CODE = 17143;
private OracleConnectionCacheManager cacheManager;
private String cacheName;
/** Local log variable **/
private final Log logger = LogFactory.getLog(getClass());
/**
* Class constructor
* #throws SQLException
*/
public TCSOracleDataSourceWrapper() throws SQLException {
super();
}
private static final long serialVersionUID = 1L;
#Override
/**
* Get a connection but if the connection is stale then refresh all DB connections
*
*/
public final Connection getConnection() throws SQLException {
logger.debug("Retrieving a database connection from the pool");
Connection connection = null;
try{
connection = super.getConnection();
}
catch(SQLException e)
{
if(e.getErrorCode() == STALE_CONNECTION_EX_CODE)
{
logger.error("Stale Oracle connection found in the Connection Pool. Refreshing invalid DB connections.");
//refresh invalid connections
cacheManager.refreshCache(cacheName, OracleConnectionCacheManager.REFRESH_INVALID_CONNECTIONS);
//now try to get the connection again
connection = super.getConnection();
}
else
{
throw e;
}
}
return connection;
}}
Any idea how I can ensure my junit tests execute the if statement?
I am currently using EasyMock and Powermock but I cannot find a way to get into this if statment using these tools
All help is greatly appreciated
Thank you
Damien
You should refactor your class to become a proxy for another data source, rather than inherit from one. This way you can easily inject into it a mock data source instead of the real one.
import javax.sql.DataSource;
public class TCSOracleDataSourceWrapper implements DataSource {
...
private DataSource wrappedDataSource;
...
public TCSOracleDataSourceWrapper(DataSource ds) {
wrappedDataSource = ds;
}
...
public final Connection getConnection() throws SQLException {
...
Connection connection = null;
try{
connection = ds.getConnection();
}
catch(SQLException e)
{
...
}
return connection;
}
}
One idea springs to mind: use aggregation rather than inheritance. This problem and others like it would go away because you can then mock the aggregated object to have whatever behavior you want. I don't see another way of getting in there right off hand. In fact, the name TCSOracleDataSourceWrapper already indicates that it's wrapping a data source (aggregation), when it actually isn't.
One quick workaround is to factor out the super.getConnection() call to a new private / protected method. Once you make that change it would be easy to mock the getBaseConnection method using power mock. This is short term fix, like the other answers suggest it is better to use delegation instead of inheritance for the wrapper implementation.
Connection getBaseConnection() throws SQLException {
return super.getConnection();
}
public final Connection getConnection() throws SQLException {
logger.debug("Retrieving a database connection from the pool");
Connection connection = null;
try{
connection = getBaseConnection();
}
catch(SQLException e)
{
if(e.getErrorCode() == STALE_CONNECTION_EX_CODE)
{
logger.error("Stale Oracle connection found in the Connection Pool. Refreshing invalid DB connections.");
//refresh invalid connections
cacheManager.refreshCache(cacheName, OracleConnectionCacheManager.REFRESH_INVALID_CONNECTIONS);
//now try to get the connection again
connection = getBaseConnection();
}
else
{
throw e;
}
}
return connection;
}
Related
I got to use MariaDB for my University Project.
it's my first time doing it, so I dont't know well how to use and code JDBC Driver and mariaDB.
Now I'm implementing the code in many places while looking at examples.
As I see, All the examples seems to creating Statement and making connection by using "DriverManager.getConnection"
Now I have a question.
I'm going to create a DBmanager Class that can connect, create tables, execute queries, and execute the code that updates data on tables in a single line.
I thought all the examples would run alone in one method and came from different places, so I could only try a new connection and create a code that would not close. But I have a gut feeling that this will be a problem.
Is there any way I can leave a connection connected at a single connection to send a command, and disconnect it to DB.disconnect()? And I'd appreciate it if you could tell me whether what I'm thinking is right or wrong.
The code below is the code I've written so far.
I am sorry if you find my English difficult to read or understand. I am Using translator, So, my English could not be display as I intended.
import java.sql.*;
import java.util.Properties;
public class DBManager {
/*********INNITIAL DEFINES********/
final static private String HOST="sumewhere.azure.com";//Azure DB URL
final static private String USER="id#somewhere";//root ID
final static private String PW="*****";//Server Password
final static private String DRIVER="org.mariadb.jdbc.Driver";//DB Driver info
private String database="user";
/***************API***************/
void setDB(String databaseinfo){
database=databaseinfo;
}
private void checkDriver() throws Exception
{
try
{
Class.forName("org.mariadb.jdbc.Driver");
}
catch (ClassNotFoundException e)
{
throw new ClassNotFoundException("MariaDB JDBC driver NOT detected in library path.", e);
}
System.out.println("MariaDB JDBC driver detected in library path.");
}
public void checkOnline(String databaseinfo) throws Exception
{
setDB(databaseinfo);
this.checkDriver();
Connection connection = null;
try
{
String url = String.format("jdbc:mariadb://%s/%s", HOST, database);
// Set connection properties.
Properties properties = new Properties();
properties.setProperty("user", USER);
properties.setProperty("password", PW);
properties.setProperty("useSSL", "true");
properties.setProperty("verifyServerCertificate", "true");
properties.setProperty("requireSSL", "false");
// get connection
connection = DriverManager.getConnection(url, properties);
}
catch (SQLException e)
{
throw new SQLException("Failed to create connection to database.", e);
}
if (connection != null)
{
System.out.println("Successfully created connection to database.");
}
else {
System.out.println("Failed to create connection to database.");
}
System.out.println("Execution finished.");
}
void makeCcnnection() throws ClassNotFoundException
{
// Check DB driver Exists
try
{
Class.forName("org.mariadb.jdbc");
}
catch (ClassNotFoundException e)
{
throw new ClassNotFoundException("MariaDB JDBC driver NOT detected in library path.", e);
}
System.out.println("MariaDB JDBC driver detected in library path.");
Connection connection = null;
}
public void updateTable(){}
public static void main(String[] args) throws Exception {
DBManager DB = new DBManager();
DB.checkOnline("DB");
}
}
For a studying project it's okay to give a connection from your DB Manager to client code and close it there automatically using try-with-resources construction.
Maybe you will find it possible to check Connection Pool tools and apply it further in your project or use as example (like HikariCP, here is a good introduction).
Read about Java try with resources. I think that this link could be usefull for your problem.
JDBC with try with resources
I am dealing with high traffic in my Spring Boot project and my goal is serving clients as much fast as possible. In this case, I have more than 500 requests per second. In each rest endpoint call, I should connect my schema and gather multiple information from multiple tables. To be able to do that, should I create new connection for each eendpoint call or create & close before each db query?
I wrote a JDBC connection class but I am not sure that it is a good way. Maybe you can give me some opinion.
JDBC Connection Class
#PropertySource({"classpath:application.properties"})
#Configuration
public class FraudJDBConfiguration {
private final Logger LOGGER = LogManager.getLogger(FraudJDBConfiguration.class);
private final Environment env;
#Autowired
public FraudJDBConfiguration(Environment env) {
this.env = env;
}
#Bean
public Connection getFraudConnection() {
// Step 1: Loading or
// registering Oracle JDBC driver class
String connectionClass = env.getProperty("fraud.db.driver-class-name");
try {
Class.forName(connectionClass);
} catch (ClassNotFoundException cnfex) {
LOGGER.error(cnfex.getMessage());
throw new RuntimeException("JDBC driver class'ı bulunamadı");
}
// Step 2: Opening database connection
try {
String environmentType = env.getProperty("environment");
if (environmentType == null) {
LOGGER.error("environment Tip Hatası (TEST - UAT - LIVE)");
throw new RuntimeException("environment Tip Hatası (TEST - UAT - LIVE)");
} else {
String connectionString = null;
String username = null;
String password = null;
switch (environmentType.toLowerCase()) {
case "dev":
connectionString = env.getProperty(/*someurl*/);
username = env.getProperty(/*someusername*/);
password = env.getProperty(/*somepassword*/);
break;
case "tst":
connectionString = env.getProperty(/*someurl*/);
username = env.getProperty(/*someusername*/);
password = env.getProperty(/*somepassword*/);
break;
case "liv":
connectionString = env.getProperty(/*someurl*/);
username = env.getProperty(/*someusername*/);
password = env.getProperty(/*somepassword*/);
break;
case "uat":
connectionString = env.getProperty(/*someurl*/);
username = env.getProperty(/*someusername*/);
password = env.getProperty(/*somepassword*/);
break;
}
// Step 2.A: Create and
// get connection using DriverManager class
if (connectionString == null) {
LOGGER.error("fraud şeması için connection string bulunamadı");
throw new RuntimeException("fraud şeması için connection string bulunamadı");
}
return DriverManager.getConnection(connectionString, username, password);
}
} catch (SQLException e) {
LOGGER.error(e.getMessage());
}
return null;
}
}
DAO
#Component
public interface FraudCommTransactionsDao {
Long count();
}
DAO IMPL
#Service
public class FraudCommTransactionsDaoImpl implements FraudCommTransactionsDao {
private final FraudJDBConfiguration fraudJDBConfiguration;
#Autowired
public FraudCommTransactionsDaoImpl(FraudJDBConfiguration fraudJDBConfiguration) {
this.fraudJDBConfiguration = fraudJDBConfiguration;
}
#Override
public Long count() {
try(Connection connection = fraudJDBConfiguration.getFraudConnection()) {
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(/*some query*/);
if (rs.next()) {
return rs.getLong("transaction_id");
} else {
return 0L;
}
} catch (SQLException ex) {
ex.printStackTrace();
}
return null;
}
}
No, establishing a new physical connection to a database server is costly. It involves multiple steps: user authorization, establishing session defaults, allocating memory on both client and server, etc. This overhead should not be added to every single request.
It's a common practice to create a connection pool to share the physical connections between application threads. This introduces a concept of logical connections e.g. a Connection object created with DriverManager.getConnection() is a physical connection while DataSource.getConnection() returns a logical connection which is a proxy.
There are multiple database connection pooling libraries for Java that you can use e.g. HikariCP. Don't write your own, this is not simple.
Get fast data and deliver to client could be possible using the simplest way of using application.properties file. You may use this to get database connection to your datasource.
I am trying to use PowerMockito to mock by DBUtil. Unlike typical testcase, I don't want to mock the db calls completely. Whenever Dbutil.getConnection() is called. I want to return the connection object to my local Database.
The simple jdbc connection code below is not working when i call from #BeforeClass method. But it works when I call from the java class.
public static Connection getConnection() throws Exception {
System.out.println("-------- Connecting to " + Constants.CONNECTION_STR + " ------");
try {
Class.forName(Constants.ORACLE_DRIVER_NAME);
}
catch (ClassNotFoundException e) {
throw new Exception("JDBC Driver not found... " + e);
}
catch (Exception e) {
// TODO: handle exception
System.out.println("getConnection :: exp :: "+ e);
}
System.out.println("Oracle JDBC Driver Registered Sucessfully!");
Connection connection = null;
try {
connection = DriverManager.getConnection(Constants.CONNECTION_STR, Constants.USERNAME, Constants.PASSWORD);
}
catch (SQLException e) {
throw new Exception("Connection Failed!",e);
}
if (connection != null) {
System.out.println("Connected to Database Sucessfully, take control your database now!");
return connection;
}
System.out.println("Failed to make connection!");
return null;
}
My Testclass
#RunWith (PowerMockRunner.class)
#PrepareForTest(DbUtil.class)
public class MyUtilTest {
#Mock
private DbUtil dbUtil;
#InjectMocks
private MyUtil myUtil;
private static Connection myDBConn;
#BeforeClass
public static void beforeClass() throws Exception {
myDBConn = OracleJDBCConnetion.getConnection(); // This always throws invalid username/password exception.
}
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void testIsAdminUser() throws Throwable{
PowerMockito.mockStatic(DbUtil.class);
PowerMockito.when(DbUtil.getConnection()).thenReturn(myDBConn);
String accId= "TH123" ;
boolean isAdmin = MyUtil.isAdminUser(cloudAccGuid);
System.out.println("isAdmin : " + isAdmin);
//then
PowerMockito.verifyStatic(Mockito.times(1));
DbUtil.getConnection();
assertTrue(isAdmin);
//Finally I am closing my connection.
if(myDBConn!=null && !myDBConn.isClosed())
OracleJDBCConnetion.closeConnection(myDBConn);
}
}
The beforeClass method always throws below expection.
Connection Failed! java.sql.SQLException: ORA-01017: invalid username/password; logon denied
But the same code works, when i try from normal Java class.
Can anyone help in understanding whats wrong here?
I am using ojdbc6.jar and powermokito-1.5.6 and my Oracle database version is 11.2.0.4.0
Thanks.
Edit :
I found that #PrepareForTest annotation is causing the error. without the annotation connection is successful but mock does not work. can anyone help me in understanding what is happening? I am very new to these mocking stuff.
The problem with #PrepareForTest annotation is, it recursively creates stubs for all dependent classes. Since DBUtil class uses java.sql.Connection class , a stub is created for Connection class also.
So, When i try to create connection, it refers to stub class and throws expection.
Add #PowerMockIgnore annotation to the class,to avoid it. #PowerMockIgnore annotation tells the powermock not to create for the classes that falls under the given package.
#RunWith (PowerMockRunner.class)
#PrepareForTest({DbUtil.class})
#PowerMockIgnore({"java.sql.*"})
public class MyUtilTest {
...
}
This worked for me.
I am developing a simple CRUD application, using JDBC to establish connection and perform basic CRUD operations. In that process, created a DatabaseListener to create a connection object at startup and storing it in the context attribute for reuse.
Below is the code.
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.log4j.Logger;
public class DatabaseInitListner implements ServletContextListener {
private static final Logger LOG = Logger.getLogger(DatabaseInitListner.class);
private DBUtil databaseUtil = null;
#Override
public void contextDestroyed(ServletContextEvent event) {
databaseUtil.closeConnection();
}
#Override
public void contextInitialized(ServletContextEvent contextinitEvent) {
ServletContext servletContext = contextinitEvent.getServletContext();
String database = servletContext.getInitParameter("db_name");
String url = servletContext.getInitParameter("db_url")
+ database;
String username = servletContext.getInitParameter("db_user");
String password = servletContext.getInitParameter("db_password");
String driverName = servletContext.getInitParameter("db_driver");
databaseUtil = new DBUtil(url, username, password,
driverName);
servletContext.setAttribute("databaseSingleConnectionObject",
databaseUtil.getConnection());
}
}
public class DBUtil {
private Connection connection = null;
private static final Logger LOG = Logger.getLogger(DatabaseUtil.class);
public DatabaseUtil(String url, String username, String password,
String driver) {
try {
Class.forName(driver);
this.connection = DriverManager.getConnection(url, username,
password);
LOG.debug("Connection Established... ");
} catch (ClassNotFoundException | SQLException e) {
LOG.error("Could not create connection... ", e);
}
}
public Connection getConnection() {
return connection;
}
public void closeConnection() {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
LOG.error("Unable to close connection... ", e);
}
}
}
}
I am accessing the connection in servlets like this
Connection jdbcConnection = (Connection) getServletContext().getAttribute("databaseSingleConnectionObject");
I am not sure if this is right approach. What are the effects of single database connection?
When you use a single database connection like this you make your application slow and brittle.
Slow: because the connection implementation is synchronized, each user has to wait until the connection is free. If one user's query takes a while to come back that directly increases the time any other concurrent users spend waiting. If there were multiple connections available from a pool then the time spent by one user would not impact other users nearly as greatly (unless a query's results take all the JVM's memory or a big query bogs down the database server).
Brittle: The connection is a network connection, they tend to go down. Without a provision to create new connections any kind of timeout, network hiccup, or period of database non-availability (such as taking the database offline for maintenance) is going to require an application restart. Using a connection pool will mean your application will be able to survive these episodes and recover without outside intervention.
This will not be threadsafe, and if it were, performance would be really poor.
Look into using a Connection Pool, like DBCP or C3PO
You should let your application server manage database connection. Add a JNDI datasource in its configuration file and make a lookup from your application to get a connection when needed (for instance when you instantiate a class that must access your database).
You may configure the datasource to manage a connection pool so that each user session will get its own.
Depending on the AS you use run a search with keywords 'JNDI' and 'datasource' and you will get further details about the AS configuration and how to implement it in your application.
The code below does NOT work:
Cause:
I assume I tracked down the cause to:
http://community.jboss.org/thread/150988
=> This article says that HornetQ uses Weak References.
My Question:
Why does the code not run? (I have this code running with a slight different implementation, but the code blow fails repeatedly). My only guess is, that the
following references:
private Connection connection = null;
private Session session = null;
private MessageProducer producer = null;
are not regarded as strong references? (And this leads to the fact that the garbage collector removes the objects... But way arent they strong references?
Or is there another problem with the code (as said the code runs fine if I copy everything into one single method. But if I use the Singleton approach below the code does not work...) Another assumption was that it might have to do with ThreadLocal stuff, but I am using only a single thread...
The Code not working (stripped down):
public class JMSMessageSenderTest {
private static final Logger logger = Logger.getLogger(JMSMessageSenderTest.class);
private static JMSMessageSenderTest instance;
private Connection connection = null;
private Session session = null;
private MessageProducer producer = null;
private JMSMessageSenderTest() {
super();
}
public static JMSMessageSenderTest getInstance() throws JMSException {
if (instance==null) {
synchronized(JMSMessageSenderTest.class) {
if (instance==null) {
JMSMessageSenderTest instanceTmp = new JMSMessageSenderTest();
instanceTmp.initializeJMSConnectionFactory();
instance = instanceTmp;
}
} }
return instance;
}
private void createConnectionSessionQueueProducer() throws Exception {
try {
Queue queue = HornetQJMSClient.createQueue("testQueue");
connection = initializeJMSConnectionFactory();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(queue);
connection.start();
} catch (Exception e) {
cleanupAfterError();
throw e;
}
}
private void cleanupAfterError() {
if (connection != null){
try{
connection.close();
}catch(JMSException jmse) {
logger.error("Closing JMS Connection Failed",jmse);
}
}
session = null;
producer = null;
}
public synchronized void sendRequest(String url) throws Exception {
if (connection==null) {
createConnectionSessionQueueProducer();
}
try {
//HERE THE EXCEPTION IS THROWN, at least when debugging
TextMessage textMessage = session.createTextMessage(url);
producer.send(textMessage);
} catch (Exception e) {
cleanupAfterError();
throw e;
}
}
private Connection initializeJMSConnectionFactory() throws JMSException{
Configuration configuration = ConfigurationFactory.getConfiguration(null, null);
Map<String, Object> connectionParams = new HashMap<String, Object>();
connectionParams.put(org.hornetq.core.remoting.impl.netty.TransportConstants.PORT_PROP_NAME, 5445);
connectionParams.put(org.hornetq.core.remoting.impl.netty.TransportConstants.HOST_PROP_NAME, "localhost");
TransportConfiguration transportConfiguration = new TransportConfiguration(NettyConnectorFactory.class.getName(), connectionParams);
ConnectionFactory connectionFactory = (ConnectionFactory) HornetQJMSClient.createConnectionFactoryWithoutHA(JMSFactoryType.CF, transportConfiguration);
// return connectionFactory.createConnection(login, password);
return connectionFactory.createConnection();
}
/**
* Orderly shutdown of all resources.
*/
public void shutdown() {
cleanupAfterError();
}
}
TestCode to run the code above
JMSMessageSenderTest jmsMessageSender = JMSMessageSenderTest.getInstance();
jmsMessageSender.sendRequest("www.example.com)");
jmsMessageSender.shutdown();
Gives the following error:
I'm closing a JMS connection you left open. Please make sure you close all JMS connections explicitly before letting them go out of scope!
The JMS connection you didn't close was created here:
java.lang.Exception
at org.hornetq.jms.client.HornetQConnection.<init>(HornetQConnection.java:152)
at org.hornetq.jms.client.HornetQConnectionFactory.createConnectionInternal(HornetQConnectionFactory.java:662)
at org.hornetq.jms.client.HornetQConnectionFactory.createConnection(HornetQConnectionFactory.java:121)
Solution:
1.) You also have to Keep a reference to the ConnectionFactory (see the answer from Clebert below)
private ConnectionFactory factory = null;
2.) AND this code contains a severe hidden bug (that is not so easy to spot):
I initialized the Connection in the Constructor as well as in the createConnectionSessionQueueProducer() method. It will therefore override the old value and (as it is a Ressource that needs to be closed) will lead to a stale connection that HornetQ then will close and will then throw the error.
Thanks very very much! Markus
HornetQ will close the connection factory when the connection factory is released.
You need to hold a reference for the connection factory.
I also have similar issues. But it is not supposed to crash . Your implementation looks good. But only thing is that you are not closing the JMS connection , which in turn is getting closed by the hornetQ gc.
One thing probably wrong with the code is that you are calling cleanupAfterError() only after an exception. You should call the same method also after you have posted a message and a JMS connection is lying idle . Since you are just opening a connection to post a message and then not closing that connection unless an exception happens , Hornetq GC is finding that object and removing it while throwing this error.
Let me know if I missed something