/* This file is part of Wattzap Community Edition.
*
* Wattzap Community Edtion is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Wattzap Community Edition is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Wattzap. If not, see <http://www.gnu.org/licenses/>.
*/
package com.wattzap.model;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.xml.bind.DatatypeConverter;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import com.wattzap.model.dto.WorkoutData;
/**
* Handles interactions with the database
*
* (c) 2013-2014 David George / Wattzap.com
*
* @author David George
* @date 11 June 2013
*/
public class DataStore {
private String framework = "embedded";
private String driver = "org.apache.derby.jdbc.EmbeddedDriver";
private String protocol = "jdbc:derby:";
Connection conn = null;
private static Cipher cipher = null; // in javax.crypto
SecretKey secretKey = null;
private Logger logger = LogManager.getLogger("DataStore");
public DataStore(String wd, String key) {
SecretKeyFactory keyGenerator;
try {
keyGenerator = SecretKeyFactory.getInstance("DES");
// keyGenerator.init(168);
DESKeySpec keySpec = new DESKeySpec(key.getBytes("UTF8"));
secretKey = keyGenerator.generateSecret(keySpec);
cipher = Cipher.getInstance("DES");
} catch (Exception e) {
logger.error(e.getLocalizedMessage());
}
try {
Class.forName(driver).newInstance();
} catch (Exception e) {
logger.error(e.getLocalizedMessage());
}
Statement s1 = null;
Statement s2 = null;
try {
Properties props = new Properties();
String dbName = wd + "/prefs"; // the name of the database
try {
conn = DriverManager.getConnection(protocol + dbName + "",
props);
logger.info("Connected to " + dbName);
} catch (SQLException e) {
logger.info("Creating Data Store");
conn = DriverManager.getConnection(protocol + dbName
+ ";create=true", props);
// Setup the database
s1 = conn.createStatement();
s1.execute("create table props(username varchar(64), k varchar(128), v varchar(128), primary key (username, k))");
}
String dbVersion = getProp("", "dbVersion");
if (dbVersion == null) {
s2 = conn.createStatement();
s2.execute(WorkoutData.dbTable12());
insertProp("", "dbVersion", "1.3");
} else if (dbVersion.equals("1.2")){
s2 = conn.createStatement();
// s2.execute("DROP TABLE workouts");
//System.out.println("adding column");
s2.execute("ALTER TABLE workouts ADD COLUMN source INTEGER DEFAULT 1");
insertProp("", "dbVersion", "1.3");
}
} catch (SQLException sqle) {
sqle.printStackTrace();
printSQLException(sqle);
} finally {
try {
if (s1 != null) {
s1.close();
}
} catch (SQLException sqle) {
logger.error(sqle.getLocalizedMessage());
}
try {
if (s2 != null) {
s2.close();
}
} catch (SQLException sqle) {
logger.error(sqle.getLocalizedMessage());
}
}
}
/**
* Save workout values to database
*
* @param user
* @param data
*/
public void saveWorkOut(String user, WorkoutData data) {
PreparedStatement psInsert = null;
try {
psInsert = conn.prepareStatement(WorkoutData.insert());
psInsert.setString(1, user);
psInsert.setString(2, data.getTcxFile());
// Power
psInsert.setInt(3, data.getFiveSecondPwr());
psInsert.setInt(4, data.getOneMinutePwr());
psInsert.setInt(5, data.getFiveMinutePwr());
psInsert.setInt(6, data.getTwentyMinutePwr());
psInsert.setInt(7, data.getQuadraticPower());
psInsert.setInt(8, data.getTotalPower());
psInsert.setInt(9, data.getMaxPower());
psInsert.setInt(10, data.getAvePower());
psInsert.setInt(11, data.getFtp());
psInsert.setInt(12, data.getMaxHR());
psInsert.setInt(13, data.getAveHR());
psInsert.setInt(14, data.getMinHR());
psInsert.setInt(15, data.getFtHR());
psInsert.setInt(16, data.getMaxCadence());
psInsert.setInt(17, data.getAveCadence());
psInsert.setDouble(18, data.getDistanceMeters());
psInsert.setDouble(19, data.getWeight());
psInsert.setTime(20, new java.sql.Time(data.getTime()));
psInsert.setDate(21, new java.sql.Date(data.getDate()));
psInsert.setString(22, data.getDescription());
psInsert.setInt(23, data.getSource()); // Wattzap
/*int i =*/ psInsert.executeUpdate();
conn.commit();
} catch (SQLException e) {
logger.error(e.getLocalizedMessage());
} finally {
try {
if (psInsert != null) {
psInsert.close();
}
} catch (SQLException e) {
logger.error(e.getLocalizedMessage());
}
}
}
/**
* Updates any calculated values
*/
public void updateWorkout(String user, WorkoutData data) {
PreparedStatement psUpdate = null;
try {
psUpdate = conn.prepareStatement(WorkoutData.updateAnalysis());
// Power
psUpdate.setInt(1, data.getFiveSecondPwr());
psUpdate.setInt(2, data.getOneMinutePwr());
psUpdate.setInt(3, data.getFiveMinutePwr());
psUpdate.setInt(4, data.getTwentyMinutePwr());
psUpdate.setInt(5, data.getQuadraticPower());
psUpdate.setInt(6, data.getTotalPower());
psUpdate.setInt(7, data.getMaxPower());
psUpdate.setInt(8, data.getAvePower());
psUpdate.setInt(9, data.getMaxHR());
psUpdate.setInt(10, data.getAveHR());
psUpdate.setInt(11, data.getMinHR());
psUpdate.setInt(12, data.getFtHR());
psUpdate.setInt(13, data.getMaxCadence());
psUpdate.setInt(14, data.getAveCadence());
psUpdate.setDouble(15, data.getDistanceMeters());
psUpdate.setString(16, user);
psUpdate.setString(17, data.getTcxFile());
psUpdate.executeUpdate();
conn.commit();
} catch (SQLException e) {
logger.error(e.getLocalizedMessage());
} finally {
try {
if (psUpdate != null) {
psUpdate.close();
}
} catch (SQLException e) {
logger.error(e.getLocalizedMessage());
}
}
}
public WorkoutData getWorkout(String user, String name) {
PreparedStatement s = null;
ResultSet rs = null;
WorkoutData data = null;
try {
s = conn.prepareStatement(WorkoutData.selectWorkout());
s.setString(1, user);
s.setString(2, name);
rs = s.executeQuery();
while (rs.next()) {
data = new WorkoutData();
data.setTcxFile(rs.getString(2));
data.setFiveSecondPwr(rs.getInt(3));
data.setOneMinutePwr(rs.getInt(4));
data.setFiveMinutePwr(rs.getInt(5));
data.setTwentyMinutePwr(rs.getInt(6));
data.setQuadraticPower(rs.getInt(7));
data.setTotalPower(rs.getInt(8));
data.setMaxPower(rs.getInt(9));
data.setAvePower(rs.getInt(10));
data.setFtp(rs.getInt(11));
data.setMaxHR(rs.getInt(12));
data.setAveHR(rs.getInt(13));
data.setMinHR(rs.getInt(14));
data.setFtHR(rs.getInt(15));
data.setMaxCadence(rs.getInt(16));
data.setAveCadence(rs.getInt(17));
data.setDistanceMeters(rs.getDouble(18));
data.setWeight(rs.getDouble(19));
data.setTime(rs.getTime(20).getTime());
data.setDate(rs.getDate(21).getTime());
data.setDescription(rs.getString(22));
data.setSource(rs.getInt(23));
}
} catch (SQLException e) {
logger.error(e.getLocalizedMessage());
} finally {
try {
if (s != null) {
s.close();
}
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return data;
}
public WorkoutData deleteWorkout(String user, String name) {
PreparedStatement s = null;
ResultSet rs = null;
WorkoutData data = null;
try {
s = conn.prepareStatement(WorkoutData.delete());
s.setString(1, user);
s.setString(2, name);
s.executeUpdate();
} catch (SQLException e) {
logger.error(e.getLocalizedMessage());
} finally {
try {
if (s != null) {
s.close();
}
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return data;
}
public List<WorkoutData> listWorkouts(String user) {
PreparedStatement s = null;
ResultSet rs = null;
List<WorkoutData> workouts = null;
try {
s = conn.prepareStatement(WorkoutData.select());
s.setString(1, user);
// s.setString(2, k);
rs = s.executeQuery();
// username, filename, fivesecp, oneminp, fiveminp, twentyminp, qp ,
// totalp,
// maxp, avep, ftp, maxhr, avehr, minhr, fthr, maxcad, avecad,
// distance, weight, ridetime,
// ridedate, primary key (username, filename))";
workouts = new ArrayList<WorkoutData>();
while (rs.next()) {
WorkoutData data = new WorkoutData();
data.setTcxFile(rs.getString(2));
data.setFiveSecondPwr(rs.getInt(3));
data.setOneMinutePwr(rs.getInt(4));
data.setFiveMinutePwr(rs.getInt(5));
data.setTwentyMinutePwr(rs.getInt(6));
data.setQuadraticPower(rs.getInt(7));
data.setTotalPower(rs.getInt(8));
data.setMaxPower(rs.getInt(9));
data.setAvePower(rs.getInt(10));
data.setFtp(rs.getInt(11));
data.setMaxHR(rs.getInt(12));
data.setAveHR(rs.getInt(13));
data.setMinHR(rs.getInt(14));
data.setFtHR(rs.getInt(15));
data.setMaxCadence(rs.getInt(16));
data.setAveCadence(rs.getInt(17));
data.setDistanceMeters(rs.getDouble(18));
data.setWeight(rs.getDouble(19));
data.setTime(rs.getTime(20).getTime());
data.setDate(rs.getDate(21).getTime());
data.setDescription(rs.getString(22));
data.setSource(rs.getInt(23));
workouts.add(data);
}
} catch (SQLException e) {
logger.error(e.getLocalizedMessage());
} finally {
try {
if (s != null) {
s.close();
}
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return workouts;
}
/**
* Encrypts values before writing to database using DES. If there is an
* exception nothing is written.
*
* @param user
* @param k
* @param v
*/
public void insertPropCrypt(String user, String k, String v) {
byte[] clearTextBytes;
try {
clearTextBytes = v.getBytes("UTF8");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] cipherBytes = cipher.doFinal(clearTextBytes);
v = toHexString(cipherBytes);
insertProp(user, k, v);
} catch (Exception e) {
logger.error(e.getLocalizedMessage());
}
}
public void insertProp(String user, String k, String v) {
PreparedStatement psInsert = null;
try {
int count = this.getRows(user, k);
if (count == 0) {
psInsert = conn
.prepareStatement("INSERT INTO props (v, username, k) values( ?, ?, ?)");
} else {
psInsert = conn
.prepareStatement("UPDATE props SET v=? WHERE username=? and k=?");
}
psInsert.setString(1, v);
psInsert.setString(2, user);
psInsert.setString(3, k);
/*int i =*/ psInsert.executeUpdate();
conn.commit();
} catch (SQLException e) {
logger.error(e.getLocalizedMessage());
} finally {
try {
if (psInsert != null) {
psInsert.close();
}
} catch (SQLException e) {
logger.error(e.getLocalizedMessage());
}
}
}
public String getPropCrypt(String user, String k) {
String v = getProp(user, k);
try {
byte[] cipherBytes = toByteArray(v);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decryptedBytes = cipher.doFinal(cipherBytes);
v = new String(decryptedBytes, "UTF8");
} catch (Exception e) {
logger.error(e.getLocalizedMessage());
}
return v;
}
public String getProp(String user, String k) {
PreparedStatement s = null;
ResultSet rs = null;
String value = null;
try {
s = conn.prepareStatement("SELECT v FROM props WHERE username = ? and k = ?");
s.setString(1, user);
s.setString(2, k);
rs = s.executeQuery();
if (rs.next()) {
value = rs.getString(1);
}
} catch (SQLException e) {
logger.error(e.getLocalizedMessage());
} finally {
try {
if (s != null) {
s.close();
}
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return value;
}
public int getRows(String user, String k) {
PreparedStatement s = null;
ResultSet rs = null;
int count = -1;
try {
s = conn.prepareStatement(" SELECT COUNT(*) FROM props WHERE username = ? and k = ?");
// s =
// conn.prepareStatement(" SELECT v FROM props WHERE username = ? and k = ?");
s.setString(1, user);
s.setString(2, k);
rs = s.executeQuery();
if (rs.next()) {
count = rs.getInt(1);
// String v = rs.getString(1);
// count = 1;
}
} catch (SQLException e) {
logger.error(e.getLocalizedMessage());
} finally {
try {
if (s != null) {
s.close();
}
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return count;
}
public void close() {
try {
if (conn != null) {
conn.close();
conn = null;
}
} catch (SQLException sqle) {
// printSQLException(sqle);
logger.error(sqle.getLocalizedMessage());
}
if (framework.equals("embedded")) {
try {
// the shutdown=true attribute shuts down Derby
DriverManager.getConnection("jdbc:derby:;shutdown=true");
// To shut down a specific database only, but keep the
// engine running (for example for connecting to other
// databases), specify a database in the connection URL:
// DriverManager.getConnection("jdbc:derby:" + dbName +
// ";shutdown=true");
} catch (SQLException se) {
if (((se.getErrorCode() == 50000) && ("XJ015".equals(se
.getSQLState())))) {
// we got the expected exception
logger.info("Data Store shut down normally");
// Note that for single database shutdown, the expected
// SQL state is "08006", and the error code is 45000.
} else {
// if the error code or SQLState is different, we have
// an unexpected exception (shutdown failed)
logger.error("Error shutting down Data Store "
+ se.getLocalizedMessage());
}
}
}
}
/**
* Prints details of an SQLException chain to <code>System.err</code>.
* Details included are SQL State, Error code, Exception message.
*
* @param e
* the SQLException from which to print details.
*/
public static void printSQLException(SQLException e) {
// Unwraps the entire exception chain to unveil the real cause of the
// Exception.
while (e != null) {
System.err.println("\n----- SQLException -----");
System.err.println(" SQL State: " + e.getSQLState());
System.err.println(" Error Code: " + e.getErrorCode());
System.err.println(" Message: " + e.getMessage());
// for stack traces, refer to derby.log or uncomment this:
// e.printStackTrace(System.err);
e = e.getNextException();
}
}
private static String toHexString(byte[] array) {
return DatatypeConverter.printHexBinary(array);
}
private static byte[] toByteArray(String s) {
return DatatypeConverter.parseHexBinary(s);
}
}