1 package net
.rizon
.acid
.sql
;
3 import java
.sql
.Connection
;
4 import java
.sql
.DriverManager
;
5 import java
.sql
.PreparedStatement
;
6 import java
.sql
.ResultSet
;
7 import java
.sql
.SQLException
;
8 import java
.util
.LinkedList
;
9 import net
.rizon
.acid
.conf
.Database
;
10 import net
.rizon
.acid
.core
.Acidictive
;
11 import org
.slf4j
.LoggerFactory
;
13 public class SQL
extends Thread
15 private static final org
.slf4j
.Logger log
= LoggerFactory
.getLogger(SQL
.class);
17 private PreparedStatement statement
;
18 private ResultSet result
;
19 private String url
, username
, password
;
20 private volatile Connection con
= null;
21 private volatile boolean shuttingDown
= false;
22 private long last_connect
= 0;
23 private volatile Object queryLock
= new Object();
24 private volatile LinkedList
<PreparedStatement
> pendingQueries
= new LinkedList
<PreparedStatement
>();
26 public SQL(final String url
, final String username
, final String password
) throws ClassNotFoundException
, SQLException
28 Class
.forName("com.mysql.jdbc.Driver");
31 this.username
= username
;
32 this.password
= password
;
38 public String
version()
43 return this.con
.getMetaData().getDatabaseProductName() + "-" + this.con
.getMetaData().getDatabaseProductVersion();
45 catch (SQLException ex
) { }
49 public void shutdown()
51 this.shuttingDown
= true;
53 log
.info("Flushing pending SQL queries...");
55 synchronized (this.queryLock
)
57 this.queryLock
.notify();
60 while (this.isAlive())
66 catch (InterruptedException ex
) { }
69 log
.info("All SQL queries successfully flushed");
71 try { this.con
.close(); }
72 catch (Exception ex
) { }
75 private void connect() throws SQLException
77 if (this.last_connect
> System
.currentTimeMillis() - 60 * 1000) // one minute
78 throw new SQLException("Reconnecting too fast");
79 this.last_connect
= System
.currentTimeMillis();
80 this.con
= DriverManager
.getConnection(this.url
, this.username
, this.password
);
82 log
.info("Successfully connected to " + this.version() + " using " + this.con
.getMetaData().getDriverName() + " (" + this.con
.getMetaData().getDriverVersion() + ")");
85 public PreparedStatement
prepare(final String statement
) throws SQLException
87 this.close(this.statement
, this.result
);
89 if (this.con
== null || this.con
.isClosed())
95 catch (SQLException ex
)
97 handleException("Unable to connect to SQL", ex
);
102 this.statement
= this.con
.prepareStatement(statement
);
103 return this.statement
;
106 public void executeThread(PreparedStatement statement
)
108 this.statement
= null;
111 synchronized (this.queryLock
)
113 this.pendingQueries
.addLast(statement
);
114 this.queryLock
.notify();
118 public int executeUpdateBlocking(PreparedStatement statement
) throws SQLException
120 int i
= statement
.executeUpdate();
121 log
.debug("Successfully executed " + statement
);
125 public ResultSet
executeQuery(PreparedStatement statement
) throws SQLException
127 this.result
= statement
.executeQuery();
129 log
.debug("Successfully executed " + statement
);
134 public PreparedStatement
persist()
136 PreparedStatement stmt
= this.statement
;
137 this.statement
= null;
142 public void close(PreparedStatement p
, ResultSet r
)
144 try { if (r
!= null) r
.close(); }
145 catch (Exception ex
) { }
147 try { if (p
!= null) p
.close(); }
148 catch (Exception ex
) { }
151 public int getPendingQueryCount()
153 synchronized (this.queryLock
)
155 return this.pendingQueries
.size();
159 public void setAutoCommit(boolean state
) throws SQLException
161 this.con
.setAutoCommit(state
);
167 long lastQuery
= System
.currentTimeMillis();
170 PreparedStatement q
= null;
172 synchronized (this.queryLock
)
174 if (this.pendingQueries
.isEmpty() == false)
175 q
= this.pendingQueries
.remove();
178 if (this.shuttingDown
)
183 this.queryLock
.wait(60 * 1000L);
185 catch (InterruptedException e
) { }
194 lastQuery
= System
.currentTimeMillis();
196 log
.debug("Successfully executed " + q
+ " in worker thread");
198 catch (SQLException ex
)
200 handleException("Unable to execute query in worker thread: " + q
, ex
);
206 if (System
.currentTimeMillis() - lastQuery
> 60 * 1000)
208 lastQuery
= System
.currentTimeMillis();
210 try (PreparedStatement stmt
= this.con
.prepareStatement("SELECT 1"))
214 catch (SQLException ex
)
216 ex
.printStackTrace();
222 private static long lastWarn
= 0;
223 public static void handleException(final String reason
, SQLException ex
)
225 long now
= System
.currentTimeMillis() / 1000L;
226 if (lastWarn
+ 60 < now
)
228 log
.error(reason
, ex
);
233 public static SQL
getConnection(final String name
)
237 for (Database d
: Acidictive
.conf
.database
)
239 if (!d
.name
.equals(name
))
242 return new SQL(d
.host
, d
.user
, d
.pass
);
245 catch (ClassNotFoundException ex
)
247 throw new RuntimeException(ex
.getMessage());
249 catch (SQLException e
)
251 throw new RuntimeException(e
.getMessage());