/** * Hudson instance description. */ package hudson.plugins.build_publisher; import hudson.Util; import hudson.XmlFile; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.Hudson; import hudson.model.Item; import hudson.model.Run; import hudson.model.listeners.ItemListener; import hudson.plugins.build_publisher.StatusInfo.State; import org.apache.commons.httpclient.Credentials; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.SimpleHttpConnectionManager; import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.auth.AuthScope; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.httpclient.params.HttpClientParams; /** * Represents remote public Hudson instance. * * @author dvrzalik@redhat.com * */ public final class HudsonInstance { static final Logger LOGGER = Logger.getLogger(Hudson.class.getName()); private String url; private String name; private String login; private String password; // Builds to be published private transient LinkedHashSet<AbstractBuild> publishRequestQueue = new LinkedHashSet<AbstractBuild>(); private transient PublisherThread publisherThread; transient BuildTransmitter buildTransmitter; private transient HttpClient client; public String getLogin() { return login; } public String getPassword() { return password; } public boolean requiresAuthentication() { return Util.fixEmpty(login)!=null; } public HudsonInstance(String name, String url, String login, String password) { this.name = name; this.url = url; this.login = login; this.password = password; initVariables(); restoreQueue(); initPublisherThread(); } public String getUrl() { if (url != null && !url.endsWith("/")) { url += '/'; } return url; } public String getName() { return name; } /** * Append the build to the publishing queue. */ public void publishNewBuild(AbstractBuild build) { publishBuild(build, new StatusInfo(State.PENDING, "Waiting in queue", name, null)); } /** * Same as previous, but doesn't set status for the build. */ public synchronized void publishBuild(AbstractBuild build, StatusInfo status) { publishRequestQueue.add(build); StatusAction.setBuildStatusAction(build, status); saveQueue(); notifyAll(); } //Disable aborting until it is properly implemented //public void abortTransmission(AbstractBuild request) { // publisherThread.abortTrasmission(request); //} // XStream init private Object readResolve() { initVariables(); // let's wait until Hudson's initialized Hudson.getInstance().getJobListeners().add(new ItemListener() { @Override public void onLoaded() { restoreQueue(); initPublisherThread(); } }); return this; } private void initVariables() { publishRequestQueue = new LinkedHashSet<AbstractBuild>(); buildTransmitter = new HTTPBuildTransmitter(); SimpleHttpConnectionManager connectionManager = new SimpleHttpConnectionManager(); HttpClientParams params = new HttpClientParams(); //set SO_TIMEOUT to prevent thread hang-up params.setSoTimeout(10 * 60 * 1000); client = new HttpClient(params, connectionManager); // --- authentication //We don't use BASIC auth //setCredentialsFroClient(client); } /*package*/ void initPublisherThread() { if(publisherThread == null || !publisherThread.isAlive()) { publisherThread = new PublisherThread(HudsonInstance.this); publisherThread.start(); } } void setCredentialsFroClient(HttpClient httpClient) { try { URL sunUrl = new URL(url); if (login != null) { Credentials credentials = new UsernamePasswordCredentials( login, password); httpClient.getState().setCredentials( new AuthScope(sunUrl.getHost(), AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthScope.ANY_SCHEME), credentials); } } catch (MalformedURLException e) { e.printStackTrace(); LOGGER.severe(e.getMessage()); } } HttpClient getHttpClient() { return client; } synchronized void removeRequest(AbstractBuild request, StatusInfo statusInfo) { if (publishRequestQueue.contains(request)) { publishRequestQueue.remove(request); saveQueue(); StatusAction.setBuildStatusAction(request, statusInfo); } } synchronized void postponeRequest(AbstractBuild request) { if (publishRequestQueue.contains(request)) { publishRequestQueue.remove(request); publishRequestQueue.add(request); } } /** * Obtains the current queue of builds that are waiting for publication. */ public synchronized List<AbstractBuild> getQueue() { return new ArrayList<AbstractBuild>(publishRequestQueue); } /** * Gets the thread that does the publication. * * @return * Can be null during the initialization of Hudson. */ public PublisherThread getPublisherThread() { return publisherThread; } synchronized AbstractBuild nextRequest() { // If there is nothing to do let's wait until next // PublishRequest waitForRequest(); return publishRequestQueue.iterator().next(); } private void waitForRequest() { while (publishRequestQueue.isEmpty()) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); return; } } } /** * Serializes the queue into $HUDSON_HOME */ private void saveQueue() { List<RequestHolder> holders = new LinkedList<RequestHolder>(); for (AbstractBuild request : publishRequestQueue) { int n = request.getNumber(); AbstractProject p = request.getProject(); RequestHolder holder = new RequestHolder(n,p.getFullName()); holders.add(holder); } XmlFile file = new XmlFile(new File(Hudson.getInstance().getRootDir(), "bp-" + name + ".xml")); try { file.write(holders); } catch (IOException e) { e.printStackTrace(); LOGGER.severe(e.getMessage()); } } private void restoreQueue() { XmlFile file = new XmlFile(new File(Hudson.getInstance().getRootDir(), "bp-" + name + ".xml")); if(!file.exists()) return; // nothing to restore. try { List<RequestHolder> holders = (List<RequestHolder>) file.read(); for (RequestHolder holder : holders) { String projectName = holder.project; Item project = Hudson.getInstance().getItemByFullName( projectName); if (project instanceof AbstractProject) { Run build = ((AbstractProject) project) .getBuildByNumber(holder.build); if (build instanceof AbstractBuild) { publishRequestQueue.add((AbstractBuild) build); } } } } catch (IOException e) { LOGGER.log(Level.SEVERE,"Could not restore publisher queue from " + file.getFile().getAbsolutePath(),e); } } private static class RequestHolder { int build; String project; public RequestHolder(int build, String project) { this.build = build; this.project = project; } } }