Here is my scenario:
Each minute one thread checks database looking for some items.
When items are found, they are passed to the another thread(consumer).
I use spring in my application, but it doesn't meant that I must use spring's classes, right?
So, now I'm totally lost in the woods of ScheduledExecutorService (java), ExecutorService (java), TaskExecutor (spring), TaskScheduler (spring), #Scheduled (spring).
Help me please to understand what is the right way to implement my scenario.
If I understand your question, you are using a Shared Database Pattern which, for for many reason is somewhat discouraged and used as a last resource.
If you want multiple application to communicate, and be decoupled, you should use Messaging ( eg: Spring Cloud Stream ).
Anyway, if you need to have a Shared Database, you may want what Listen / Notify provide.
From https://jdbc.postgresql.org/documentation/81/listennotify.html:
import java.sql.*;
public class NotificationTest {
public static void main(String args[]) throws Exception {
Class.forName("org.postgresql.Driver");
String url = "jdbc:postgresql://localhost:5432/test";
// Create two distinct connections, one for the notifier
// and another for the listener to show the communication
// works across connections although this example would
// work fine with just one connection.
Connection lConn = DriverManager.getConnection(url,"test","");
Connection nConn = DriverManager.getConnection(url,"test","");
// Create two threads, one to issue notifications and
// the other to receive them.
Listener listener = new Listener(lConn);
Notifier notifier = new Notifier(nConn);
listener.start();
notifier.start();
}
}
Listener
class Listener extends Thread {
private Connection conn;
private org.postgresql.PGConnection pgconn;
Listener(Connection conn) throws SQLException {
this.conn = conn;
this.pgconn = (org.postgresql.PGConnection)conn;
Statement stmt = conn.createStatement();
stmt.execute("LISTEN mymessage");
stmt.close();
}
public void run() {
while (true) {
try {
// issue a dummy query to contact the backend
// and receive any pending notifications.
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT 1");
rs.close();
stmt.close();
org.postgresql.PGNotification notifications[] = pgconn.getNotifications();
if (notifications != null) {
for (int i=0; i<notifications.length; i++) {
System.out.println("Got notification: " + notifications[i].getName());
}
}
// wait a while before checking again for new
// notifications
Thread.sleep(500);
} catch (SQLException sqle) {
sqle.printStackTrace();
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
}
Notifier
class Notifier extends Thread {
private Connection conn;
public Notifier(Connection conn) {
this.conn = conn;
}
public void run() {
while (true) {
try {
Statement stmt = conn.createStatement();
stmt.execute("NOTIFY mymessage");
stmt.close();
Thread.sleep(2000);
} catch (SQLException sqle) {
sqle.printStackTrace();
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
}
Instead of using ScheduledExecutorService you can user quartz scheduler that is used to schdule some jobs in specific intervals, in your case every minute. It can be easily integrated with spring.
Cron expressions are used to specify the time when to schedule.
You can write your logic that checks database looking for some items in a class that extends QuartzJobBean class.
See:
https://examples.javacodegeeks.com/enterprise-java/quartz/spring-quartz-scheduler-example/
Related
I'm working on an app that retrieves from and enters information to a database, using Spring JDBC template. On the service tier, I would like to set up some logic to catch an exception if the database goes down. However, I have no idea how to do this. I'm able to set up the methods to catch if they fail, but I'd like set up specific logic for the server going down.
As an option - you can create a sceduler which will check database connectivity.
Database connectivity could be checked executing a simple query or via Connection interface:
boolean isValid(int timeout) throws SQLException
Returns true if the connection has not been closed and is still valid.
The driver shall submit a query on the connection or use some other
mechanism that positively verifies the connection is still valid when
this method is called. The query submitted by the driver to validate
the connection shall be executed in the context of the current
transaction.
An example of checking database connectivity via Spring scheduler:
#Service
public class ConnectionListener {
private Connection connection;
#Autowired
private JdbcTemplate jdbcTemplate;
#PostConstruct
public void init() {
connection = jdbcTemplate.getDatasource().getConnection();
}
#Scheduled(fixedRate = 60000) // check every 60 sec
public void checkConnection() {
try {
connection.isValid(10);
} catch (SQLException e) { // Or just handle it here
throw new ConnectionTimeoutException(e);
}
}
}
You need some additional cnfiguration to handle exceptions thrown from Spring Scheduler:
#EnableScheduling
#Configuration
class SchedulingConfiguration implements SchedulingConfigurer {
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(...);
}
}
Sceduler also could be implemented with ExecutorService.
#Service
class ConnectionLisener {
private ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
private Connection connection;
#PostConstruct
public void init() {
connection = jdbcTemplate.getDatasource().getConnection();
checkConnection();
}
#PreDestroy
public void destroy() {
service.shutdown();
}
public void checkConnection() {
service.scheduleAtFixedRate(() -> {
try {
connection.isValid(10);
} catch (Exception e) {
// handle your exception
}
}, 60, 60, TimeUnit.SECONDS);
}
}
That's a general overview and just a couple of hints for doing further research.
Just one note that if a server is going down you need a disaster recovery, catching an exception will not help. That's a big infrastructure and architectural task, not the responsibility of single application.
In my application i am accessing data from another system Database through ip address communication.
So for that,if database system is offline at that time exception is occur during connection so in catch block again i am calling run() method ,when it come to online normal flow of application execution happen. but in this process i am getting "Exception in thread "Thread-1" java.lang.StackOverflowError" this exception how to solve this exception in my scenario.
This is my code:
MAIN CLASS :-
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new LsduJFrame().setVisible(true);
}
});
new Thread(new DisplayPlazaNameLocation()).start();// i am calling here
}
DisplayPlazaNameLocation:-
public class DisplayPlazaNameLocation implements Runnable{
static String plazaNameLocation;
static Connection con;
int i =0;
public void getPlazaNameLocation(){
try {
System.out.println("in getPlazaNameLocation()==============================================");
PreparedStatement pst=con.prepareStatement("SELECT DISTINCT Plaza_Loc FROM lsdu_live");
ResultSet rs = pst.executeQuery();
while(rs.next()){
plazaNameLocation = rs.getString("Plaza_Loc");
//System.out.println(plazaNameLocation);
}
String ar[] = plazaNameLocation.split(",");
jLabel199.setText("<html>"+ar[0]+"<br>"+ar[1]+"</html>");
rs.close();
pst.close();
con.close();
} catch (SQLException ex) {
run();
}
}
#Override
public void run(){
try {
Class.forName(DB_DRIVER_CLASS);
con= DriverManager.getConnection(DB_URL[0],DB_USERNAME,DB_PASSWORD);
if(con != null){
this.getPlazaNameLocation();}
} catch (ClassNotFoundException ex) {
run();
} catch (SQLException ex) {
run();
}
}
}
After executing some time I am getting this exception Exception in thread "Thread-1" java.lang.StackOverflowError
suppose when i resolve the Connection issue before getting this Exception the application working fine, but after getting this exception, no use of resolve after getting this exception at that time i need close the application and open again at that time it's working. How to resolve this issue with respect to my code ?
stackTrace() data:-
Exception in thread "Thread-1" java.lang.StackOverflowError
at java.lang.Exception.<init>(Exception.java:66)
at java.sql.SQLException.<init>(SQLException.java:70)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1073)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:987)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:982)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:927)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:435)
at com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:381)
at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:305)
at java.sql.DriverManager.getConnection(DriverManager.java:571)
at java.sql.DriverManager.getConnection(DriverManager.java:215)
at lsdu_application.DisplayPlazaNameLocation.run(DisplayPlazaNameLocation.java:47)
at lsdu_application.DisplayPlazaNameLocation.run(DisplayPlazaNameLocation.java:59)
at
In your run method, you call getPlazaNameLocation. If the SQL fails, you call run again. This causes an infinite "loop" (recursion) between those 2 methods, each call adding onto your call stack which eventually causes a StackOverflowError.
why do you call run in case of an SQLException? You should handle the error in the catch block, not restart your functionality. Remove that call and get some infos about your SQLException using e.printStackTrace().
never call run manually. That is supposed to be invoked using Thread.start().
EDIT: If you really want to re-try the database operation in case it fails, try this:
public void getPlazaNameLocation() throws SQLException {
//same code, but remove the try/catch blocks and
// especially the run()!
}
#Override
public void run(){
try {
Class.forName(DB_DRIVER_CLASS);
con = DriverManager.getConnection(DB_URL[0],DB_USERNAME,DB_PASSWORD);
} catch (ClassNotFoundException ex) {
e.printStackTrace();
//fail here since no driver was found
return;
}
boolean retry = true;
while(retry){
try {
getPlazaNameLocation();
retry = false;
} catch (SQLException ex) {
//if we arrive here, "retry" will still have a value of true
}
}
}
Please note i don't recommend spamming requests over a broken connection to the database like this code does.
When you inject a datasource in your application and get a connection by invoking getConnection() on it, are you supposed to close the connection?
Even though the datasource itself is container managed, the API indeed requires the programmer to close connections. This is different from a couple of other container managed resources (like the entity manager), where the container takes care of closing. Note that closing here in the majority of cases doesn't actually closes the connection here, but returns the connection to a connection pool.
As a rule of thumb, if you use a factory-ish resources to obtain one or more other resources from that can be closed, you have to close them. Otherwise the container does this.
Since Connection implements AutoCloseable, you can use a try-with-resources block for this:
#Stateless
public class MyBean {
#Resource(lookup = "java:/app/datasource")
private DataSource dataSource;
public void doStuff() {
try (Connection connection = dataSource.getConnection()) {
// Work with connection here
} catch (SQLException e) {
throw new SomeRuntimeException(e);
}
}
}
Of course, otherwise you'll exhaust your connection pool. It's best to do this in finally block:
#Resource(mappedName="jndi/yourDatasource")
DataSource ds;
..
Connection conn = null;
try {
conn = ds.getConnection();
//PERFORM QUERY, ETC..
}
catch(SQLException ex) {
//EXCEPTION HANDLING
}
finally {
try {
if(conn != null)
conn.close();
}
catch(SQLException ex) {..}
}
My requirement is quite similar to this one except that my long running IO operation is a database select.
One rather rather creative solution suggested in that thread, involved closing the IO stream in a separate thread. However, I don't really have a reference to the input stream or socket.
I can't even close the connection being used as I use spring-jdbc, which does not provide me access to the underlying connection being used. I believe JdbcTemplate.getDataSource().getConnection() will potentially return another connection from the data source.
Appreciate any help/suggestions i can get.
Use JdbcTemplate.execute(PreparedStatementCreator, PreparedStatementCallback)
In the PreparedStatementCreator, you get access to the Statement, which you can give to another thread. This other thread creates a timer and calls .cancel on the Statement if necessary.
As Istvan Mentioned, PreparedStatementCreator is the way to go.
Pasting my implementation in case its helpful ..
private final long timeout = 1000; //timeout in seconds
final SqlRowSet rowSet = jdbcTemplate.getJdbcOperations().query(new PreparedStatementCreator() {
#Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
if (timeout>0){
Timer timer = new Timer();
timer.schedule(new ConnectionInterrupter(con), timeout*1000);
}
return con.prepareStatement(queryString);
}
}, new ResultSetExtractor<SqlRowSet>() {
#Override
public SqlRowSet extractData(ResultSet rs) throws SQLException, DataAccessException {
CachedRowSet rowSet = newCachedRowSet();
rowSet.populate(rs);
return new ResultSetWrappingSqlRowSet(rowSet);
}
protected CachedRowSet newCachedRowSet() throws SQLException {
return new CachedRowSetImpl();
}
});
private class ConnectionInterrupter extends TimerTask {
private final Connection connection;
public ConnectionInterrupter(Connection connection) {
this.connection = connection;
}
#Override
public void run() {
try {
logger.warn("Connection timeout has occurred. Manually closing the connection ...");
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
We plan to implement connection pooling into our java application. We google and found a number of it such as BoneCp,DbPool,Apache,c3p0,DbCp and others. The problem now we are finding difficult to make a decision to which one to apply as some are outdated. Which method will be best solution?
public class cServer
{
class ConnectionHandler implements Runnable {
ConnectionHandler(Socket receivedSocketConn1) {
this.receivedSocketConn1=receivedSocketConn1;
}
public void run(){
createConnection();
while (read the socket values){
//number of queries to run in terms of select,insert and updates.
}
closeConnection();
}
void createConnection(){
try{
dbconn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test1?"+"user=user1&password=*******");
dbconn.setAutoCommit(false);
}
catch(Throwable ex){
ex.printStackTrace(System.out);
}
}
}
public void main()
{
try
{
final ServerSocket serverSocketConn = new ServerSocket(8000);
while (true){
try{
Socket socketConn1 = serverSocketConn.accept();
new Thread(new ConnectionHandler(socketConn1)).start();
}
catch(Exception e){
e.printStackTrace(System.out);
}
}
}
catch (Exception e){
e.printStackTrace(System.out);
}
}
}
First identify what you need from the connection pool and look for the libraries that provide that functionality.
Choose one for now, using popular opinion already found on the net or by asking specific questions here on SO.
Next, again on the basis of what you need from the pool, create an abstraction layer for the connection pooling functionality and implement using the chosen library.
That way you can change the underlying library if you are not happy with it, even during the course of development.