Skip to content

Commit

Permalink
[FIXED JENKINS-11420] rewrote the JDK installer to avoid using HtmlUnit.
Browse files Browse the repository at this point in the history
This lets us get rid of Xalan safely (as well as other often problematic
dependencies like nekohtml + Xerces combo), and reduce the dependency
footprint.
(cherry picked from commit e1691a1)

Conflicts:

	changelog.html
	core/pom.xml
	core/src/main/java/hudson/tools/JDKInstaller.java
  • Loading branch information
kohsuke authored and vjuranek committed Feb 13, 2012
1 parent 6771142 commit fa134e6
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 99 deletions.
5 changes: 4 additions & 1 deletion changelog.html
Expand Up @@ -54,7 +54,10 @@

<!-- Record your changes in the trunk here. -->
<div id="trunk" style="display:none"><!--=TRUNK-BEGIN=-->
<ul class=image>
<ul class=image>
<li class=bug>
Rewrote the JDK installer to remove problematic HtmlUnit dependencies.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-11420">issue 11420</a>)
<li class=bug>
Fixed NPE in Subversion polling of Maven jobs.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-11592">issue 11592</a>)
Expand Down
15 changes: 2 additions & 13 deletions core/pom.xml
Expand Up @@ -150,19 +150,8 @@ THE SOFTWARE.
<version>2.1-rev7</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci</groupId>
<artifactId>htmlunit</artifactId>
<version>2.6-jenkins-5</version>
<exclusions>
<exclusion>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
</exclusion>
<exclusion>
<groupId>xalan</groupId>
<artifactId>xalan</artifactId>
</exclusion>
</exclusions>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
</dependency>
<dependency>
<groupId>args4j</groupId>
Expand Down
184 changes: 99 additions & 85 deletions core/src/main/java/hudson/tools/JDKInstaller.java
Expand Up @@ -23,20 +23,12 @@
*/
package hudson.tools;

import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.DefaultCredentialsProvider;
import com.gargoylesoftware.htmlunit.ProxyConfig;
import com.gargoylesoftware.htmlunit.ElementNotFoundException;
import com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import hudson.AbortException;
import hudson.Extension;
import hudson.FilePath;
import hudson.ProxyConfiguration;
import hudson.Launcher;
import hudson.Launcher.ProcStarter;
import hudson.ProxyConfiguration;
import hudson.Util;
import hudson.model.DownloadService.Downloadable;
import hudson.model.JDK;
Expand All @@ -50,7 +42,12 @@
import hudson.util.Secret;
import jenkins.model.Jenkins;
import net.sf.json.JSONObject;
import org.apache.commons.httpclient.auth.CredentialsProvider;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.io.IOUtils;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.HttpResponse;
Expand All @@ -74,6 +71,8 @@
import java.util.List;
import java.util.Locale;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static hudson.tools.JDKInstaller.Preference.*;

Expand Down Expand Up @@ -347,91 +346,106 @@ public URL locate(TaskListener log, Platform platform, CPU cpu) throws IOExcepti
LOGGER.fine("Platform choice:"+primary);

log.getLogger().println("Downloading JDK from "+primary.filepath);
URL src = new URL(primary.filepath);

WebClient wc = new WebClient();
// honor jenkins proxy settings in WebClient
Jenkins h = Jenkins.getInstance();
ProxyConfiguration jpc = h!=null ? h.proxy : null;
HttpClient hc = new HttpClient();
hc.getParams().setParameter("http.useragent","Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)");
Jenkins j = Jenkins.getInstance();
ProxyConfiguration jpc = j!=null ? j.proxy : null;
if(jpc != null) {
ProxyConfig pc = new ProxyConfig();
pc.setProxyHost(jpc.name);
pc.setProxyPort(jpc.port);
wc.setProxyConfig(pc);
if(jpc.getUserName() != null) {
DefaultCredentialsProvider cp = new DefaultCredentialsProvider();
cp.addCredentials(jpc.getUserName(), jpc.getPassword(), jpc.name, jpc.port, null);
wc.setCredentialsProvider(cp);
}
hc.getHostConfiguration().setProxy(jpc.name, jpc.port);
if(jpc.getUserName() != null)
hc.getState().setProxyCredentials(AuthScope.ANY,new UsernamePasswordCredentials(jpc.getUserName(),jpc.getPassword()));
}

