package com.ubx1.pdpscanner.server.services;
import java.sql.ResultSet;
import java.util.ArrayList;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.BasicAuthenticationManager;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.wc.ISVNInfoHandler;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNInfo;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNWCClient;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.ubx1.pdpscanner.client.FindbugsCategory;
import com.ubx1.pdpscanner.client.services.ProjectService;
import com.ubx1.pdpscanner.server.database.BCrypt;
import com.ubx1.pdpscanner.server.database.MySQLConnection;
import com.ubx1.pdpscanner.shared.FindbugsBugInstance;
import com.ubx1.pdpscanner.shared.Project;
import com.ubx1.pdpscanner.shared.UserRole;
import com.ubx1.pdpscanner.shared.exceptions.BadProjectNameException;
import com.ubx1.pdpscanner.shared.exceptions.BadReposUrlException;
import com.ubx1.pdpscanner.shared.exceptions.InvalidSessionException;
import com.ubx1.pdpscanner.shared.exceptions.RepositoryException;
import com.ubx1.pdpscanner.shared.validation.Validator;
/**
* The project service implementation
*
* @author wbraik
*
*/
public class ProjectServiceImpl extends RemoteServiceServlet implements
ProjectService {
private static final long serialVersionUID = 1L;
/**
* Obtain the list of all projects from the database
*
* @return an array containing all projects, each of them encapsulated in a
* Project object
*/
@Override
public Project[] fetchProjects() throws Exception {
MySQLConnection conn = new MySQLConnection();
conn.connect();
ResultSet rs1 = conn
.SQLSelect("SELECT * FROM projects JOIN users_in_projects ON projects.id = users_in_projects.project "
+ "JOIN users ON "
+ "users_in_projects.user = users.id "
+ "WHERE users_in_projects.project = projects.id"
+ " AND users_in_projects.role = "
+ UserRole.OWNER.code());
ArrayList<Project> projectsList = new ArrayList<>();
while (rs1.next()) {
Project p = new Project(rs1.getInt("id"),
rs1.getString("users.name"),
rs1.getString("projects.name"), rs1.getString("repos_url"),
rs1.getString("repos_user"),
rs1.getString("repos_password"), rs1.getDate(
"creation_date").toString()
+ " " + rs1.getTime("creation_date").toString());
// Get project's loc stat for all versions
ResultSet rs2 = conn
.SQLSelect("SELECT * FROM versions JOIN general_stats "
+ "ON versions.project = " + rs1.getInt("id")
+ " AND general_stats.version = versions.id "
+ "ORDER BY versions.version");
while (rs2.next()) {
int nloc = rs2.getInt("nloc");
if (!rs2.wasNull())
p.addNloc(nloc);
else
p.addNloc(-1);
}
projectsList.add(p);
}
conn.disconnect();
return projectsList.toArray(new Project[0]);
}
/**
* Add a new project to the database
*
* @param name
* the name of the new project
* @param svnUrl
* the repository location for the new project
* @param svnUser
* username, used for authentication on the repository's server
* @param svnPassword
* password, used for authentication on the repository's server
* @param creationDate
* the creation date of the new project
*/
@Override
public String addProject(String name, String svnUrl, String svnUser,
String svnPassword, String creationDate) throws Exception {
if (getThreadLocalRequest().isRequestedSessionIdValid()) {
int ownerId = Integer.parseInt((String) getThreadLocalRequest()
.getSession().getAttribute("id"));
/*
* Validate project
*/
Validator.validateProject(name, svnUrl, svnUser, svnPassword);
try {
SVNURL.parseURIDecoded(svnUrl);
} catch (SVNException e) {
throw new BadReposUrlException(svnUrl,
"Invalid repository url format");
}
try {
checkProjectRepository(svnUrl, svnUser, svnPassword);
MySQLConnection conn = new MySQLConnection();
conn.connect();
ResultSet rs = conn.SQLSelect("SELECT * FROM projects");
// Check project name unicity
while (rs.next()) {
String s = rs.getString("name");
if (s.equals(name)) {
throw new BadProjectNameException(name,
"Project name already exists");
}
}
// Prepare insert sql statement
name = "'" + name + "'";
svnUrl = "'" + svnUrl + "'";
if (!svnUser.isEmpty())
svnUser = "'" + svnUser + "'";
else
svnUser = "NULL";
if (!svnPassword.isEmpty()) {
svnPassword = "'"
+ BCrypt.hashpw(svnPassword, BCrypt.gensalt())
+ "'";
} else
svnPassword = "NULL";
creationDate = "'" + creationDate + "'";
int projectId = conn
.SQLUpdate("INSERT INTO projects (name,repos_url,repos_user,repos_password,creation_date)"
+ " VALUES ("
+ name
+ ","
+ svnUrl
+ ","
+ svnUser
+ ","
+ svnPassword
+ ","
+ creationDate + ")");
conn.SQLUpdate("INSERT INTO users_in_projects (user,project,role)"
+ " VALUES ("
+ ownerId
+ ","
+ projectId
+ ","
+ UserRole.OWNER.code() + ")");
conn.disconnect();
} catch (SVNException e) {
throw new RepositoryException(svnUrl, svnUser, svnPassword,
"Project repository couldn't be reached (svn info)");
}
return null;
} else {
throw new InvalidSessionException();
}
}
/**
* Delete a project from the databse
*
* @param id
* the id of the project to delete
* @return a null String object
*/
public String deleteProject(int id) throws Exception {
MySQLConnection conn = new MySQLConnection();
conn.connect();
// First delete the associated stats
conn.SQLUpdate("DELETE FROM general_stats "
+ "USING versions,general_stats " + "WHERE versions.project = "
+ id + " AND general_stats.version = versions.id");
conn.SQLUpdate("DELETE FROM test_stats " + "USING versions,test_stats "
+ "WHERE versions.project = " + id
+ " AND test_stats.version = versions.id");
conn.SQLUpdate("DELETE FROM findbugs_stats "
+ "USING versions,findbugs_stats "
+ "WHERE versions.project = " + id
+ " AND findbugs_stats.version = versions.id");
// Then delete the project's users
conn.SQLUpdate("DELETE FROM users_in_projects "
+ "WHERE users_in_projects.project = " + id);
// Then delete the project's versions
conn.SQLUpdate("DELETE FROM versions WHERE project = " + id);
// Finally delete the project itself
conn.SQLUpdate("DELETE FROM projects WHERE id = " + id);
conn.disconnect();
return null;
}
/**
* Obtain the statistics for a particular project from the database
*
* @param p
* the project for which to retrieve the stats
* @return the modified Project object, which now contains the stats
*/
@Override
public Project fetchStats(Project p) throws Exception {
MySQLConnection conn = new MySQLConnection();
conn.connect();
// Get project's stats for all versions
ResultSet rs1 = conn
.SQLSelect("SELECT * FROM versions JOIN general_stats "
+ "ON general_stats.version = versions.id "
+ "WHERE versions.project = " + p.getId()
+ " ORDER BY versions.version");
ResultSet rs2 = conn
.SQLSelect("SELECT * FROM versions JOIN test_stats "
+ "ON test_stats.version = versions.id "
+ "WHERE versions.project = " + p.getId()
+ " ORDER BY versions.version");
ResultSet rs3 = conn
.SQLSelect("SELECT * FROM versions JOIN findbugs_stats "
+ "ON findbugs_stats.version = versions.id "
+ "WHERE versions.project = " + p.getId()
+ " ORDER BY versions.version");
// fetchProjects() already fetched the nlocs which now might be from an
// old version, so clear them then fetch them again
p.clearNlocs();
// Get project's general stats
while (rs1.next()) {
int nloc = rs1.getInt("nloc");
if (!rs1.wasNull())
p.addNloc(nloc);
else
p.addNloc(-1);
int npkg = rs1.getInt("npkg");
if (!rs1.wasNull())
p.addNpkg(npkg);
else
p.addNpkg(-1);
int nfile = rs1.getInt("nfile");
if (!rs1.wasNull())
p.addNfile(nfile);
else
p.addNfile(-1);
int nfunc = rs1.getInt("nfunc");
if (!rs1.wasNull())
p.addNfunc(nfunc);
else
p.addNfunc(-1);
int ncl = rs1.getInt("ncl");
if (!rs1.wasNull())
p.addNcl(ncl);
else
p.addNcl(-1);
int ncom = rs1.getInt("ncom");
if (!rs1.wasNull())
p.addNcom(ncom);
else
p.addNcom(-1);
int ndupl = rs1.getInt("ndupl");
if (!rs1.wasNull())
p.addNdupl(ndupl);
else
p.addNdupl(-1);
}
// Get project's test stats
while (rs2.next()) {
int ncovl = rs2.getInt("ncovl");
if (!rs2.wasNull())
p.addNcovl(ncovl);
else
p.addNcovl(-1);
}
int currentVersion = 0;
ArrayList<FindbugsBugInstance> bps = null;
ArrayList<FindbugsBugInstance> ps = null;
// Get project's Findbugs bug instances
while (rs3.next()) {
int version = rs3.getInt("version");
// We got to the next version
if (version > currentVersion) {
currentVersion++;
if (bps != null) {
p.addBp(bps);
p.addP(ps);
}
bps = new ArrayList<FindbugsBugInstance>();
ps = new ArrayList<FindbugsBugInstance>();
String category = rs3.getString("category");
// If category is null, Findbugs bug instances are unavailable
// for this version
if (rs3.wasNull()) {
p.addBp(null);
p.addP(null);
} else {
String abbrev = rs3.getString("abbrev");
String type = rs3.getString("type");
String file = rs3.getString("file");
if (rs3.wasNull())
file = null;
int line = rs3.getInt("line");
if (rs3.wasNull())
line = -1;
FindbugsBugInstance fb = new FindbugsBugInstance(category,
abbrev, type, file, line);
if (category.equals(FindbugsCategory.BAD_PRACTICE
.toString())) {
bps.add(fb);
} else {
ps.add(fb);
}
}
}
// We are reading the same version as in the previous loop, ignore
// anything but Findbugs
else {
// Get project's Findbugs bug instances
String category = rs3.getString("category");
String abbrev = rs3.getString("abbrev");
String type = rs3.getString("type");
String file = rs3.getString("file");
if (rs3.wasNull())
file = null;
int line = rs3.getInt("line");
if (rs3.wasNull())
line = -1;
FindbugsBugInstance fb = new FindbugsBugInstance(category,
abbrev, type, file, line);
if (category.equals(FindbugsCategory.BAD_PRACTICE.toString())) {
bps.add(fb);
} else {
ps.add(fb);
}
}
}
p.addBp(bps);
p.addP(ps);
conn.disconnect();
return p;
}
/**
* Check access to the project repository to make sure the analyszer will be
* able to checkout the project later
*
* @param svnUrl
* the URL from which to checkout the project
* @param svnUser
* used for authentication on the repository's server
* @param svnPassword
* used for authentication on the repository's server
*/
private void checkProjectRepository(String svnUrl, String svnUser,
String svnPassword) throws SVNException {
SVNClientManager svnClientManager = SVNClientManager.newInstance();
if (!svnUser.isEmpty() && !svnPassword.isEmpty()) {
ISVNAuthenticationManager authManager = new BasicAuthenticationManager(
svnUser, svnPassword);
svnClientManager.setAuthenticationManager(authManager);
}
SVNWCClient wcClient = svnClientManager.getWCClient();
wcClient.setIgnoreExternals(false);
wcClient.doInfo(SVNURL.parseURIDecoded(svnUrl), SVNRevision.HEAD,
SVNRevision.HEAD, SVNDepth.IMMEDIATES, new ISVNInfoHandler() {
@Override
public void handleInfo(SVNInfo info) throws SVNException {
System.out.println("svn info : " + info.getPath());
}
});
}
}