package com.ikokoon.serenity.hudson;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.apache.log4j.Logger;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import com.ikokoon.serenity.IConstants;
import com.ikokoon.serenity.hudson.modeller.HighchartsModeller;
import com.ikokoon.serenity.hudson.modeller.IModeller;
import com.ikokoon.serenity.model.Class;
import com.ikokoon.serenity.model.Composite;
import com.ikokoon.serenity.model.Method;
import com.ikokoon.serenity.model.Package;
import com.ikokoon.serenity.model.Project;
import com.ikokoon.serenity.persistence.DataBaseOdb;
import com.ikokoon.serenity.persistence.IDataBase;
import com.ikokoon.toolkit.Toolkit;
/**
* This is the result that will be used to render the results on the front end.
*
* @author Michael Couck
* @since 12.08.09
* @version 01.00
*/
public class SerenityResult implements ISerenityResult {
private Logger logger = Logger.getLogger(SerenityResult.class);
/** Owner is necessary to render the sidepanel.jelly */
private AbstractBuild<?, ?> abstractBuild;
/** The project for the result. */
private Project<?, ?> project;
/** The currently selected composite. */
private Composite<?, ?> composite;
/**
* Constructor takes the real action that generated the build for the project.
*
* @param abstractBuild
* the build action that generated the build for the project
*/
public SerenityResult(AbstractBuild<?, ?> abstractBuild) {
logger.debug("SerenityResult");
this.abstractBuild = abstractBuild;
}
/**
* This method is called from the front end. The result from the call will result in some piece of data being extracted from the database. For
* example if the user clicks on a package the name of the package will be used to get that package from the database and will be made available
* to the UI.
*
* @param token
* the token from the front end
* @param req
* the Stapler request from the ui
* @param rsp
* the Stapler response for the ui
* @return the result which is this class
* @throws IOException
*/
@SuppressWarnings("unchecked")
public Object getDynamic(String token, StaplerRequest req, StaplerResponse rsp) throws Exception {
logger.debug("getDynamic:" + token);
String klass = req.getParameter("class");
String id = req.getParameter("id");
IDataBase dataBase = null;
try {
if (klass != null && id != null) {
long _id = Long.parseLong(id);
if (composite != null && composite.getId().equals(_id)) {
return this;
}
java.lang.Class _klass = java.lang.Class.forName(klass);
dataBase = getDataBase(abstractBuild);
composite = dataBase.find((java.lang.Class<Composite<?, ?>>) _klass, _id);
logger.debug("Class : " + klass + ", id : " + id + ", " + composite);
}
} catch (Exception e) {
logger.error("Exception initialising the model and the source for : " + klass + ", " + id, e);
} finally {
closeDataBase(dataBase);
}
return this;
}
public Object getOwner() {
logger.debug("getOwner");
return this.abstractBuild;
}
public String getName() {
logger.debug("getName");
if (abstractBuild != null) {
AbstractProject<?, ?> abstractProject = abstractBuild.getProject();
if (abstractProject != null) {
return abstractProject.getName();
}
}
return "No name Project...";
}
public boolean hasReport() {
logger.debug("hasReport");
IDataBase dataBase = null;
try {
dataBase = getDataBase(abstractBuild);
return dataBase.find(Project.class, Toolkit.hash(Project.class.getName())) != null;
} finally {
closeDataBase(dataBase);
}
}
public Project<?, ?> getProject() {
logger.debug("getProject");
if (project == null) {
IDataBase dataBase = null;
try {
dataBase = getDataBase(abstractBuild);
project = dataBase.find(Project.class, Toolkit.hash(Project.class.getName()));
} finally {
closeDataBase(dataBase);
}
}
// logger.debug("Project : " + ToStringBuilder.reflectionToString(project));
return project;
}
@SuppressWarnings("unchecked")
public List<Package> getPackages() {
logger.debug("getPackages");
IDataBase dataBase = null;
try {
dataBase = getDataBase(abstractBuild);
List<Package> packages = dataBase.find(Package.class);
if (packages != null) {
Collections.sort(packages, new Comparator<Package>() {
public int compare(Package o1, Package o2) {
return o1.getName().compareTo(o2.getName());
}
});
// Sort the classes in the packages
for (Package<?, ?> pakkage : packages) {
Collections.sort(pakkage.getChildren(), new Comparator<Class<?, ?>>() {
public int compare(Class<?, ?> o1, Class<?, ?> o2) {
return o1.getName().compareTo(o2.getName());
}
});
// Sort the methods in the classes
for (Class<?, ?> klass : pakkage.getChildren()) {
Collections.sort(klass.getChildren(), new Comparator<Method<?, ?>>() {
public int compare(Method<?, ?> o1, Method<?, ?> o2) {
return o1.getName().compareTo(o2.getName());
}
});
}
}
}
// Remove the inner classes from the packages
for (Package<?, ?> pakkage : packages) {
Iterator<Class<?, ?>> iterator = pakkage.getChildren().iterator();
while (iterator.hasNext()) {
Class<?, ?> klass = iterator.next();
if (klass.getName().indexOf("$") > -1) {
iterator.remove();
}
}
}
return packages;
} finally {
closeDataBase(dataBase);
}
}
public String getModel() {
logger.debug("getModel");
if (composite == null) {
return "";
}
return getModel(null, composite);
}
public String getProjectModel() {
logger.debug("getProjectModel");
// Move the build forward to the last build because Hudson will go to the last stable build
// which we don't want, we want the last build
AbstractBuild<?, ?> abstractBuild = getLastBuild(this.abstractBuild);
IDataBase dataBase = getDataBase(abstractBuild);
Project<?, ?> project = dataBase.find(Project.class, Toolkit.hash(Project.class.getName()));
Object object = abstractBuild.getProject();
if (object instanceof hudson.model.Project<?, ?>) {
hudson.model.Project<?, ?> hudsonProject = (hudson.model.Project<?, ?>) object;
String projectName = hudsonProject.getName();
project.setName(projectName);
}
String model = getModel("ProjectSmall", project);
return model;
}
private AbstractBuild<?, ?> getLastBuild(AbstractBuild<?, ?> abstractBuild) {
if (abstractBuild.getNextBuild() == null) {
return abstractBuild;
}
AbstractBuild<?, ?> nextAbstractBuild = getLastBuild(abstractBuild.getNextBuild());
if (nextAbstractBuild.isBuilding()) {
return abstractBuild;
}
return getLastBuild(abstractBuild.getNextBuild());
}
public String getSource() {
logger.debug("getSource");
return getSource(composite);
}
public String getFile(String name) {
return Toolkit.getContents(this.getClass().getResourceAsStream(name)).toString();
}
private String getSource(Composite<?, ?> composite) {
logger.debug("getSource");
if (composite instanceof Class<?, ?>) {
String className = ((Class<?, ?>) composite).getName();
String sourceFilePath = abstractBuild.getRootDir().getAbsolutePath() + File.separator + IConstants.SERENITY_SOURCE + File.separator
+ className + ".html";
logger.debug("Looking for source : " + sourceFilePath);
File sourceFile = new File(sourceFilePath);
if (sourceFile.exists()) {
String source = Toolkit.getContents(sourceFile).toString();
return source;
}
}
return "";
}
@SuppressWarnings("unchecked")
public String getModel(String modelName, Composite<?, ?> composite) {
logger.debug("getModel");
if (composite == null) {
return "";
}
LinkedList<Composite<?, ?>> composites = new LinkedList<Composite<?, ?>>();
composites.addFirst(composite);
LinkedList<Integer> buildNumbers = new LinkedList<Integer>();
buildNumbers.addFirst(abstractBuild.number);
composites = getPreviousComposites((java.lang.Class<Composite<?, ?>>) composite.getClass(), abstractBuild, composites, buildNumbers,
composite.getId(), 1);
IModeller modeller = new HighchartsModeller(modelName, buildNumbers.toArray(new Integer[buildNumbers.size()]));
modeller.visit(composite.getClass(), composites.toArray(new Composite[composites.size()]));
String model = modeller.getModel();
return model;
}
@SuppressWarnings("unchecked")
LinkedList<Composite<?, ?>> getPreviousComposites(java.lang.Class<Composite<?, ?>> klass, AbstractBuild<?, ?> abstractBuild,
LinkedList<Composite<?, ?>> composites, LinkedList<Integer> buildNumbers, Long id, int history) {
if (history >= HISTORY) {
return composites;
}
logger.debug("Abstract build : " + abstractBuild);
AbstractBuild<?, ?> previousBuild = abstractBuild.getPreviousBuild();
if (previousBuild == null) {
return composites;
}
IDataBase dataBase = getDataBase(previousBuild);
if (dataBase == null) {
return composites;
}
Composite<?, ?> composite = dataBase.find(klass, id);
logger.debug("Looking for composite : " + id + ", " + composite);
closeDataBase(dataBase);
if (composite == null) {
return composites;
}
composites.addFirst(composite);
buildNumbers.addFirst(previousBuild.number);
return getPreviousComposites((java.lang.Class<Composite<?, ?>>) composite.getClass(), previousBuild, composites, buildNumbers, id, ++history);
}
@SuppressWarnings("unchecked")
protected void printParameters(StaplerRequest req) {
Enumeration<String> parameterNames = req.getParameterNames();
while (parameterNames.hasMoreElements()) {
String parameterName = parameterNames.nextElement();
logger.debug("Parameter : " + parameterName + ", value : " + req.getParameter(parameterName));
}
}
private IDataBase getDataBase(AbstractBuild<?, ?> abstractBuild) {
String dataBaseFile = abstractBuild.getRootDir().getAbsolutePath() + File.separator + IConstants.DATABASE_FILE_ODB;
return IDataBase.DataBaseManager.getDataBase(DataBaseOdb.class, dataBaseFile, null);
}
private void closeDataBase(IDataBase dataBase) {
try {
if (dataBase != null) {
dataBase.close();
}
} catch (Exception e) {
logger.error("Exception closing database : " + dataBase, e);
}
}
}