wc.setJavaScriptEnabled(false);
wc.setCssEnabled(false);
Page page = wc.getPage(src);
int authCount=0;
int totalPageCount=0;
while (page instanceof HtmlPage) {
// some times we are redirected to the SSO login page.
HtmlPage html = (HtmlPage) page;
URL loginUrl = page.getWebResponse().getUrl();
if (!loginUrl.getHost().equals("login.oracle.com"))
throw new IOException("Expected to see a login page but instead saw "+loginUrl);

String u = getDescriptor().getUsername();
Secret p = getDescriptor().getPassword();
if (u==null || p==null) {
log.hyperlink(getCredentialPageUrl(),"Oracle now requires Oracle account to download previous versions of JDK. Please specify your Oracle account username/password.\n");
throw new AbortException("Unable to install JDK unless a valid username/password is provided.");
}

if (totalPageCount++>16) // looping too much
throw new IOException("Unable to find the login form in "+html.asXml());
int authCount=0, totalPageCount=0; // counters for avoiding infinite loop

try {
// JavaScript check page. Just submit and move on
HtmlForm loginForm = html.getFormByName("myForm");
page = loginForm.submit(null);
continue;
} catch (ElementNotFoundException e) {
// fall through
}

try {
// real authentication page
if (authCount++ > 3) {
log.hyperlink(getCredentialPageUrl(),"Your Oracle account doesn't appear valid. Please specify a valid username/password\n");
throw new AbortException("Unable to install JDK unless a valid username/password is provided.");
}
HtmlForm loginForm = html.getFormByName("LoginForm");
loginForm.getInputByName("ssousername").setValueAttribute(u);
loginForm.getInputByName("password").setValueAttribute(p.getPlainText());
page = loginForm.submit(null);
continue;
} catch (ElementNotFoundException e) {
// fall through
}

throw new IOException("Unable to find the login form in "+html.asXml());
}

// download to a temporary file and rename it in to handle concurrency and failure correctly,
File tmp = new File(cache.getPath()+".tmp");
tmp.getParentFile().mkdirs();
HttpMethodBase m = new GetMethod(primary.filepath);
try {
FileOutputStream out = new FileOutputStream(tmp);
try {
IOUtils.copy(page.getWebResponse().getContentAsStream(), out);
} finally {
out.close();
while (true) {
if (totalPageCount++>16) // looping too much
throw new IOException("Unable to find the login form");

LOGGER.fine("Requesting " + m.getURI());
int r = hc.executeMethod(m);
if (r/100==3) {
// redirect?
String loc = m.getResponseHeader("Location").getValue();
m.releaseConnection();
m = new GetMethod(loc);
continue;
}
if (r!=200)
throw new IOException("Failed to request " + m.getURI() +" exit code="+r);

if (m.getURI().getHost().equals("login.oracle.com")) {
LOGGER.fine("Appears to be a login page");
String resp = IOUtils.toString(m.getResponseBodyAsStream(), m.getResponseCharSet());
m.releaseConnection();
Matcher pm = Pattern.compile("<form .*?action=\"([^\"]*)\" .*?</form>", Pattern.DOTALL).matcher(resp);
if (!pm.find())
throw new IllegalStateException("Unable to find a form in the response:\n"+resp);

String form = pm.group();
PostMethod post = new PostMethod(
new URL(new URL(m.getURI().getURI()),pm.group(1)).toExternalForm());

String u = getDescriptor().getUsername();
Secret p = getDescriptor().getPassword();
if (u==null || p==null) {
log.hyperlink(getCredentialPageUrl(),"Oracle now requires Oracle account to download previous versions of JDK. Please specify your Oracle account username/password.\n");
throw new AbortException("Unable to install JDK unless a valid Oracle account username/password is provided in the system configuration.");
}

for (String fragment : form.split("<input")) {
String n = extractAttribute(fragment,"name");
String v = extractAttribute(fragment,"value");
if (n==null || v==null) continue;
if (n.equals("ssousername"))
v = u;
if (n.equals("password")) {
v = p.getPlainText();
if (authCount++ > 3) {
log.hyperlink(getCredentialPageUrl(),"Your Oracle account doesn't appear valid. Please specify a valid username/password\n");
throw new AbortException("Unable to install JDK unless a valid username/password is provided.");
}
}
post.addParameter(n, v);
}

m = post;
} else {
log.getLogger().println("Downloading " + m.getResponseContentLength() + "bytes");

// download to a temporary file and rename it in to handle concurrency and failure correctly,
File tmp = new File(cache.getPath()+".tmp");
try {
tmp.getParentFile().mkdirs();
FileOutputStream out = new FileOutputStream(tmp);
try {
IOUtils.copy(m.getResponseBodyAsStream(), out);
} finally {
out.close();
}

tmp.renameTo(cache);
return cache.toURL();
} finally {
tmp.delete();
}
}
}

tmp.renameTo(cache);
return cache.toURL();
} finally {
tmp.delete();
m.releaseConnection();
}
}

private static String extractAttribute(String s, String name) {
String h = name + "=\"";
int si = s.indexOf(h);
if (si<0) return null;
int ei = s.indexOf('\"',si+h.length());
return s.substring(si+h.length(),ei);
}

private String getCredentialPageUrl() {
return "/"+getDescriptor().getDescriptorUrl()+"/enterCredential";
}
Expand Down

0 comments on commit fa134e6

Please sign in to comment.