/*******************************************************************************
* Copyright (c) 2010 Eric Bodden.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eric Bodden - initial API and implementation
******************************************************************************/
package de.bodden.tamiflex.db.mysqlaccess;
import java.net.InetAddress;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Vector;
import de.bodden.tamiflex.db.datamodel.*;
/**
* Database Controller
*
* @author Ivaylo Petkov and Oleg Manov
*
*/
public class DBController{
/**
* Current run id.
*/
private static int runID;
/**
* Connection status.
*/
public static boolean connected=false;
/**
* Insert buffer.
*/
public static StringBuffer buffer=new StringBuffer();
/**
* Connects to the the database
* @param input
* if true adds new entry in the table runs
* @param url
* url address of the database
* @param username
* @param password
* @return
*/
public static boolean connect(boolean input,String url, String username,String password){
if(!connected)
connected=MySQLAccess.connect(url,username, password);
if(input&&connected){
String host="noname";
try{
host=InetAddress.getLocalHost().getHostName();
}catch (Exception e){
}
java.util.Date today = new java.util.Date();
Run currentRun=new Run(host,new java.sql.Timestamp(today.getTime()));
String statement=currentRun.generateIDSearchStatement();
String newstatement=currentRun.generateInsertStatement();
int i=-1;
try {
MySQLAccess.executeUpdate(newstatement);
i=handleResultSet(MySQLAccess.executeQuery(statement));
} catch (SQLException e) {
e.printStackTrace();
}
currentRun.setID(i);
runID=i;
}
return connected;
}
/**
* Closes the database
*/
public static void closeDB(){
MySQLAccess.close();
}
/**
* Checks if all tables exist.
* @return
*/
public static boolean checkTables(){
if(tableExists("calls")&&tableExists("calltypes")&&tableExists("classid")&&
tableExists("locations")&&tableExists("runs")&&tableExists("runtocall"))
return true;
else return false;
}
/**
* Checks if a table exists.
* @param tableName
* @return
*/
private static boolean tableExists(String tableName){
try {
String statement="show tables like "+'"'+tableName+'"';
ResultSet resultSet;
resultSet = MySQLAccess.executeQuery(statement);
if(resultSet.next())return true;
} catch (SQLException e) {
return false;
}
return false;
}
/**
* Tries to insert the entry in the data base if there is a connection.
* If there is no connection, buffers the entry for later insertion.
* @param entry
* The reflective call, that is going to be inserted in the database.
* @throws NumberFormatException
* @throws SQLException
*/
public static void insert(String entry) throws NumberFormatException, SQLException{
if(!connected){
buffer.append(entry+"\n");
}else
{
String[] entryX=entry.split(";");
insertInDB(entryX[1],entryX[0],entryX[2],Integer.valueOf(entryX[3]),"thread","className",-1);
}
}
/**
* Inserts the buffered entries in the database.
* @throws NumberFormatException
* @throws SQLException
*/
public static void sendBuffer() throws NumberFormatException, SQLException{
if(buffer.length()>1){
String[] entries =buffer.toString().split("\n");
for(String i:entries){
String[] entryX=i.split(";");
insertInDB(entryX[1],entryX[0],entryX[2],Integer.valueOf(entryX[3]),"thread","className",-1);
}
}
}
/**
* Inserts data in the database
* @param target target of the reflective call
* @param type type of the reflective call
* @param method the method, from which the call was made
* @param line the line number, from which the call was made
* @param thread the thread, from which the call was made
* @param classname the name of the class, from which the call was made
* @param version the hash code of the class
* @throws SQLException
*/
public static void insertInDB(String target,String type,String method,int line,String thread,String classname,long version)throws SQLException{
ClassID classID=new ClassID(classname,version);
String statement=classID.generateIDSearchStatement();
int i=handleResultSet(MySQLAccess.executeQuery(statement));
if(i==-1){
String newstatement=classID.generateInsertStatement();
MySQLAccess.executeUpdate(newstatement);
i=handleResultSet(MySQLAccess.executeQuery(statement));
}
classID.setID(i);
CallType callType=new CallType(type);
statement=callType.generateIDSearchStatement();
i=handleResultSet(MySQLAccess.executeQuery(statement));
if(i==-1){
String newstatement=callType.generateInsertStatement();
MySQLAccess.executeUpdate(newstatement);
i=handleResultSet(MySQLAccess.executeQuery(statement));
}
callType.setID(i);
Location location=new Location(method,line,callType.getID(),classID.getID());
statement=location.generateIDSearchStatement();
i=handleResultSet(MySQLAccess.executeQuery(statement));
if(i==-1){
String newstatement=location.generateInsertStatement();
MySQLAccess.executeUpdate(newstatement);
i=handleResultSet(MySQLAccess.executeQuery(statement));
}
location.setID(i);
Call call=new Call(target,thread,location.getID());
String newstatement=call.generateInsertStatement();
MySQLAccess.executeUpdate(newstatement);
statement=call.generateIDSearchStatement();
i=handleResultSet(MySQLAccess.executeQuery(statement));
call.setID(i);
RunToCall runToCall=new RunToCall(runID,call.getID());
statement=runToCall.generateInsertStatement();
MySQLAccess.executeUpdate(statement);
}
/**
* Gets the id from the result set.
* @param resultSet
* @return id
* @throws SQLException
*/
public static int handleResultSet(ResultSet resultSet) throws SQLException {
if(resultSet.next())
return resultSet.getInt(1);
else return -1;
}
/**
* Get the location from a class file.
* @param className
* the name of the class
* @param version
* hashcode of the class file
* @return list of locations
* @throws SQLException
*/
public static Vector<Location> findLocations(String className,long version) throws SQLException{
Vector<Location> locations=new Vector<Location>();
if(connected){
ClassID classID=new ClassID(className,version);
String statement=classID.generateIDSearchStatement();
int i=handleResultSet(MySQLAccess.executeQuery(statement));
if(i==-1){return locations;}
classID.setID(i);
SearchStatement st=new SearchStatement("locations", "idlocations,classid,method,line,calltypeid");
st.addClauseEqual("classid", classID.getID());
statement=st.generateStatement();
ResultSet resultSet=MySQLAccess.executeQuery(statement);
while(resultSet.next()){
Location location=new Location(resultSet);
locations.add(location);
}
}
return locations;
}
/**
* Finds all reflective calls from a specific location.
* @param locationID the unique id in the database of the location.
* @return Vector containing all the reflective calls from the location.
* @throws SQLException
*/
public static Vector<Call> findCalls(int locationID) throws SQLException{
Vector<Call> list=new Vector<Call>();
if(connected){
SearchStatement st=new SearchStatement("calls", "idcalls,locationid,target,thread");
st.addClauseEqual("locationid", locationID);
String statement=st.generateStatement();
ResultSet resultSet=MySQLAccess.executeQuery(statement);
while(resultSet.next()){
Call call=new Call(resultSet);
list.add(call);
}
}
return list;
}
/**
* Finds All Reflective Calls form the last run
* @param locationID the unique id in the database of the location.
* @return Vector containing the reflective calls from the location from the last run.
* @throws SQLException
*/
public static Vector<Call> findLastRunCalls(int locationID) throws SQLException{
Vector<Call> list= new Vector<Call>();
if(connected){
String statement="select idruns from runs where time=(select max(time) from runs)";
int i=handleResultSet(MySQLAccess.executeQuery(statement));
statement="select distinct idcalls,locationid,target,thread from calls,runs,runtocall where runtocall.runid=" +i+
" and runtocall.callid=calls.idcalls and calls.locationid="+locationID;
ResultSet resultSet=MySQLAccess.executeQuery(statement);
while(resultSet.next()){
Call call=new Call(resultSet);
list.add(call);
}
}
return list;
}
/**
* Searches for locations, from which the call was made.
* @param callTarget
* @return
* Vector with the locations.
* @throws SQLException
*/
public static Vector<Location> findLocationsFromCall(String callTarget) throws SQLException{
Vector<Location> list=new Vector<Location>();
if(connected){
String statement="select distinct idlocations,classid,method,line,calltypeid "+
"from locations,calls where locations.idlocations=calls.locationid and "+
"calls.target like "+'"'+callTarget+'"';
ResultSet resultSet=MySQLAccess.executeQuery(statement);
while(resultSet.next()){
Location location=new Location(resultSet);
list.add(location);
}
}
return list;
}
/**
* Gets the entry in the table ClassID with the specified idclassid.
* @param idclassid id of the class
* @return
* @throws SQLException
*/
public static ClassID getClassID(int idclassid) throws SQLException{
String statement="select * from classid where idclassid="+idclassid;
if(connected){
ResultSet resultSet=MySQLAccess.executeQuery(statement);
if(resultSet.next())
return new ClassID(resultSet);
else return null;
} else return null;
}
}