public void setChannel()

in core/src/main/java/hudson/slaves/SlaveComputer.java [608:728]


    public void setChannel(@NonNull Channel channel,
                           @CheckForNull OutputStream launchLog,
                           @CheckForNull Channel.Listener listener) throws IOException, InterruptedException {
        if(this.channel!=null)
            throw new IllegalStateException("Already connected");

        final TaskListener taskListener = launchLog != null ? new StreamTaskListener(launchLog) : TaskListener.NULL;
        PrintStream log = taskListener.getLogger();

        channel.setProperty(SlaveComputer.class, this);

        channel.addListener(new LoggingChannelListener(logger, Level.FINEST) {
            @Override
            public void onClosed(Channel c, IOException cause) {
                // Orderly shutdown will have null exception
                if (cause!=null) {
                    offlineCause = new ChannelTermination(cause);
                    Functions.printStackTrace(cause, taskListener.error("Connection terminated"));
                } else {
                    taskListener.getLogger().println("Connection terminated");
                }
                closeChannel();
                try {
                    launcher.afterDisconnect(SlaveComputer.this, taskListener);
                } catch (Throwable t) {
                    LogRecord lr = new LogRecord(Level.SEVERE,
                            "Launcher {0}'s afterDisconnect method propagated an exception when {1}'s connection was closed: {2}");
                    lr.setThrown(t);
                    lr.setParameters(new Object[]{launcher, SlaveComputer.this.getName(), t.getMessage()});
                    logger.log(lr);
                }
            }
        });
        if(listener!=null)
            channel.addListener(listener);

        String slaveVersion = channel.call(new SlaveVersion());
        log.println("Remoting version: " + slaveVersion);
        VersionNumber agentVersion = new VersionNumber(slaveVersion);
        if (agentVersion.isOlderThan(RemotingVersionInfo.getMinimumSupportedVersion())) {
            log.println(String.format("WARNING: Remoting version is older than a minimum required one (%s). " +
                    "Connection will not be rejected, but the compatibility is NOT guaranteed",
                    RemotingVersionInfo.getMinimumSupportedVersion()));
        }

        boolean _isUnix = channel.call(new DetectOS());
        log.println(_isUnix? hudson.model.Messages.Slave_UnixSlave():hudson.model.Messages.Slave_WindowsSlave());

        String defaultCharsetName = channel.call(new DetectDefaultCharset());

        Slave node = getNode();
        if (node == null) { // Node has been disabled/removed during the connection
            throw new IOException("Node "+nodeName+" has been deleted during the channel setup");
        }

        String remoteFS = node.getRemoteFS();
        if (Util.isRelativePath(remoteFS)) {
            remoteFS = channel.call(new AbsolutePath(remoteFS));
            log.println("NOTE: Relative remote path resolved to: "+remoteFS);
        }
        if(_isUnix && !remoteFS.contains("/") && remoteFS.contains("\\"))
            log.println("WARNING: "+remoteFS
                    +" looks suspiciously like Windows path. Maybe you meant "+remoteFS.replace('\\','/')+"?");
        FilePath root = new FilePath(channel,remoteFS);

        // reference counting problem is known to happen, such as JENKINS-9017, and so as a preventive measure
        // we pin the base classloader so that it'll never get GCed. When this classloader gets released,
        // it'll have a catastrophic impact on the communication.
        channel.pinClassLoader(getClass().getClassLoader());

        channel.call(new SlaveInitializer(DEFAULT_RING_BUFFER_SIZE));
        try (ACLContext ctx = ACL.as(ACL.SYSTEM)) {
            for (ComputerListener cl : ComputerListener.all()) {
                cl.preOnline(this,channel,root,taskListener);
            }
        }

        offlineCause = null;

        // update the data structure atomically to prevent others from seeing a channel that's not properly initialized yet
        synchronized(channelLock) {
            if(this.channel!=null) {
                // check again. we used to have this entire method in a big synchronization block,
                // but Channel constructor blocks for an external process to do the connection
                // if CommandLauncher is used, and that cannot be interrupted because it blocks at InputStream.
                // so if the process hangs, it hangs the thread in a lock, and since Hudson will try to relaunch,
                // we'll end up queuing the lot of threads in a pseudo deadlock.
                // This implementation prevents that by avoiding a lock. HUDSON-1705 is likely a manifestation of this.
                channel.close();
                throw new IllegalStateException("Already connected");
            }
            isUnix = _isUnix;
            numRetryAttempt = 0;
            this.channel = channel;
            this.absoluteRemoteFs = remoteFS;
            defaultCharset = Charset.forName(defaultCharsetName);

            synchronized (statusChangeLock) {
                statusChangeLock.notifyAll();
            }
        }
        try (ACLContext ctx = ACL.as(ACL.SYSTEM)) {
            for (ComputerListener cl : ComputerListener.all()) {
                try {
                    cl.onOnline(this,taskListener);
                } catch (Exception e) {
                    // Per Javadoc log exceptions but still go online.
                    // NOTE: this does not include Errors, which indicate a fatal problem
                    taskListener.getLogger().format(
                        "onOnline: %s reported an exception: %s%n",
                        cl.getClass(),
                        e.toString());
                } catch (Throwable e) {
                    closeChannel();
                    throw e;
                }
            }
        }
        log.println("Agent successfully connected and online");
        Jenkins.get().getQueue().scheduleMaintenance();
    }