package org.jggug.hudson.plugins.gcrawler.crawlers;
import static java.lang.String.format;
import static java.util.regex.Pattern.compile;
import static org.apache.commons.lang.exception.ExceptionUtils.getFullStackTrace;
import static org.jggug.hudson.plugins.gcrawler.util.HudsonPluginUtils.addGBuildWrapper;
import static org.jggug.hudson.plugins.gcrawler.util.HudsonPluginUtils.createEmotionalHudsonPublisher;
import static org.jggug.hudson.plugins.gcrawler.util.HudsonPluginUtils.createTwitterPublisher;
import static org.jggug.hudson.plugins.gcrawler.util.HudsonPluginUtils.isActive;
import static org.jggug.hudson.plugins.gcrawler.util.JobTemplate.createTemplate;
import static org.jggug.hudson.plugins.gcrawler.util.PropertyFileUtils.getStringPropertyValue;
import static org.jggug.hudson.plugins.gcrawler.util.PropertyFileUtils.toResourceBundleFromText;
import hudson.model.Descriptor;
import hudson.model.FreeStyleProject;
import hudson.model.Hudson;
import hudson.tasks.Builder;
import hudson.tasks.LogRotator;
import hudson.tasks.Publisher;
import hudson.tasks.Shell;
import hudson.tasks.junit.JUnitResultArchiver;
import hudson.triggers.SCMTrigger;
import hudson.util.DescribableList;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.PropertyResourceBundle;
import java.util.concurrent.Callable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.jggug.hudson.plugins.gcrawler.CrawlContext;
import org.jggug.hudson.plugins.gcrawler.GrailsProjectInfo;
import org.jggug.hudson.plugins.gcrawler.scm.FileInfo;
import org.jggug.hudson.plugins.gcrawler.scm.RepositoryException;
import org.jggug.hudson.plugins.gcrawler.scm.RepositoryWrapper;
import org.jggug.hudson.plugins.gcrawler.util.HttpUtils;
import org.jggug.hudson.plugins.gcrawler.util.JobTemplate;
import com.g2one.hudson.grails.GrailsBuilder;
public abstract class GrailsProjectCrawlerTask implements Callable<GrailsProjectInfo> {
private static final String VERSION = "app.version";
private static final String SERVLET_VERSION = "app.servlet.version";
private static final String GRAILS_VERSION = "app.grails.version";
private static final String NAME = "app.name";
private static final Pattern PLUGINS = compile("plugins\\.(.*?)=(.*)");
private static final JobTemplate JOB_SHELL = createTemplate("google_grails_shell.txt");
private static final Pattern PATTERN_TESTS = Pattern.compile("^.*Tests.groovy$");
private String name;
private CrawlContext context;
private JobTemplate descriptionTemplate;
private RepositoryWrapper repository;
public GrailsProjectCrawlerTask(String name, CrawlContext context, JobTemplate template, RepositoryWrapper repository) {
this.name = name;
this.context = context;
this.descriptionTemplate = template;
this.repository = repository;
}
public GrailsProjectInfo call() throws Exception {
long start = System.currentTimeMillis();
GrailsProjectInfo oldProject = context.getProjectMap().get(name);
GrailsProjectInfo result = null;
if (oldProject != null) {
if (isUpdated(oldProject)) {
result = createProjectInfo();
} else {
context.getLogger().info("--++-- not modified! --++--");
return oldProject;
}
if (result.hasError()) {
removeHudsonJob(result);
return result;
}
} else {
result = createProjectInfo();
}
result.setParseTime(System.currentTimeMillis() - start);
context.getLogger().info(result.toString());
return result;
}
protected boolean isUpdated(GrailsProjectInfo oldProject) throws RepositoryException {
return oldProject.getRevision() != repository.getLatestRevision();
}
private GrailsProjectInfo createProjectInfo() {
GrailsProjectInfo info = new GrailsProjectInfo();
info.setName(name);
info.setProjectUrl(getProjectUrl(info));
info.setDomain(getDomain());
try {
info.setRevision(repository.getLatestRevision());
FileInfo appProperties = repository.findFile("application.properties");
info.setTestsAvirable(repository.existsFileByPattern(PATTERN_TESTS));
String url = appProperties.getUrl();
info.setScmUrl(url.substring(0, url.lastIndexOf('/')));
setupProjectInfo(info, appProperties.getContent());
info.setType(isPluginProject(info) ? "grails-plugin" : "grails");
if (!info.hasError()) saveAsHudsonJob(info);
} catch (FileNotFoundException e) {
info.setErrorMessage("application.properties is not found.");
} catch (Exception e) {
context.getLogger().info(getFullStackTrace(e));
info.setErrorMessage("Unknwon erorr.");
}
return info;
}
protected void setupProjectInfo(GrailsProjectInfo info, String appProperties) {
try {
PropertyResourceBundle b = toResourceBundleFromText(appProperties);
info.setAppName(getStringPropertyValue(b, NAME));
info.setVersion(getStringPropertyValue(b, VERSION));
info.setGrailsVersion(getStringPropertyValue(b, GRAILS_VERSION));
info.setServletVersion(getStringPropertyValue(b, SERVLET_VERSION));
Matcher m = PLUGINS.matcher(appProperties);
while (m.find()) {
info.addPlugin(format("%s-%s", m.group(1), m.group(2)));
}
if (!context.getGrailsMap().containsKey(info.getGrailsVersion())) {
info.setErrorMessage(format("Unsupported grails version. [%s]", info.getGrailsVersion()));
}
} catch (IOException e) {
info.setErrorMessage("invalid application.properties.");
}
}
protected void removeHudsonJob(GrailsProjectInfo info) throws InterruptedException, IOException {
FreeStyleProject job = (FreeStyleProject) Hudson.getInstance().getItem(info.toJobName());
if (job != null) {
job.delete();
}
}
protected void saveAsHudsonJob(GrailsProjectInfo info) throws Exception {
String jobName = info.toJobName();
Hudson hudson = Hudson.getInstance();
FreeStyleProject job = (FreeStyleProject) hudson.getItem(jobName);
if (job == null) {
job = (FreeStyleProject) hudson.createProject(FreeStyleProject.DESCRIPTOR, jobName);
}
setupJob(job, info);
job.save();
job.onLoad(hudson, jobName);
}
protected void setupJob(FreeStyleProject job, GrailsProjectInfo info) throws Exception {
job.setDescription(descriptionTemplate.generate(info));
job.setLogRotator(new LogRotator(-1, 3));
job.addTrigger(new SCMTrigger("*/5 * * * *"));
job.setAssignedLabel(null);
addGBuildWrapper(job);
DescribableList<Builder,Descriptor<Builder>> builders = job.getBuildersList();
builders.clear();
builders.add(new Shell(JOB_SHELL.generate(info)));
String targets = "clean " + (info.isTestsAvirable() ? "test-app" : "package") + " --non-interactive";
builders.add(new GrailsBuilder(targets,
context.getGrailsMap().get(info.getGrailsVersion()), null, null, null, null));
DescribableList<Publisher,Descriptor<Publisher>> publishers = job.getPublishersList();
publishers.clear();
if (info.isTestsAvirable()) {
publishers.add(new JUnitResultArchiver(format("%s/test/reports/TEST*.xml", info.getName()), null));
}
if (isActive("emotional-hudson")) {
publishers.add(createEmotionalHudsonPublisher());
}
if (isActive("twitter")) {
publishers.add(createTwitterPublisher());
}
}
protected abstract String getProjectUrl(GrailsProjectInfo info);
protected abstract String getDomain();
protected boolean isPluginProject(GrailsProjectInfo info) {
return HttpUtils.existsFile(HttpUtils.joinAsPath(info.getScmUrl(), toPluginFileName(info.getAppName())));
}
protected String toPluginFileName(String appName) {
StringBuilder buff = new StringBuilder();
for (String s : appName.split("-")) {
buff.append(StringUtils.capitalize(s));
}
return buff.append("GrailsPlugin.groovy").toString();
}
}