diff --git a/changelog.html b/changelog.html index 850a487c6e3a..e02f7fff9b0b 100644 --- a/changelog.html +++ b/changelog.html @@ -55,7 +55,14 @@ diff --git a/cli/src/main/java/hudson/cli/CLI.java b/cli/src/main/java/hudson/cli/CLI.java index f24549f8c5f0..bf75736560e5 100644 --- a/cli/src/main/java/hudson/cli/CLI.java +++ b/cli/src/main/java/hudson/cli/CLI.java @@ -29,7 +29,7 @@ import hudson.remoting.Pipe; import hudson.remoting.RemoteInputStream; import hudson.remoting.RemoteOutputStream; -import hudson.remoting.SocketInputStream; +import hudson.remoting.SocketChannelStream; import hudson.remoting.SocketOutputStream; import javax.crypto.SecretKey; @@ -201,7 +201,7 @@ public void close() throws IOException { } else { s = new Socket(); s.connect(clip.endpoint,3000); - out = new SocketOutputStream(s); + out = SocketChannelStream.out(s); } closables.add(new Closeable() { @@ -210,7 +210,7 @@ public void close() throws IOException { } }); - Connection c = new Connection(new SocketInputStream(s),out); + Connection c = new Connection(SocketChannelStream.in(s),out); switch (clip.version) { case 1: diff --git a/cli/src/main/java/hudson/cli/Connection.java b/cli/src/main/java/hudson/cli/Connection.java index 8f35276cb7af..165a6deb7e6a 100644 --- a/cli/src/main/java/hudson/cli/Connection.java +++ b/cli/src/main/java/hudson/cli/Connection.java @@ -23,8 +23,7 @@ */ package hudson.cli; -import hudson.remoting.SocketInputStream; -import hudson.remoting.SocketOutputStream; +import hudson.remoting.SocketChannelStream; import org.apache.commons.codec.binary.Base64; import javax.crypto.Cipher; @@ -63,7 +62,7 @@ public class Connection { public final DataOutputStream dout; public Connection(Socket socket) throws IOException { - this(new SocketInputStream(socket),new SocketOutputStream(socket)); + this(SocketChannelStream.in(socket),SocketChannelStream.out(socket)); } public Connection(InputStream in, OutputStream out) { diff --git a/core/src/main/java/hudson/Launcher.java b/core/src/main/java/hudson/Launcher.java index 6822a5c8ca71..39d1a56b1371 100644 --- a/core/src/main/java/hudson/Launcher.java +++ b/core/src/main/java/hudson/Launcher.java @@ -818,7 +818,7 @@ public Channel launchChannel(OutputStream out, ProcessBuilder pb) throws IOExcep * Kill the process when the channel is severed. */ @Override - protected synchronized void terminate(IOException e) { + public synchronized void terminate(IOException e) { super.terminate(e); ProcessTree pt = ProcessTree.get(); try { diff --git a/core/src/main/java/hudson/TcpSlaveAgentListener.java b/core/src/main/java/hudson/TcpSlaveAgentListener.java index 2c5f4f916c62..b93a9f115b90 100644 --- a/core/src/main/java/hudson/TcpSlaveAgentListener.java +++ b/core/src/main/java/hudson/TcpSlaveAgentListener.java @@ -32,8 +32,10 @@ import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.BindException; +import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; +import java.nio.channels.ServerSocketChannel; import java.util.logging.Level; import java.util.logging.Logger; @@ -55,7 +57,7 @@ */ public final class TcpSlaveAgentListener extends Thread { - private final ServerSocket serverSocket; + private final ServerSocketChannel serverSocket; private volatile boolean shuttingDown; public final int configuredPort; @@ -67,7 +69,8 @@ public final class TcpSlaveAgentListener extends Thread { public TcpSlaveAgentListener(int port) throws IOException { super("TCP slave agent listener port="+port); try { - serverSocket = new ServerSocket(port); + serverSocket = ServerSocketChannel.open(); + serverSocket.socket().bind(new InetSocketAddress(port)); } catch (BindException e) { throw (BindException)new BindException("Failed to listen on port "+port+" because it's already in use.").initCause(e); } @@ -82,7 +85,7 @@ public TcpSlaveAgentListener(int port) throws IOException { * Gets the TCP port number in which we are listening. */ public int getPort() { - return serverSocket.getLocalPort(); + return serverSocket.socket().getLocalPort(); } @Override @@ -90,7 +93,7 @@ public void run() { try { // the loop eventually terminates when the socket is closed. while (true) { - Socket s = serverSocket.accept(); + Socket s = serverSocket.accept().socket(); // this prevents a connection from silently terminated by the router in between or the other peer // and that goes without unnoticed. However, the time out is often very long (for example 2 hours diff --git a/core/src/main/java/hudson/cli/CliProtocol.java b/core/src/main/java/hudson/cli/CliProtocol.java index b3b725fe33ff..1d4f25627e07 100644 --- a/core/src/main/java/hudson/cli/CliProtocol.java +++ b/core/src/main/java/hudson/cli/CliProtocol.java @@ -4,9 +4,13 @@ import hudson.model.Computer; import hudson.remoting.Channel; import hudson.remoting.Channel.Mode; +import hudson.remoting.ChannelBuilder; import jenkins.AgentProtocol; import jenkins.model.Jenkins; +import jenkins.slaves.NioChannelSelector; +import org.jenkinsci.remoting.nio.NioChannelHub; +import javax.inject.Inject; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedWriter; @@ -23,6 +27,9 @@ */ @Extension public class CliProtocol extends AgentProtocol { + @Inject + NioChannelSelector nio; + @Override public String getName() { return "CLI-connect"; @@ -30,13 +37,23 @@ public String getName() { @Override public void handle(Socket socket) throws IOException, InterruptedException { - new Handler(socket).run(); + new Handler(nio.getHub(),socket).run(); } protected static class Handler { + protected final NioChannelHub hub; protected final Socket socket; + /** + * @deprecated as of 1.559 + * Use {@link #Handler(NioChannelHub, Socket)} + */ public Handler(Socket socket) { + this(null,socket); + } + + public Handler(NioChannelHub hub, Socket socket) { + this.hub = hub; this.socket = socket; } @@ -47,9 +64,21 @@ public void run() throws IOException, InterruptedException { } protected void runCli(Connection c) throws IOException, InterruptedException { - Channel channel = new Channel("CLI channel from " + socket.getInetAddress(), - Computer.threadPoolForRemoting, Mode.BINARY, - new BufferedInputStream(c.in), new BufferedOutputStream(c.out), null, true, Jenkins.getInstance().pluginManager.uberClassLoader); + ChannelBuilder cb; + String name = "CLI channel from " + socket.getInetAddress(); + + // Connection can contain cipher wrapper, which can't be NIO-ed. +// if (hub!=null) +// cb = hub.newChannelBuilder(name, Computer.threadPoolForRemoting); +// else + cb = new ChannelBuilder(name, Computer.threadPoolForRemoting); + + Channel channel = cb + .withMode(Mode.BINARY) + .withRestricted(true) + .withBaseLoader(Jenkins.getInstance().pluginManager.uberClassLoader) + .build(new BufferedInputStream(c.in), new BufferedOutputStream(c.out)); + channel.setProperty(CliEntryPoint.class.getName(),new CliManagerImpl(channel)); channel.join(); } diff --git a/core/src/main/java/hudson/cli/CliProtocol2.java b/core/src/main/java/hudson/cli/CliProtocol2.java index bd3af1ee6497..644914a089bd 100644 --- a/core/src/main/java/hudson/cli/CliProtocol2.java +++ b/core/src/main/java/hudson/cli/CliProtocol2.java @@ -2,6 +2,7 @@ import hudson.Extension; import jenkins.model.Jenkins; +import org.jenkinsci.remoting.nio.NioChannelHub; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; @@ -28,14 +29,22 @@ public String getName() { @Override public void handle(Socket socket) throws IOException, InterruptedException { - new Handler2(socket).run(); + new Handler2(nio.getHub(), socket).run(); } protected static class Handler2 extends Handler { + /** + * @deprecated as of 1.559 + * Use {@link #Handler2(NioChannelHub, Socket)} + */ public Handler2(Socket socket) { super(socket); } + public Handler2(NioChannelHub hub, Socket socket) { + super(hub, socket); + } + @Override public void run() throws IOException, InterruptedException { try { diff --git a/core/src/main/java/hudson/model/AbstractBuild.java b/core/src/main/java/hudson/model/AbstractBuild.java index 963bd0736289..164bd285e9c7 100644 --- a/core/src/main/java/hudson/model/AbstractBuild.java +++ b/core/src/main/java/hudson/model/AbstractBuild.java @@ -71,7 +71,6 @@ import java.io.InterruptedIOException; import java.io.StringWriter; import java.lang.ref.WeakReference; -import java.text.MessageFormat; import java.util.AbstractSet; import java.util.ArrayList; import java.util.Calendar; @@ -156,7 +155,11 @@ public abstract class AbstractBuild

,R extends Abs */ protected transient List buildEnvironments; - private transient LazyBuildMixIn.RunMixIn runMixIn; + private transient final LazyBuildMixIn.RunMixIn runMixIn = new LazyBuildMixIn.RunMixIn() { + @Override protected R asRun() { + return _this(); + } + }; protected AbstractBuild(P job) throws IOException { super(job); @@ -174,14 +177,7 @@ public final P getProject() { return getParent(); } - @Override public final synchronized LazyBuildMixIn.RunMixIn getRunMixIn() { - if (runMixIn == null) { - runMixIn = new LazyBuildMixIn.RunMixIn() { - @Override protected R asRun() { - return _this(); - } - }; - } + @Override public final LazyBuildMixIn.RunMixIn getRunMixIn() { return runMixIn; } diff --git a/core/src/main/java/hudson/model/ItemGroupMixIn.java b/core/src/main/java/hudson/model/ItemGroupMixIn.java index 311afa572da8..131738ed18f9 100644 --- a/core/src/main/java/hudson/model/ItemGroupMixIn.java +++ b/core/src/main/java/hudson/model/ItemGroupMixIn.java @@ -108,6 +108,7 @@ public boolean accept(File child) { item = (V) Items.load( parent, subdir ); }else{ Logger.getLogger( ItemGroupMixIn.class.getName() ).log( Level.WARNING, "could not find file " + xmlFile.getFile()); + continue; } } else { item.onLoad(parent, subdir.getName()); diff --git a/core/src/main/java/hudson/model/ReconfigurableDescribable.java b/core/src/main/java/hudson/model/ReconfigurableDescribable.java index 7738089b1d8b..e9dd8c2f6c01 100644 --- a/core/src/main/java/hudson/model/ReconfigurableDescribable.java +++ b/core/src/main/java/hudson/model/ReconfigurableDescribable.java @@ -25,6 +25,8 @@ import hudson.model.Descriptor.FormException; import hudson.slaves.NodeProperty; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; import net.sf.json.JSONObject; import org.kohsuke.stapler.StaplerRequest; @@ -77,5 +79,5 @@ public interface ReconfigurableDescribable clazz) { protected ViewDescriptor() { } - @Deprecated - public AutoCompletionCandidates doAutoCompleteCopyNewItemFrom(final String prefix) { - final AutoCompletionCandidates r = new AutoCompletionCandidates(); - - new ItemVisitor() { - @Override - public void onItemGroup(ItemGroup group) { - // only dig deep when the path matches what's typed. - // for example, if 'foo/bar' is typed, we want to show 'foo/barcode' - if (prefix.startsWith(group.getFullName())) - super.onItemGroup(group); - } - - @Override - public void onItem(Item i) { - if (i.getFullName().startsWith(prefix)) { - r.add((i.getFullName())); - super.onItem(i); - } - } - }.onItemGroup(Jenkins.getInstance()); - - return r; - } - /** * Auto-completion for the "copy from" field in the new job page. - * @since 1.553 */ + @Restricted(DoNotUse.class) public AutoCompletionCandidates doAutoCompleteCopyNewItemFrom(@QueryParameter final String value, @AncestorInPath ItemGroup container) { return AutoCompletionCandidates.ofJobNames(TopLevelItem.class, value, container); } diff --git a/core/src/main/java/hudson/model/listeners/RunListener.java b/core/src/main/java/hudson/model/listeners/RunListener.java index 8e5a5ad4858c..4489e5dc7717 100644 --- a/core/src/main/java/hudson/model/listeners/RunListener.java +++ b/core/src/main/java/hudson/model/listeners/RunListener.java @@ -235,7 +235,7 @@ public static void fireFinalized(Run r) { } /** - * Fires the {@link #onFinalized(Run)} event. + * Fires the {@link #onDeleted} event. */ public static void fireDeleted(Run r) { for (RunListener l : all()) { diff --git a/core/src/main/java/hudson/slaves/Channels.java b/core/src/main/java/hudson/slaves/Channels.java index 09214dc9a46d..a8d26aa444f1 100644 --- a/core/src/main/java/hudson/slaves/Channels.java +++ b/core/src/main/java/hudson/slaves/Channels.java @@ -30,8 +30,7 @@ import hudson.model.TaskListener; import hudson.remoting.Channel; import hudson.remoting.Launcher; -import hudson.remoting.SocketInputStream; -import hudson.remoting.SocketOutputStream; +import hudson.remoting.SocketChannelStream; import hudson.util.ClasspathBuilder; import hudson.util.JVMBuilder; import hudson.util.StreamCopyThread; @@ -73,7 +72,7 @@ public static Channel forProcess(String name, ExecutorService execService, Input * Kill the process when the channel is severed. */ @Override - protected synchronized void terminate(IOException e) { + public synchronized void terminate(IOException e) { super.terminate(e); try { proc.kill(); @@ -109,7 +108,7 @@ public static Channel forProcess(String name, ExecutorService execService, final * Kill the process when the channel is severed. */ @Override - protected synchronized void terminate(IOException e) { + public synchronized void terminate(IOException e) { super.terminate(e); proc.destroy(); // the stderr copier should exit by itself @@ -205,8 +204,8 @@ public static Channel newJVM(String displayName, TaskListener listener, JVMBuild serverSocket.close(); return forProcess("Channel to "+displayName, Computer.threadPoolForRemoting, - new BufferedInputStream(new SocketInputStream(s)), - new BufferedOutputStream(new SocketOutputStream(s)),null,p); + new BufferedInputStream(SocketChannelStream.in(s)), + new BufferedOutputStream(SocketChannelStream.out(s)),null,p); } diff --git a/core/src/main/java/jenkins/model/ParameterizedJobMixIn.java b/core/src/main/java/jenkins/model/ParameterizedJobMixIn.java index 5fe9bdb588bf..c417c1ba914f 100644 --- a/core/src/main/java/jenkins/model/ParameterizedJobMixIn.java +++ b/core/src/main/java/jenkins/model/ParameterizedJobMixIn.java @@ -75,12 +75,12 @@ public abstract class ParameterizedJobMixIn & Param /** @see BuildableItem#scheduleBuild() */ @SuppressWarnings("deprecation") public final boolean scheduleBuild() { - return scheduleBuild(Jenkins.getInstance().getQuietPeriod(), new Cause.LegacyCodeCause()); + return scheduleBuild(asJob().getQuietPeriod(), new Cause.LegacyCodeCause()); } /** @see BuildableItem#scheduleBuild(Cause) */ public final boolean scheduleBuild(Cause c) { - return scheduleBuild(Jenkins.getInstance().getQuietPeriod(), c); + return scheduleBuild(asJob().getQuietPeriod(), c); } /** @see BuildableItem#scheduleBuild(int) */ diff --git a/core/src/main/java/jenkins/model/lazy/LazyBuildMixIn.java b/core/src/main/java/jenkins/model/lazy/LazyBuildMixIn.java index 4538ef6f9e83..ea24e8d6765b 100644 --- a/core/src/main/java/jenkins/model/lazy/LazyBuildMixIn.java +++ b/core/src/main/java/jenkins/model/lazy/LazyBuildMixIn.java @@ -280,7 +280,7 @@ public interface LazyLoadingRun & Queue.Task & LazyB /** * Accompanying helper for the run type. - * Stateful but should be held in a {@code transient} field. + * Stateful but should be held in a {@code transient final} field. */ public static abstract class RunMixIn & Queue.Task & LazyBuildMixIn.LazyLoadingJob, RunT extends Run & LazyLoadingRun> { diff --git a/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol.java b/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol.java index 088fc1ecb387..24a1c6d6b649 100644 --- a/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol.java +++ b/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol.java @@ -2,18 +2,18 @@ import hudson.AbortException; import hudson.Extension; +import hudson.model.Computer; import hudson.remoting.Channel; import hudson.remoting.Channel.Listener; +import hudson.remoting.ChannelBuilder; import hudson.remoting.Engine; -import hudson.remoting.SocketInputStream; -import hudson.remoting.SocketOutputStream; import hudson.slaves.SlaveComputer; import jenkins.AgentProtocol; import jenkins.model.Jenkins; import jenkins.security.HMACConfidentialKey; +import org.jenkinsci.remoting.nio.NioChannelHub; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; +import javax.inject.Inject; import java.io.BufferedWriter; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -55,6 +55,9 @@ */ @Extension public class JnlpSlaveAgentProtocol extends AgentProtocol { + @Inject + NioChannelSelector hub; + @Override public String getName() { return "JNLP-connect"; @@ -62,10 +65,11 @@ public String getName() { @Override public void handle(Socket socket) throws IOException, InterruptedException { - new Handler(socket).run(); + new Handler(hub.getHub(),socket).run(); } protected static class Handler { + protected final NioChannelHub hub; protected final Socket socket; /** @@ -82,7 +86,16 @@ protected static class Handler { */ protected final PrintWriter out; + /** + * @deprecated as of 1.559 + * Use {@link #Handler(NioChannelHub, Socket)} + */ public Handler(Socket socket) throws IOException { + this(null,socket); + } + + public Handler(NioChannelHub hub, Socket socket) throws IOException { + this.hub = hub; this.socket = socket; in = new DataInputStream(socket.getInputStream()); out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(),"UTF-8")),true); @@ -121,7 +134,14 @@ protected Channel jnlpConnect(SlaveComputer computer) throws InterruptedExceptio logw.println("JNLP agent connected from "+ socket.getInetAddress()); try { - computer.setChannel(new BufferedInputStream(new SocketInputStream(socket)), new BufferedOutputStream(new SocketOutputStream(socket)), log, + ChannelBuilder cb; + + if (hub==null) + cb = new ChannelBuilder(nodeName, Computer.threadPoolForRemoting); + else + cb = hub.newChannelBuilder(nodeName, Computer.threadPoolForRemoting); + + computer.setChannel(cb.withHeaderStream(log).build(socket), log, new Listener() { @Override public void onClosed(Channel channel, IOException cause) { diff --git a/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol2.java b/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol2.java index ab6239dbe168..354b94b10324 100644 --- a/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol2.java +++ b/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol2.java @@ -7,6 +7,7 @@ import hudson.remoting.Engine; import hudson.slaves.SlaveComputer; import jenkins.model.Jenkins; +import org.jenkinsci.remoting.nio.NioChannelHub; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -40,14 +41,22 @@ public String getName() { @Override public void handle(Socket socket) throws IOException, InterruptedException { - new Handler2(socket).run(); + new Handler2(hub.getHub(),socket).run(); } protected static class Handler2 extends Handler { + /** + * @deprecated as of 1.559 + * Use {@link #Handler2(NioChannelHub, Socket)} + */ public Handler2(Socket socket) throws IOException { super(socket); } + public Handler2(NioChannelHub hub, Socket socket) throws IOException { + super(hub, socket); + } + /** * Handles JNLP slave agent connection request (v2 protocol) */ diff --git a/core/src/main/java/jenkins/slaves/NioChannelSelector.java b/core/src/main/java/jenkins/slaves/NioChannelSelector.java new file mode 100644 index 000000000000..3aea28a1e32d --- /dev/null +++ b/core/src/main/java/jenkins/slaves/NioChannelSelector.java @@ -0,0 +1,43 @@ +package jenkins.slaves; + +import hudson.Extension; +import hudson.model.Computer; +import org.jenkinsci.remoting.nio.NioChannelHub; + +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Singleton holder of {@link NioChannelHub} + * + * @author Kohsuke Kawaguchi + */ +@Extension +public class NioChannelSelector { + private NioChannelHub hub; + + public NioChannelSelector() { + try { + if (!DISABLED) { + this.hub = new NioChannelHub(Computer.threadPoolForRemoting); + Computer.threadPoolForRemoting.submit(hub); + } + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Failed to launch NIO hub",e); + this.hub = null; + DISABLED = true; + } + } + + public NioChannelHub getHub() { + return hub; + } + + /** + * Escape hatch to disable use of NIO. + */ + public static boolean DISABLED = Boolean.getBoolean(NioChannelSelector.class.getName()+".disabled"); + + private static final Logger LOGGER = Logger.getLogger(NioChannelSelector.class.getName()); +} diff --git a/pom.xml b/pom.xml index 5fe4ab328851..1aaa276929ee 100644 --- a/pom.xml +++ b/pom.xml @@ -172,7 +172,7 @@ THE SOFTWARE. org.jenkins-ci.main remoting - 2.37 + 2.39 diff --git a/war/src/main/webapp/WEB-INF/web.xml b/war/src/main/webapp/WEB-INF/web.xml index aa14977b414f..e25949906fb7 100644 --- a/war/src/main/webapp/WEB-INF/web.xml +++ b/war/src/main/webapp/WEB-INF/web.xml @@ -160,6 +160,14 @@ THE SOFTWARE. * + + + + other + /* + + + FORM diff --git a/war/src/main/webapp/scripts/hudson-behavior.js b/war/src/main/webapp/scripts/hudson-behavior.js index bec413dd153f..06f06744752b 100644 --- a/war/src/main/webapp/scripts/hudson-behavior.js +++ b/war/src/main/webapp/scripts/hudson-behavior.js @@ -1814,6 +1814,7 @@ function buildFormTree(form) { } var p; + var r; var type = e.getAttribute("type"); if(type==null) type=""; switch(type.toLowerCase()) { @@ -1861,15 +1862,17 @@ function buildFormTree(form) { break; case "radio": if(!e.checked) break; - while (e.name.substring(0,8)=='removeme') - e.name = e.name.substring(e.name.indexOf('_',8)+1); + r=0; + while (e.name.substring(r,r+8)=='removeme') + r = e.name.indexOf('_',r+8)+1; + p = findParent(e); if(e.groupingNode) { - p = findParent(e); - addProperty(p, e.name, e.formDom = { value: e.value }); - break; + addProperty(p, e.name.substring(r), e.formDom = { value: e.value }); + } else { + addProperty(p, e.name.substring(r), e.value); } + break; - // otherwise fall through default: p = findParent(e); addProperty(p, e.name, e.value);