staticLegacyInstances = new ConcurrentHashMap<>();
/**
* Exposed for the test harness to clear all legacy extension instances.
diff --git a/core/src/main/java/hudson/model/AbstractBuild.java b/core/src/main/java/hudson/model/AbstractBuild.java
index 1f2e32bea3..5e66617e27 100644
--- a/core/src/main/java/hudson/model/AbstractBuild.java
+++ b/core/src/main/java/hudson/model/AbstractBuild.java
@@ -436,11 +436,19 @@ public abstract class AbstractBuild,R extends Abs
protected Lease decideWorkspace(@Nonnull Node n, WorkspaceList wsl) throws InterruptedException, IOException {
String customWorkspace = getProject().getCustomWorkspace();
if (customWorkspace != null) {
+ FilePath rootPath = n.getRootPath();
+ if (rootPath == null) {
+ throw new AbortException(n.getDisplayName() + " seems to be offline");
+ }
// we allow custom workspaces to be concurrently used between jobs.
- return Lease.createDummyLease(n.getRootPath().child(getEnvironment(listener).expand(customWorkspace)));
+ return Lease.createDummyLease(rootPath.child(getEnvironment(listener).expand(customWorkspace)));
}
// TODO: this cast is indicative of abstraction problem
- return wsl.allocate(n.getWorkspaceFor((TopLevelItem)getProject()), getBuild());
+ FilePath ws = n.getWorkspaceFor((TopLevelItem) getProject());
+ if (ws == null) {
+ throw new AbortException(n.getDisplayName() + " seems to be offline");
+ }
+ return wsl.allocate(ws, getBuild());
}
public Result run(@Nonnull BuildListener listener) throws Exception {
diff --git a/core/src/main/java/hudson/model/AbstractProject.java b/core/src/main/java/hudson/model/AbstractProject.java
index 954011f650..ffee6413d8 100644
--- a/core/src/main/java/hudson/model/AbstractProject.java
+++ b/core/src/main/java/hudson/model/AbstractProject.java
@@ -1015,24 +1015,6 @@ public abstract class AbstractProject
,R extends A
return this; // in this way, any member that wants to run with the main guy can nominate the project itself
}
- /**
- * {@inheritDoc}
- *
- *
- * A project must be blocked if its own previous build is in progress,
- * or if the blockBuildWhenUpstreamBuilding option is true and an upstream
- * project is building, but derived classes can also check other conditions.
- */
- @Override
- public boolean isBuildBlocked() {
- return getCauseOfBlockage()!=null;
- }
-
- public String getWhyBlocked() {
- CauseOfBlockage cb = getCauseOfBlockage();
- return cb!=null ? cb.getShortDescription() : null;
- }
-
/**
* @deprecated use {@link BlockedBecauseOfBuildInProgress} instead.
*/
@@ -1075,6 +1057,14 @@ public abstract class AbstractProject
,R extends A
}
}
+ /**
+ * {@inheritDoc}
+ *
+ *
+ * A project must be blocked if its own previous build is in progress,
+ * or if the blockBuildWhenUpstreamBuilding option is true and an upstream
+ * project is building, but derived classes can also check other conditions.
+ */
@Override
public CauseOfBlockage getCauseOfBlockage() {
// Block builds until they are done with post-production
diff --git a/core/src/main/java/hudson/model/AllView.java b/core/src/main/java/hudson/model/AllView.java
index 8db2f352a1..4daedafd5a 100644
--- a/core/src/main/java/hudson/model/AllView.java
+++ b/core/src/main/java/hudson/model/AllView.java
@@ -27,7 +27,6 @@ import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
-import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import jenkins.util.SystemProperties;
import org.apache.commons.lang.StringUtils;
@@ -121,7 +120,7 @@ public class AllView extends View {
* its name is one of the localized forms of {@link Messages#_Hudson_ViewName()} and the user has not opted out of
* fixing the view name by setting the system property {@code hudson.mode.AllView.JENKINS-38606} to {@code false}.
* Use this method to round-trip the primary view name, e.g.
- * {@code primaryView = applyJenkins38606Fixup(views, primaryView)}
+ * {@code primaryView = migrateLegacyPrimaryAllViewLocalizedName(views, primaryView)}
*
* NOTE: we can only fix the localized name of an {@link AllView} if it is the primary view as otherwise urls
* would change, whereas the primary view is special and does not normally get accessed by the
@@ -164,7 +163,7 @@ public class AllView extends View {
// bingo JENKINS-38606 detected
LOGGER.log(Level.INFO,
"JENKINS-38606 detected for AllView in {0}; renaming view from {1} to {2}",
- new Object[]{allView.owner.getUrl(), primaryView, DEFAULT_VIEW_NAME});
+ new Object[] {allView.owner, primaryView, DEFAULT_VIEW_NAME});
allView.name = DEFAULT_VIEW_NAME;
return DEFAULT_VIEW_NAME;
}
diff --git a/core/src/main/java/hudson/model/ListView.java b/core/src/main/java/hudson/model/ListView.java
index 22d6b6b038..ad9667f04c 100644
--- a/core/src/main/java/hudson/model/ListView.java
+++ b/core/src/main/java/hudson/model/ListView.java
@@ -29,6 +29,8 @@ import hudson.Util;
import hudson.diagnosis.OldDataMonitor;
import hudson.model.Descriptor.FormException;
import hudson.model.listeners.ItemListener;
+import hudson.search.CollectionSearchIndex;
+import hudson.search.SearchIndexBuilder;
import hudson.security.ACL;
import hudson.security.ACLContext;
import hudson.util.CaseInsensitiveComparator;
@@ -168,7 +170,8 @@ public class ListView extends View implements DirectlyModifiableView {
public DescribableList> getColumns() {
return columns;
}
-
+
+
/**
* Returns a read-only view of all {@link Job}s in this view.
*
@@ -178,6 +181,20 @@ public class ListView extends View implements DirectlyModifiableView {
*/
@Override
public List getItems() {
+ return getItems(this.recurse);
+ }
+
+ /**
+ * Returns a read-only view of all {@link Job}s in this view.
+ *
+ *
+ *
+ * This method returns a separate copy each time to avoid
+ * concurrent modification issue.
+ * @param recurse {@code false} not to recurse in ItemGroups
+ * true to recurse in ItemGroups
+ */
+ private List getItems(boolean recurse) {
SortedSet names;
List items = new ArrayList();
@@ -217,6 +234,23 @@ public class ListView extends View implements DirectlyModifiableView {
return items;
}
+ @Override
+ public SearchIndexBuilder makeSearchIndex() {
+ SearchIndexBuilder sib = new SearchIndexBuilder().addAllAnnotations(this);
+ sib.add(new CollectionSearchIndex() {// for jobs in the view
+ protected TopLevelItem get(String key) { return getItem(key); }
+ protected Collection all() { return getItems(); }
+ @Override
+ protected String getName(TopLevelItem o) {
+ // return the name instead of the display for suggestion searching
+ return o.getName();
+ }
+ });
+ // add the display name for each item in the search index
+ addDisplayNamesToSearchIndex(sib, getItems(true));
+ return sib;
+ }
+
private List expand(Collection items, List allItems) {
for (TopLevelItem item : items) {
if (item instanceof ItemGroup) {
diff --git a/core/src/main/java/hudson/model/Queue.java b/core/src/main/java/hudson/model/Queue.java
index 3ea6e8dacf..ece3da76cf 100644
--- a/core/src/main/java/hudson/model/Queue.java
+++ b/core/src/main/java/hudson/model/Queue.java
@@ -1814,18 +1814,22 @@ public class Queue extends ResourceController implements Saveable {
/**
* Returns true if the execution should be blocked
* for temporary reasons.
- *
- *
- * Short-hand for {@code getCauseOfBlockageForItem()!=null}.
+ * @deprecated Use {@link #getCauseOfBlockage} != null
*/
- boolean isBuildBlocked();
+ @Deprecated
+ default boolean isBuildBlocked() {
+ return getCauseOfBlockage() != null;
+ }
/**
* @deprecated as of 1.330
* Use {@link CauseOfBlockage#getShortDescription()} instead.
*/
@Deprecated
- String getWhyBlocked();
+ default String getWhyBlocked() {
+ CauseOfBlockage cause = getCauseOfBlockage();
+ return cause != null ? cause.getShortDescription() : null;
+ }
/**
* If the execution of this task should be blocked for temporary reasons,
@@ -1841,9 +1845,6 @@ public class Queue extends ResourceController implements Saveable {
*/
@CheckForNull
default CauseOfBlockage getCauseOfBlockage() {
- if (isBuildBlocked()) {
- return CauseOfBlockage.fromMessage(Messages._Queue_Unknown());
- }
return null;
}
diff --git a/core/src/main/java/hudson/model/Slave.java b/core/src/main/java/hudson/model/Slave.java
index 3b9bbf42af..cde11411fe 100644
--- a/core/src/main/java/hudson/model/Slave.java
+++ b/core/src/main/java/hudson/model/Slave.java
@@ -26,15 +26,16 @@ package hudson.model;
import com.google.common.collect.ImmutableSet;
import hudson.DescriptorExtensionList;
+import hudson.EnvVars;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Launcher.RemoteLauncher;
import hudson.Util;
+import hudson.cli.CLI;
import hudson.model.Descriptor.FormException;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.remoting.Which;
-import hudson.slaves.CommandLauncher;
import hudson.slaves.ComputerLauncher;
import hudson.slaves.DumbSlave;
import hudson.slaves.JNLPLauncher;
@@ -47,6 +48,7 @@ import hudson.util.ClockDifference;
import hudson.util.DescribableList;
import hudson.util.FormValidation;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
@@ -57,6 +59,8 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;
@@ -225,6 +229,15 @@ public abstract class Slave extends Node implements Serializable {
}
public ComputerLauncher getLauncher() {
+ if (launcher == null && !StringUtils.isEmpty(agentCommand)) {
+ try {
+ launcher = (ComputerLauncher) Jenkins.getInstance().getPluginManager().uberClassLoader.loadClass("hudson.slaves.CommandLauncher").getConstructor(String.class, EnvVars.class).newInstance(agentCommand, null);
+ agentCommand = null;
+ save();
+ } catch (Exception x) {
+ LOGGER.log(Level.WARNING, "could not update historical agentCommand setting to CommandLauncher", x);
+ }
+ }
// Default launcher does not use Work Directory
return launcher == null ? new JNLPLauncher(false) : launcher;
}
@@ -389,20 +402,58 @@ public abstract class Slave extends Node implements Serializable {
throw new MalformedURLException("The specified file path " + fileName + " is not allowed due to security reasons");
}
- if (name.equals("hudson-cli.jar")) {
- name="jenkins-cli.jar";
+ if (name.equals("hudson-cli.jar") || name.equals("jenkins-cli.jar")) {
+ File cliJar = Which.jarFile(CLI.class);
+ if (cliJar.isFile()) {
+ name = "jenkins-cli.jar";
+ } else {
+ URL res = findExecutableJar(cliJar, CLI.class);
+ if (res != null) {
+ return res;
+ }
+ }
} else if (name.equals("agent.jar") || name.equals("slave.jar") || name.equals("remoting.jar")) {
- name = "lib/" + Which.jarFile(Channel.class).getName();
+ File remotingJar = Which.jarFile(hudson.remoting.Launcher.class);
+ if (remotingJar.isFile()) {
+ name = "lib/" + remotingJar.getName();
+ } else {
+ URL res = findExecutableJar(remotingJar, hudson.remoting.Launcher.class);
+ if (res != null) {
+ return res;
+ }
+ }
}
URL res = Jenkins.getInstance().servletContext.getResource("/WEB-INF/" + name);
if(res==null) {
- // during the development this path doesn't have the files.
- res = new URL(new File(".").getAbsoluteFile().toURI().toURL(),"target/jenkins/WEB-INF/"+name);
+ throw new FileNotFoundException(name); // giving up
+ } else {
+ LOGGER.log(Level.FINE, "found {0}", res);
}
return res;
}
+ /** Useful for {@code JenkinsRule.createSlave}, {@code hudson-dev:run}, etc. */
+ private @CheckForNull URL findExecutableJar(File notActuallyJAR, Class> mainClass) throws IOException {
+ if (notActuallyJAR.getName().equals("classes")) {
+ File[] siblings = notActuallyJAR.getParentFile().listFiles();
+ if (siblings != null) {
+ for (File actualJar : siblings) {
+ if (actualJar.getName().endsWith(".jar")) {
+ try (JarFile jf = new JarFile(actualJar, false)) {
+ Manifest mf = jf.getManifest();
+ if (mf != null && mainClass.getName().equals(mf.getMainAttributes().getValue("Main-Class"))) {
+ LOGGER.log(Level.FINE, "found {0}", actualJar);
+ return actualJar.toURI().toURL();
+ }
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
public byte[] readFully() throws IOException {
try (InputStream in = connect().getInputStream()) {
return IOUtils.toByteArray(in);
@@ -502,12 +553,6 @@ public abstract class Slave extends Node implements Serializable {
* Invoked by XStream when this object is read into memory.
*/
protected Object readResolve() {
- // convert the old format to the new one
- if (launcher == null) {
- launcher = (agentCommand == null || agentCommand.trim().length() == 0)
- ? new JNLPLauncher(false)
- : new CommandLauncher(agentCommand);
- }
if(nodeProperties==null)
nodeProperties = new DescribableList,NodePropertyDescriptor>(Jenkins.getInstance().getNodesObject());
return this;
diff --git a/core/src/main/java/hudson/model/queue/QueueTaskFilter.java b/core/src/main/java/hudson/model/queue/QueueTaskFilter.java
index 4688551b8e..9bf4887247 100644
--- a/core/src/main/java/hudson/model/queue/QueueTaskFilter.java
+++ b/core/src/main/java/hudson/model/queue/QueueTaskFilter.java
@@ -56,10 +56,12 @@ public abstract class QueueTaskFilter implements Queue.Task {
return base.getLastBuiltOn();
}
+ @Deprecated
public boolean isBuildBlocked() {
return base.isBuildBlocked();
}
+ @Deprecated
public String getWhyBlocked() {
return base.getWhyBlocked();
}
diff --git a/core/src/main/java/hudson/slaves/CommandConnector.java b/core/src/main/java/hudson/slaves/CommandConnector.java
deleted file mode 100644
index be23463986..0000000000
--- a/core/src/main/java/hudson/slaves/CommandConnector.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * The MIT License
- *
- * Copyright (c) 2010, InfraDNA, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package hudson.slaves;
-
-import hudson.EnvVars;
-import hudson.Extension;
-import hudson.Util;
-import hudson.model.TaskListener;
-import hudson.util.FormValidation;
-import org.jenkinsci.Symbol;
-import org.kohsuke.stapler.DataBoundConstructor;
-
-import java.io.IOException;
-import jenkins.model.Jenkins;
-import org.kohsuke.stapler.QueryParameter;
-
-/**
- * Executes a program on the master and expect that script to connect.
- *
- * @author Kohsuke Kawaguchi
- */
-public class CommandConnector extends ComputerConnector {
- public final String command;
-
- @DataBoundConstructor
- public CommandConnector(String command) {
- this.command = command;
- Jenkins.getInstance().checkPermission(Jenkins.RUN_SCRIPTS);
- }
-
- private Object readResolve() {
- Jenkins.getInstance().checkPermission(Jenkins.RUN_SCRIPTS);
- return this;
- }
-
- @Override
- public CommandLauncher launch(String host, TaskListener listener) throws IOException, InterruptedException {
- return new CommandLauncher(command,new EnvVars("SLAVE",host));
- }
-
- @Extension @Symbol("command")
- public static class DescriptorImpl extends ComputerConnectorDescriptor {
- @Override
- public String getDisplayName() {
- return Messages.CommandLauncher_displayName();
- }
-
- public FormValidation doCheckCommand(@QueryParameter String value) {
- if (!Jenkins.getInstance().hasPermission(Jenkins.RUN_SCRIPTS)) {
- return FormValidation.warning(Messages.CommandLauncher_cannot_be_configured_by_non_administrato());
- }
- if (Util.fixEmptyAndTrim(value) == null) {
- return FormValidation.error(Messages.CommandLauncher_NoLaunchCommand());
- } else {
- return FormValidation.ok();
- }
- }
-
- }
-}
diff --git a/core/src/main/java/hudson/slaves/CommandLauncher.java b/core/src/main/java/hudson/slaves/CommandLauncher.java
deleted file mode 100644
index 2e42131863..0000000000
--- a/core/src/main/java/hudson/slaves/CommandLauncher.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * The MIT License
- *
- * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Stephen Connolly
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package hudson.slaves;
-
-import hudson.AbortException;
-import hudson.EnvVars;
-import hudson.Util;
-import hudson.Extension;
-import hudson.Functions;
-import hudson.model.Descriptor;
-import hudson.model.Slave;
-import jenkins.model.Jenkins;
-import hudson.model.TaskListener;
-import hudson.remoting.Channel;
-import hudson.security.ACL;
-import hudson.util.StreamCopyThread;
-import hudson.util.FormValidation;
-import hudson.util.ProcessTree;
-
-import java.io.IOException;
-import java.util.Date;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.apache.commons.lang.StringUtils;
-import org.jenkinsci.Symbol;
-import org.kohsuke.stapler.DataBoundConstructor;
-import org.kohsuke.stapler.QueryParameter;
-
-/**
- * {@link ComputerLauncher} through a remote login mechanism like ssh/rsh.
- *
- * @author Stephen Connolly
- * @author Kohsuke Kawaguchi
-*/
-public class CommandLauncher extends ComputerLauncher {
-
- /**
- * Command line to launch the agent, like
- * "ssh myslave java -jar /path/to/hudson-remoting.jar"
- */
- private final String agentCommand;
-
- /**
- * Optional environment variables to add to the current environment. Can be null.
- */
- private final EnvVars env;
-
- @DataBoundConstructor
- public CommandLauncher(String command) {
- this(command, null);
- }
-
- public CommandLauncher(String command, EnvVars env) {
- this.agentCommand = command;
- this.env = env;
- Jenkins.getInstance().checkPermission(Jenkins.RUN_SCRIPTS);
- }
-
- private Object readResolve() {
- Jenkins.getInstance().checkPermission(Jenkins.RUN_SCRIPTS);
- return this;
- }
-
- public String getCommand() {
- return agentCommand;
- }
-
- /**
- * Gets the formatted current time stamp.
- */
- private static String getTimestamp() {
- return String.format("[%1$tD %1$tT]", new Date());
- }
-
- @Override
- public void launch(SlaveComputer computer, final TaskListener listener) {
- EnvVars _cookie = null;
- Process _proc = null;
- try {
- Slave node = computer.getNode();
- if (node == null) {
- throw new AbortException("Cannot launch commands on deleted nodes");
- }
-
- listener.getLogger().println(hudson.model.Messages.Slave_Launching(getTimestamp()));
- if(getCommand().trim().length()==0) {
- listener.getLogger().println(Messages.CommandLauncher_NoLaunchCommand());
- return;
- }
- listener.getLogger().println("$ " + getCommand());
-
- ProcessBuilder pb = new ProcessBuilder(Util.tokenize(getCommand()));
- final EnvVars cookie = _cookie = EnvVars.createCookie();
- pb.environment().putAll(cookie);
- pb.environment().put("WORKSPACE", StringUtils.defaultString(computer.getAbsoluteRemoteFs(), node.getRemoteFS())); //path for local slave log
-
- {// system defined variables
- String rootUrl = Jenkins.getInstance().getRootUrl();
- if (rootUrl!=null) {
- pb.environment().put("HUDSON_URL", rootUrl); // for backward compatibility
- pb.environment().put("JENKINS_URL", rootUrl);
- pb.environment().put("SLAVEJAR_URL", rootUrl+"/jnlpJars/agent.jar");
- pb.environment().put("AGENTJAR_URL", rootUrl+"/jnlpJars/agent.jar");
- }
- }
-
- if (env != null) {
- pb.environment().putAll(env);
- }
-
- final Process proc = _proc = pb.start();
-
- // capture error information from stderr. this will terminate itself
- // when the process is killed.
- new StreamCopyThread("stderr copier for remote agent on " + computer.getDisplayName(),
- proc.getErrorStream(), listener.getLogger()).start();
-
- computer.setChannel(proc.getInputStream(), proc.getOutputStream(), listener.getLogger(), new Channel.Listener() {
- @Override
- public void onClosed(Channel channel, IOException cause) {
- reportProcessTerminated(proc, listener);
-
- try {
- ProcessTree.get().killAll(proc, cookie);
- } catch (InterruptedException e) {
- LOGGER.log(Level.INFO, "interrupted", e);
- }
- }
- });
-
- LOGGER.info("agent launched for " + computer.getDisplayName());
- } catch (InterruptedException e) {
- Functions.printStackTrace(e, listener.error(Messages.ComputerLauncher_abortedLaunch()));
- } catch (RuntimeException e) {
- Functions.printStackTrace(e, listener.error(Messages.ComputerLauncher_unexpectedError()));
- } catch (Error e) {
- Functions.printStackTrace(e, listener.error(Messages.ComputerLauncher_unexpectedError()));
- } catch (IOException e) {
- Util.displayIOException(e, listener);
-
- String msg = Util.getWin32ErrorMessage(e);
- if (msg == null) {
- msg = "";
- } else {
- msg = " : " + msg;
- // FIXME TODO i18n what is this!?
- }
- msg = hudson.model.Messages.Slave_UnableToLaunch(computer.getDisplayName(), msg);
- LOGGER.log(Level.SEVERE, msg, e);
- Functions.printStackTrace(e, listener.error(msg));
-
- if(_proc!=null) {
- reportProcessTerminated(_proc, listener);
- try {
- ProcessTree.get().killAll(_proc, _cookie);
- } catch (InterruptedException x) {
- Functions.printStackTrace(x, listener.error(Messages.ComputerLauncher_abortedLaunch()));
- }
- }
- }
- }
-
- private static void reportProcessTerminated(Process proc, TaskListener listener) {
- try {
- int exitCode = proc.exitValue();
- listener.error("Process terminated with exit code " + exitCode);
- } catch (IllegalThreadStateException e) {
- // hasn't terminated yet
- }
- }
-
- private static final Logger LOGGER = Logger.getLogger(CommandLauncher.class.getName());
-
- @Extension @Symbol("command")
- public static class DescriptorImpl extends Descriptor {
- public String getDisplayName() {
- return Messages.CommandLauncher_displayName();
- }
-
- public FormValidation doCheckCommand(@QueryParameter String value) {
- if (!Jenkins.getInstance().hasPermission(Jenkins.RUN_SCRIPTS)) {
- return FormValidation.warning(Messages.CommandLauncher_cannot_be_configured_by_non_administrato());
- }
- if(Util.fixEmptyAndTrim(value)==null)
- return FormValidation.error(Messages.CommandLauncher_NoLaunchCommand());
- else
- return FormValidation.ok();
- }
- }
-}
diff --git a/core/src/main/java/hudson/tools/JDKInstaller.java b/core/src/main/java/hudson/tools/JDKInstaller.java
index bb316fc104..6b4eaa98a3 100644
--- a/core/src/main/java/hudson/tools/JDKInstaller.java
+++ b/core/src/main/java/hudson/tools/JDKInstaller.java
@@ -48,6 +48,7 @@ import net.sf.json.JsonConfig;
import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethodBase;
+import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;
@@ -476,10 +477,29 @@ public class JDKInstaller extends ToolInstaller {
throw new IOException("Failed to request " + m.getURI() +" exit code="+r);
if (m.getURI().getHost().equals("login.oracle.com")) {
+ /* Login flow:
+ * 1. /oaam_server/oamLoginPage.jsp: Form for username + password. Submit action is:
+ * 2. /oaam_server/login.do: Returns a 302 to:
+ * 3. /oaam_server/loginAuth.do: After 2 seconds, JS sets window.location to:
+ * 4. /oaam_server/authJump.do: Contains a single form with hidden inputs and JS that submits the form to:
+ * 5. /oam/server/dap/cred_submit: Returns a 302 to:
+ * 6. https://edelivery.oracle.com/osso_login_success: Returns a 302 to the download.
+ */
+ if (m.getURI().getPath().contains("/loginAuth.do")) { // You are redirected to this page immediately after logging in.
+ try {
+ Thread.sleep(2000); // Oracle website waits 2 seconds after logging in before redirecting.
+ m.releaseConnection();
+ m = new GetMethod(new URI(m.getURI(), "/oaam_server/authJump.do?jump=false", true).toString());
+ continue;
+ } catch (InterruptedException x) {
+ throw new IOException("Interrupted while logging in", x);
+ }
+ }
+
LOGGER.fine("Appears to be a login page");
String resp = IOUtils.toString(m.getResponseBodyAsStream(), m.getResponseCharSet());
m.releaseConnection();
- Matcher pm = Pattern.compile("