package thaw.plugins.transferLogs;
import java.sql.*;
import java.util.Observer;
import java.util.Observable;
import thaw.core.Logger;
import thaw.fcp.FCPTransferQuery;
import thaw.fcp.FCPClientPut;
import thaw.fcp.FreenetURIHelper;
import thaw.plugins.Hsqldb;
import thaw.plugins.TransferLogs;
public class Transfer implements Observer {
private Hsqldb db;
private FCPTransferQuery query;
private int id;
private Timestamp dateStart;
private Timestamp dateEnd;
private byte transferType;
private String key;
private String filename;
private long size;
private boolean isDup;
private boolean isSuccess;
private TransferTable table;
public Transfer(Hsqldb db,
FCPTransferQuery query,
TransferTable table) {
this.db = db;
this.query = query;
this.id = -1;
this.table = table;
if (!findOrInsertEntry(query))
findOrInsertEntry(query); /* because we need the id of the entry */
if (!query.isFinished()
&& query instanceof Observable) {
((Observable)query).addObserver(this);
}
}
/**
* If found, return true, if inserted returns false.
* If found, but some informations in the bdd is not correct, it's updated.
*/
private boolean findOrInsertEntry(FCPTransferQuery query) {
boolean entryFound;
entryFound = false;
Logger.info(this, "Searching corresponding query in the logs ...");
synchronized(db.dbLock) {
try {
PreparedStatement st;
String qKey = query.getFileKey();
if (qKey == null || !FreenetURIHelper.isAKey(qKey))
qKey = null;
/* by key first */
if (qKey != null) {
st = db.getConnection().prepareStatement("SELECT id, dateStart, dateEnd, "+
"transferType, "+
"key, filename, size, isDup, isSuccess "+
"FROM transferLogs "+
"WHERE key LIKE ? AND dateEnd IS NULL "+
"ORDER BY dateStart DESC "+
"LIMIT 1");
st.setString(1, FreenetURIHelper.getComparablePart(qKey)+"%");
ResultSet set = st.executeQuery();
if (set.next()) {
entryFound = true;
this.id = set.getInt("id");
this.dateStart = set.getTimestamp("dateStart");
this.dateEnd = set.getTimestamp("dateEnd");
this.transferType = set.getByte("transferType");
this.key = set.getString("key");
this.filename = set.getString("filename");
this.size = set.getLong("size");
this.isDup = set.getBoolean("isDup");
this.isSuccess = set.getBoolean("isSuccess");
}
st.close();
}
/* by filename else */
String filename = query.getFilename();
if (filename != null && entryFound == false) {
st = db.getConnection().prepareStatement("SELECT id, dateStart, dateEnd, "+
"transferType, "+
"key, filename, size, isDup, isSuccess "+
"FROM transferLogs "+
"WHERE filename = ? AND dateEnd IS NULL "+
"ORDER BY dateStart DESC "+
"LIMIT 1");
st.setString(1, filename);
ResultSet set = st.executeQuery();
if (set.next()) {
entryFound = true;
this.id = set.getInt("id");
this.dateStart = set.getTimestamp("dateStart");
this.dateEnd = set.getTimestamp("dateEnd");
this.transferType = set.getByte("transferType");
this.key = set.getString("key");
this.filename = set.getString("filename");
this.size = set.getLong("size");
this.isDup = set.getBoolean("isDup");
this.isSuccess = set.getBoolean("isSuccess");
}
st.close();
}
if (qKey == null && filename == null) { /* this query would be useless ?! */
Logger.warning(this, "Query with filename & key == null ? can do nothing with that");
return false;
}
if (entryFound) { /* we check if we must update the entry */
boolean mustUpdateKey = false;
boolean mustUpdateDateStart = false;
boolean mustUpdateDateEnd = false;
boolean mustUpdateSize = false;
if (this.key == null && qKey != null)
mustUpdateKey = true;
if (qKey == null && this.key != null)
qKey = this.key;
if (qKey != null && this.key != null
&& !qKey.equals(this.key)) /* the key has changed ? can it happen ? */
mustUpdateKey = true;
if((query.getStartupTime() != dateStart.getTime()) && (query.getStartupTime() != -1))
mustUpdateDateStart = true;
if (query.isFinished() && this.dateEnd == null)
mustUpdateDateEnd = true;
if (query.getFileSize() >= 0
&& query.getFileSize() >= size)
mustUpdateSize = true;
if (mustUpdateKey) {
updateKey(qKey);
}
if (mustUpdateDateStart) {
updateDateStart();
}
if (mustUpdateDateEnd) {
updateDateEnd(query.isSuccessful());
}
if (mustUpdateSize) {
updateSize(query.getFileSize());
}
} else { /* we insert a new one */
/* except if this query has already ended,
* because it means we weren't
* used for this job, so we won't move :P */
if (query.isFinished())
return false;
/* the main problem here is that when we insert a data
* hsqldb is not able to give us back the primary key generated
* so when this function will return false (no entry found => inserted),
* the constructor will call it again to find the primary key
*/
Timestamp now = TransferLogs.getNow();
st = db.getConnection().prepareStatement("INSERT INTO transferLogs "+
"(dateStart, dateEnd, transferType, "+
" key, filename, size, isDup, isSuccess) "+
"VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
st.setTimestamp(1, now);
if (query.isFinished())
st.setTimestamp(2, now);
else
st.setNull(2, Types.TIMESTAMP);
if (query instanceof FCPClientPut)
st.setByte(3, TransferLogs.TRANSFER_TYPE_INSERTION);
else
st.setByte(3, TransferLogs.TRANSFER_TYPE_DOWNLOAD);
if (qKey != null)
st.setString(4, qKey);
else
st.setNull(4, Types.VARCHAR);
st.setString(5, filename);
st.setLong(6, query.getFileSize());
st.setBoolean(7, TransferLogs.isDup(db, query.getFileKey()));
st.setBoolean(8, query.isFinished() && query.isSuccessful());
st.execute();
st.close();
}
} catch(SQLException e) {
Logger.error(this,
"Error while trying to find a specific "+
"entry in the log: "+e.toString());
}
}
if (entryFound)
Logger.info(this, "Entry found");
else
Logger.info(this, "Entry added");
return entryFound;
}
private void updateKey(String qKey) {
Logger.info(this, "Updating key in logs");
try {
PreparedStatement st = db.getConnection().prepareStatement("UPDATE transferLogs SET "+
"key = ? WHERE id = ?");
st.setString(1, qKey);
st.setInt(2, this.id);
st.execute();
st.close();
} catch(SQLException e) {
Logger.error(this, "Unable to update key in transfer logs because : "+e.toString());
}
}
private void updateSize(long size) {
Logger.info(this, "Updating file size in logs");
try {
PreparedStatement st = db.getConnection().prepareStatement("UPDATE transferLogs SET "+
"size = ? WHERE id = ?");
st.setLong(1, size);
st.setInt(2, this.id);
st.execute();
st.close();
} catch(SQLException e) {
Logger.error(this, "Unable to update size in transfer logs because : "+e.toString());
}
}
private void updateDateEnd(boolean successful) {
Logger.info(this, "Updating end date in logs");
try {
PreparedStatement st = db.getConnection().prepareStatement("UPDATE transferLogs SET "+
"dateEnd = ?, isSuccess = ?"+
"WHERE id = ?");
dateEnd = new Timestamp(query.getCompletionTime());
st.setTimestamp(1, dateEnd);
st.setBoolean(2, successful);
st.setInt(3, this.id);
st.execute();
st.close();
} catch(SQLException e) {
Logger.error(this, "Unable to update dateEnd in transfer logs because : "+e.toString());
}
}
private void updateDateStart() {
Logger.info(this, "Updating start date in logs");
try {
PreparedStatement st = db.getConnection().prepareStatement("UPDATE transferLogs SET "+
"dateStart = ?"+
"WHERE id = ?");
dateStart = new Timestamp(query.getStartupTime());
st.setTimestamp(1, dateStart);
st.setInt(2, this.id);
st.execute();
st.close();
} catch(SQLException e) {
Logger.error(this, "Unable to update dateEnd in transfer logs because : "+e.toString());
}
}
public Transfer(Hsqldb db,
int id,
Timestamp dateStart, Timestamp dateEnd,
byte transferType, String key, String filename, long size,
boolean isDup, boolean isSuccess) {
this.db = db;
this.id = id;
this.dateStart = dateStart;
this.dateEnd = dateEnd;
this.transferType = transferType;
this.key = key;
this.filename = filename;
this.size = size;
this.isDup = isDup;
this.isSuccess = isSuccess;
}
public Timestamp getDateStart() {
return dateStart;
}
public Timestamp getDateEnd() {
return dateEnd;
}
public byte getTransferTypeByte() {
return transferType;
}
public String getTransferTypeStr() {
return TransferLogs.TRANSFER_TYPE_NAMES[transferType];
}
public String getKey() {
return key;
}
public String getFilename() {
return filename;
}
public long getSize() {
return size;
}
public boolean isDup() {
return isDup;
}
public boolean isSuccess() {
return isSuccess;
}
/**
* @return a value in byte / s
*/
public long getAverageSpeed() {
if (!isSuccess() || getDateEnd() == null || getSize() <= 0)
return -1;
long diff = (getDateEnd().getTime() - getDateStart().getTime())/1000;
if (diff <= 0)
return -1;
return getSize() / diff;
}
protected FCPTransferQuery getQuery() {
return query;
}
protected int getId() {
return id;
}
public void delete() {
Logger.info(this, "Deleting transfer logs entry ...");
try {
synchronized(db.dbLock) {
PreparedStatement st;
st = db.getConnection().prepareStatement("DELETE FROM transferLogs WHERE id = ?");
st.setInt(1, id);
st.execute();
st.close();
}
} catch(SQLException e) {
Logger.error(this, "Can't delete transfer because: "+e.toString());
}
}
public boolean equals(Object o) {
if (o == null || (!(o instanceof Transfer)))
return false;
return (id == ((Transfer)o).getId()
|| query == ((Transfer)o).getQuery());
}
public void update(Observable o,
Object param) {
boolean hasChanged;
if (!(o instanceof FCPTransferQuery) || id < 0) {
return;
}
hasChanged = false;
final FCPTransferQuery query = (FCPTransferQuery)o;
if (query.isFinished()) {
o.deleteObserver(this);
updateDateEnd(query.isSuccessful());
hasChanged = true;
}
if (query.getFileKey() != null &&
(this.key == null
|| !this.key.equals(query.getFileKey()))) {
updateKey(query.getFileKey());
key = query.getFileKey();
hasChanged = true;
}
if (query.getFileSize() != size) {
updateSize(query.getFileSize());
size = query.getFileSize();
hasChanged = true;
}
if (hasChanged && table != null)
table.refresh();
}
}