/******************************************************************************* * * Copyright (c) 2004-2009 Oracle Corporation. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * * Kohsuke Kawaguchi * * *******************************************************************************/ package hudson; import hudson.util.DualOutputStream; import hudson.util.EncodingStream; import com.thoughtworks.xstream.core.util.Base64Encoder; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.Writer; import java.net.HttpRetryException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; import java.nio.charset.Charset; /** * Entry point to Hudson from command line. * * <p> This tool runs another process and sends its result to Hudson. * * @author Kohsuke Kawaguchi */ public class Main { public static void main(String[] args) { try { System.exit(run(args)); } catch (Exception e) { e.printStackTrace(); System.exit(-1); } } public static int run(String[] args) throws Exception { String home = getHudsonHome(); if (home == null) { System.err.println("HUDSON_HOME is not set."); return -1; } if (args.length < 2) { System.err.println("Usage: <job-name> <command> <args..>"); return -1; } return remotePost(args); } private static String getHudsonHome() { return EnvVars.masterEnvVars.get("HUDSON_HOME"); } /** * Run command and place the result to a remote Hudson installation */ public static int remotePost(String[] args) throws Exception { String projectName = args[0]; String home = getHudsonHome(); if (!home.endsWith("/")) { home = home + '/'; // make sure it ends with '/' } // check for authentication info String auth = new URL(home).getUserInfo(); if (auth != null) { auth = "Basic " + new Base64Encoder().encode(auth.getBytes("UTF-8")); } {// check if the home is set correctly HttpURLConnection con = open(new URL(home)); if (auth != null) { con.setRequestProperty("Authorization", auth); } con.connect(); if (con.getResponseCode() != 200 || con.getHeaderField("X-Hudson") == null) { System.err.println(home + " is not Hudson (" + con.getResponseMessage() + ")"); return -1; } } String projectNameEnc = URLEncoder.encode(projectName, "UTF-8").replaceAll("\\+", "%20"); {// check if the job name is correct HttpURLConnection con = open(new URL(home + "job/" + projectNameEnc + "/acceptBuildResult")); if (auth != null) { con.setRequestProperty("Authorization", auth); } con.connect(); if (con.getResponseCode() != 200) { System.err.println(projectName + " is not a valid job name on " + home + " (" + con.getResponseMessage() + ")"); return -1; } } // get a crumb to pass the csrf check String crumbField = null, crumbValue = null; try { HttpURLConnection con = open(new URL(home + "crumbIssuer/api/xml?xpath=concat(//crumbRequestField,\":\",//crumb)'")); if (auth != null) { con.setRequestProperty("Authorization", auth); } BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); String line = in.readLine(); in.close(); String[] components = line.split(":"); if (components.length == 2) { crumbField = components[0]; crumbValue = components[1]; } } catch (IOException e) { // presumably this Hudson doesn't use CSRF protection } // write the output to a temporary file first. File tmpFile = File.createTempFile("hudson", "log"); try { FileOutputStream os = new FileOutputStream(tmpFile); Writer w = new OutputStreamWriter(os, "UTF-8"); w.write("<?xml version='1.0' encoding='UTF-8'?>"); w.write("<run><log encoding='hexBinary' content-encoding='" + Charset.defaultCharset().name() + "'>"); w.flush(); // run the command long start = System.currentTimeMillis(); List<String> cmd = new ArrayList<String>(); for (int i = 1; i < args.length; i++) { cmd.add(args[i]); } Proc proc = new Proc.LocalProc(cmd.toArray(new String[cmd.size()]), (String[]) null, System.in, new DualOutputStream(System.out, new EncodingStream(os))); int ret = proc.join(); w.write("</log><result>" + ret + "</result><duration>" + (System.currentTimeMillis() - start) + "</duration></run>"); w.close(); String location = home + "job/" + projectNameEnc + "/postBuildResult"; while (true) { try { // start a remote connection HttpURLConnection con = open(new URL(location)); if (auth != null) { con.setRequestProperty("Authorization", auth); } if (crumbField != null && crumbValue != null) { con.setRequestProperty(crumbField, crumbValue); } con.setDoOutput(true); // this tells HttpURLConnection not to buffer the whole thing con.setFixedLengthStreamingMode((int) tmpFile.length()); con.connect(); // send the data FileInputStream in = new FileInputStream(tmpFile); Util.copyStream(in, con.getOutputStream()); in.close(); if (con.getResponseCode() != 200) { Util.copyStream(con.getErrorStream(), System.err); } return ret; } catch (HttpRetryException e) { if (e.getLocation() != null) { // retry with the new location location = e.getLocation(); continue; } // otherwise failed for reasons beyond us. throw e; } } } finally { tmpFile.delete(); } } /** * Connects to the given HTTP URL and configure time out, to avoid infinite * hang. */ private static HttpURLConnection open(URL url) throws IOException { HttpURLConnection c = (HttpURLConnection) url.openConnection(); c.setReadTimeout(TIMEOUT); c.setConnectTimeout(TIMEOUT); return c; } /** * Set to true if we are running unit tests. */ public static boolean isUnitTest = false; /** * Set to true if we are running inside "mvn hpi:run" or "mvn * hudson-dev:run" */ public static boolean isDevelopmentMode = Boolean.getBoolean(Main.class.getName() + ".development"); /** * Time out for socket connection to Hudson. */ public static final int TIMEOUT = Integer.getInteger(Main.class.getName() + ".timeout", 15000); }