package org.jggug.hudson.plugins.gcrawler; import static java.lang.String.format; import hudson.Extension; import hudson.model.Descriptor; import hudson.model.Hudson; import hudson.model.ListView; import hudson.model.PeriodicWork; import hudson.model.View; import hudson.model.Descriptor.FormException; import hudson.views.BuildButtonColumn; import hudson.views.JobColumn; import hudson.views.LastDurationColumn; import hudson.views.LastFailureColumn; import hudson.views.LastSuccessColumn; import hudson.views.ListViewColumn; import hudson.views.StatusColumn; import hudson.views.WeatherColumn; import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Map.Entry; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import javax.servlet.ServletException; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import org.apache.commons.lang.time.DurationFormatUtils; import org.apache.commons.lang.time.StopWatch; import org.jggug.hudson.plugins.gcrawler.crawlers.GrailsPluginsCrawler; import org.kohsuke.stapler.StaplerRequest; public class GCrawler extends PeriodicWork { @Extension public static final GCrawler CRAWLER = new GCrawler(); private CrawlLogger logger; private boolean isActive; private CrawlContext context; protected GCrawler() {} @Override protected void doRun() { if (isActive) { return; } isActive = true; StopWatch watch = new StopWatch(); watch.start(); if (context == null || context.isClosed()) { context = CrawlContext.newInstance(); } logger = context.getLogger(); logger.info("crawl start."); logger.info("-- Installed Grails Versions --"); List<String> versions = new ArrayList<String>(context.getGrailsMap().keySet()); Collections.sort(versions); for (String ver : versions) { logger.info(format(" %s", ver)); } logger.info("-------------------------------"); try { ExecutorService service = Executors.newSingleThreadExecutor(); // TODO setting by management view. // Future<List<GrailsProjectInfo>> future = service.submit(new GoogleCodeCrawler(context)); Future<List<GrailsProjectInfo>> future = service.submit(new GrailsPluginsCrawler(context)); List<GrailsProjectInfo> projects = future.get(); rebuildViews(projects); GCrawlerPlugin.getConfig().setGrailsProjectInfoList(projects); watch.stop(); logger.info(format("crawl complete [%s].", DurationFormatUtils.formatDuration(watch.getTime(), "m:ss.SSS"))); } catch (InterruptedException e) { logger.warn(e); } catch (ExecutionException e) { logger.warn(e); } catch (IOException e) { logger.warn(e); } finally { context.close(); isActive = false; GCrawlerPlugin.getConfig().setLastCrawlDate(new Date()); } } @Override public long getRecurrencePeriod() { // each 1 hour. return 60 * 60 * 1000; } private void rebuildViews(List<GrailsProjectInfo> projects) throws IOException { Hudson hudson = Hudson.getInstance(); for (View view : hudson.getViews()) { if (view instanceof ListView) hudson.deleteView(view); } Map<String, Set<String>> map = new HashMap<String, Set<String>>(); for (GrailsProjectInfo p : projects) { if (!p.hasError()) { Set<String> jobNames = map.get(p.getGrailsVersion()); if (jobNames == null) { map.put(p.getGrailsVersion(), jobNames = new HashSet<String>()); } jobNames.add(format("%s.%s", p.getName(), p.getDomain())); } } for (Entry<String, Set<String>> entry : map.entrySet()) { GrailsListView.create(format("grails-%s", entry.getKey())).save(entry.getValue()); } } private static class GrailsListView extends ListView { private GrailsListView(String name) { super(name); } public void save(Set<String> jobNames) { try { submit(MockStaplerRequest.create(jobNames)); } catch (ServletException e) { throw new RuntimeException(e); } catch (FormException e) { throw new RuntimeException(e); } } public static GrailsListView create(String name) { GrailsListView result = new GrailsListView(name); result.owner = Hudson.getInstance(); try { Hudson.getInstance().addView(result); } catch (IOException e) { throw new RuntimeException(e); } return result; } } public synchronized boolean isActive() { return isActive; } private static class MockStaplerRequest implements InvocationHandler { private Set<String> jobNames; private JSONObject json; private MockStaplerRequest(Set<String> jobNames) { this.jobNames = jobNames; json = new JSONObject().accumulate("columns", new JSONArray() .element(createElement(StatusColumn.DescriptorImpl.class)) .element(createElement(WeatherColumn.DescriptorImpl.class)) .element(createElement(JobColumn.DescriptorImpl.class)) .element(createElement(LastSuccessColumn.DescriptorImpl.class)) .element(createElement(LastFailureColumn.DescriptorImpl.class)) .element(createElement(LastDurationColumn.DescriptorImpl.class)) .element(createElement(BuildButtonColumn.DescriptorImpl.class))); } private JSONObject createElement(Class<? extends Descriptor<ListViewColumn>> type) { return new JSONObject().accumulate("kind", type.getName()); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); if (methodName.equals("getParameter")) { return jobNames.contains(args[0]) ? "true" : null; } else if (methodName.equals("getSubmittedForm")) { return json; } throw new UnsupportedOperationException(methodName); } public static StaplerRequest create(Set<String> jobNames) { return (StaplerRequest) Proxy.newProxyInstance( StaplerRequest.class.getClassLoader(), new Class[] {StaplerRequest.class}, new MockStaplerRequest(jobNames)); } } public void setCrawlerContext(CrawlContext context) { this.context = context; } public CrawlContext getCrawlerContext() { return context; } }