Class Channel

  • All Implemented Interfaces:
    VirtualChannel, Closeable, AutoCloseable

    public class Channel
    extends Object
    implements VirtualChannel, Closeable
    Represents a communication channel to the remote peer.

    A Channel is a mechanism for two JVMs to communicate over bi-directional InputStream/OutputStream pair. Channel represents an endpoint of the stream, and thus two Channels are always used in a pair.

    Communication is established as soon as two Channel instances are created at the end fo the stream pair until the stream is terminated via close().

    The basic unit of remoting is an executable Callable object. An application can create a Callable object, and execute it remotely by using the call(Callable) method or callAsync(Callable) method.

    In this sense, Channel is a mechanism to delegate/offload computation to other JVMs and somewhat like an agent system. This is bit different from remoting technologies like CORBA or web services, where the server exposes a certain functionality that clients invoke.

    Callable object, as well as the return value / exceptions, are transported by using Java serialization. All the necessary class files are also shipped over Channel on-demand, so there's no need to pre-deploy such classes on both JVMs.

    Implementor's Note

    Channel builds its features in a layered model. Its higher-layer features are built on top of its lower-layer features, and they are called layer-0, layer-1, etc.

    • Layer 0: See Command for more details. This is for higher-level features, and not likely useful for applications directly.
    • Layer 1: See Request for more details. This is for higher-level features, and not likely useful for applications directly.
    Author:
    Kohsuke Kawaguchi
    • Field Detail

      • executor

        @Restricted(org.kohsuke.accmod.restrictions.NoExternalUse.class)
        public final ExecutorService executor
      • classLoadingTime

        public final AtomicLong classLoadingTime
        Total number of nanoseconds spent for remote class loading.

        Remote code execution often results in classloading activity (more precisely, when the remote peer requests some computation on this channel, this channel often has to load necessary classes from the remote peer.)

        This counter represents the total amount of time this channel had to spend loading classes from the remote peer. The time measurement doesn't include the time locally spent to actually define the class (as the local classloading would have incurred the same cost.)

      • classLoadingCount

        public final AtomicInteger classLoadingCount
        Total counts of remote classloading activities. Used in a pair with classLoadingTime.
      • classLoadingPrefetchCacheCount

        public final AtomicInteger classLoadingPrefetchCacheCount
        Prefetch cache hits. Out of all the counts in classLoadingCount, how many times were we able to resolve them by ourselves, saving a remote roundtrip call?
        Since:
        2.24
      • resourceLoadingTime

        public final AtomicLong resourceLoadingTime
        Total number of nanoseconds spent for remote resource loading.
        See Also:
        classLoadingTime
      • remoteCapability

        public final Capability remoteCapability
        Capability of the remote Channel.
      • PIPE_WINDOW_SIZE

        public static final int PIPE_WINDOW_SIZE
        Default pipe window size.

        This controls the amount of bytes that can be in flight. Value too small would fail to efficiently utilize a high-latency/large-bandwidth network, but a value too large would cause the risk of a large memory consumption when a pipe clogs (that is, the receiver isn't consuming bytes we are sending fast enough.)

        If we have a gigabit ethernet (with effective transfer rate of 100M bps) and 20ms latency, the pipe will hold (100M bits/sec * 0.02sec / 8 bits/byte = 0.25MB. So 1MB or so is big enough for most network, and hopefully this is an acceptable enough memory consumption in case of clogging.

        See Also:
        PipeWindow
    • Method Detail

      • isOutClosed

        public boolean isOutClosed()
        Is the sender side of the transport already closed?
      • getSenderCloseCause

        @CheckForNull
        public final Throwable getSenderCloseCause()
        Get why the sender side of the channel has been closed.
        Returns:
        Close cause or null if the sender side is active. null result does not guarantee that the channel is actually operational.
        Since:
        3.11
      • isClosingOrClosed

        public boolean isClosingOrClosed()
        Returns true if the channel is either in the process of closing down or has closed down. If the result is true, it means that the channel will be closed at some point by Remoting, and that it makes no sense to send any new UserRequests to the remote side. Invocations like call(hudson.remoting.Callable) and callAsync(hudson.remoting.Callable) will just fail as well.
        Since:
        2.33
      • getCloseRequestCause

        @CheckForNull
        public Throwable getCloseRequestCause()
        Gets cause of the close request.
        Returns:
        outClosed if not null, value of the transient cache closeRequestCause otherwise. The latter one may show random cause in the case of race conditions.
        Since:
        3.11
      • export

        public <T> T export​(Class<T> type,
                            T instance)
        Exports an object for remoting to the other Channel by creating a remotable proxy. The returned reference must be kept if there is ongoing operation on the remote side. Once it is released, the exported object will be deallocated as well. Please keep in mind that the object may be also released earlier than expected by JVM (e.g. see JENKINS-23271).
        Specified by:
        export in interface VirtualChannel
        Type Parameters:
        T - Type
        Parameters:
        type - Interface to be remoted.
        instance - Instance to be exported. null instances won't be exported to the remote instance.

        All the parameters and return values must be serializable.

        Returns:
        the proxy object that implements T. This object can be transferred to the other Channel, and calling methods on it from the remote side will invoke the same method on the given local instance object. null if the input instance is null.
      • pin

        public void pin​(@NonNull
                        Object instance)
        Increase reference count so much to effectively prevent de-allocation.
        Parameters:
        instance - Instance to be pinned
      • pinClassLoader

        public void pinClassLoader​(ClassLoader cl)
        Pin down the exported classloader.
      • preloadJar

        public boolean preloadJar​(Callable<?,​?> classLoaderRef,
                                  Class<?>... classesInJar)
                           throws IOException,
                                  InterruptedException
        Preloads jar files on the remote side.

        This is a performance improvement method that can be safely ignored if your goal is just to make things working.

        Normally, classes are transferred over the network one at a time, on-demand. This design is mainly driven by how Java classloading works — we can't predict what classes will be necessarily upfront very easily.

        Classes are loaded only once, so for long-running Channel, this is normally an acceptable overhead. But sometimes, for example when a channel is short-lived, or when you know that you'll need a majority of classes in certain jar files, then it is more efficient to send a whole jar file over the network upfront and thereby avoiding individual class transfer over the network.

        That is what this method does. It ensures that a series of jar files are copied to the remote side (AKA "preloading.") Classloading will consult the preloaded jars before performing network transfer of class files.

        Beware that this method is not useful in all configurations. If a RemoteClassLoader has another RemoteClassLoader as a parent, which would be typical, then preloading a JAR in it will not reduce network round-trips: each class load still has to call loadClass on the parent, which will wind up checking the remote side just to get a negative answer.

        Parameters:
        classLoaderRef - This parameter is used to identify the remote classloader that will prefetch the specified jar files. That is, prefetching will ensure that prefetched jars will kick in when this Callable object is actually executed remote side.

        RemoteClassLoaders are created wisely, one per local ClassLoader, so this parameter doesn't have to be exactly the same Callable to be executed later — it just has to be of the same class.

        classesInJar - Class objects that identify jar files to be preloaded. Jar files that contain the specified classes will be preloaded into the remote peer. You just need to specify one class per one jar.
        Returns:
        true if the preloading actually happened. false if all the jars are already preloaded. This method is implemented in such a way that unnecessary jar file transfer will be avoided, and the return value will tell you if this optimization kicked in. Under normal circumstances your program shouldn't depend on this return value. It's just a hint.
        Throws:
        IOException - if the preloading fails.
        InterruptedException
      • getJarCache

        @CheckForNull
        public JarCache getJarCache()
        If this channel is built with jar file caching, return the object that manages this cache.
        Returns:
        JAR Cache object. null if JAR caching is disabled
        Since:
        2.24, 3.10 JAR Cache is Nonnull, 3.12 JAR Cache made nullable again due to JENKINS-45755
      • setJarCache

        public void setJarCache​(@NonNull
                                JarCache jarCache)
        You can change the JarCache while the channel is in operation, but doing so doesn't impact RemoteClassLoaders that are already created. So to best avoid performance loss due to race condition, please set a JarCache in the constructor, unless your call sequence guarantees that you call this method before remote classes are loaded.
        Parameters:
        jarCache - New JAR Cache to be used. Cannot be null, JAR Cache disabling on a running channel is not supported.
        Since:
        2.24
      • call

        public <V,​T extends Throwable> V call​(Callable<V,​T> callable)
                                             throws IOException,
                                                    T extends Throwable,
                                                    InterruptedException
        Makes a remote procedure call.

        Sends Callable to the remote system, executes it, and returns its result. Such calls will be considered as user-space requests. If the channel cannot execute the requests (e.g. when it is being closed), the operations may be rejected even if the channel is still active.

        Specified by:
        call in interface VirtualChannel
        Parameters:
        callable - Callable to be executed
        Throws:
        IOException - If there's any error in the communication between Channels.
        T - User exception defined by the callable
        InterruptedException - If the current thread is interrupted while waiting for the completion.
        T extends Throwable
      • callAsync

        public <V,​T extends ThrowableFuture<V> callAsync​(Callable<V,​T> callable)
                                                          throws IOException
        Makes an asynchronous remote procedure call.

        Similar to VirtualChannel.call(Callable) but returns immediately. The result of the Callable can be obtained through the Future object. Such calls will be considered as user-space requests. If the channel cannot execute the requests (e.g. when it is being closed), the operations may be rejected even if the channel is still active.

        Specified by:
        callAsync in interface VirtualChannel
        Returns:
        The Future object that can be used to wait for the completion.
        Throws:
        IOException - If there's an error during the communication.
      • terminate

        public void terminate​(@NonNull
                              IOException e)
        Aborts the connection in response to an error. This is an SPI for CommandTransport implementation to notify Channel when the underlying connection is severed. Once the call is called closeRequested will be set immediately to prevent further executions of UserRequests.
        Parameters:
        e - The error that caused the connection to be aborted. Never null.
      • removeListener

        public boolean removeListener​(Channel.Listener l)
        Removes a listener.
        Returns:
        false if the given listener has not been registered to begin with.
      • addLocalExecutionInterceptor

        public void addLocalExecutionInterceptor​(CallableDecorator decorator)
        Adds a CallableDecorator that gets a chance to decorate every Callables that run locally sent by the other peer. This is useful to tweak the environment those closures are run, such as setting up the thread context environment.
      • isInClosed

        public boolean isInClosed()
        If the receiving end of the channel is closed (that is, if we are guaranteed to receive nothing further), this method returns true.
      • isRemoteClassLoadingAllowed

        public boolean isRemoteClassLoadingAllowed()
        Since:
        2.47
      • setRemoteClassLoadingAllowed

        public void setRemoteClassLoadingAllowed​(boolean b)
        Controls whether or not this channel is willing to load classes from the other side. The default is on.
        Since:
        2.47
      • isArbitraryCallableAllowed

        public boolean isArbitraryCallableAllowed()
        Since:
        2.47
      • setMaximumBytecodeLevel

        public void setMaximumBytecodeLevel​(short level)
                                     throws IOException,
                                            InterruptedException
        Sets the maximum bytecode version (~ JDK) that we expect this channel to be able to load. If attempts are made to load remote classes using newer bytecode, they are immediately rejected, even if the remote JVM is actually new enough to load it. This helps maintain compatibility by making tests fail immediately without the need for an old JDK installation. By default, the remote class loader will try to load any bytecode version.
        Parameters:
        level - e.g. 5 for JDK 5 (the minimum sensible value)
        Throws:
        IOException
        InterruptedException
        Since:
        2.29
      • join

        public void join​(long timeout)
                  throws InterruptedException
        Waits for this Channel to be closed down, but only up the given milliseconds.
        Specified by:
        join in interface VirtualChannel
        Parameters:
        timeout - TImeout in milliseconds
        Throws:
        InterruptedException - If the current thread is interrupted while waiting for the completion.
        Since:
        1.299
      • resetPerformanceCounters

        public void resetPerformanceCounters()
        Resets all the performance counters.
      • dumpPerformanceCounters

        public void dumpPerformanceCounters​(PrintWriter w)
                                     throws IOException
        Print the performance counters.
        Parameters:
        w - Output writer
        Throws:
        IOException
        Since:
        2.24
      • dumpDiagnostics

        @Restricted(org.kohsuke.accmod.restrictions.NoExternalUse.class)
        public void dumpDiagnostics​(@NonNull
                                    PrintWriter w)
                             throws IOException
        Print the diagnostic information.

        Here's how you interpret these metrics:

        Created
        When the channel was created, which is more or less the same thing as when the channel is connected.
        Commands sent
        Number of Command objects sent to the other side. More specifically, number of commands successfully passed to transport, which means data was written to socket with ClassicCommandTransport but just buffered for write with NioChannelHub. If you have access to the remoting diagnostics of the other side of the channel, then you can compare this with "commandsReceived" metrics of the other side to see how many commands are in transit in transport. If commandsSent==commandsReceived, then no command is in transit.
        Commands received
        Number of Command objects received from the other side. More precisely, number of commands reported from transport. So for example, if data is still buffered somewhere in the networking stack, it won't be counted here.
        Last command sent
        The timestamp in which the last command was sent to the other side. The timestamp in which lastCommandSentAt was updated.
        Last command received
        The timestamp in which the last command was sent to the other side. The timestamp in which lastCommandReceivedAt was updated.
        Pending outgoing calls
        Number of RPC calls (e.g., method call through a proxy) that are made but not returned yet. If you have the remoting diagnostics of the other side, you can compare this number with "pending incoming calls" on the other side to see how many RPC calls are executing vs in flight. "one side's incoming calls" < "the other side's outgoing calls" indicates some RPC requests or responses are passing through the network layer, and mismatch between "# of commands sent" vs "# of commands received" can give some indication of whether it is request or response that's in flight.
        Pending incoming calls
        The reverse of "pending outgoing calls". Number of RPC calls (e.g., method call through a proxy) that the other side has made to us but not yet returned yet.
        Parameters:
        w - Output destination
        Throws:
        IOException - Error while creating or writing the channel information
        Since:
        2.62.3 - stable 2.x (restricted), 3.1
      • close

        public void close​(@CheckForNull
                          Throwable diagnosis)
                   throws IOException
        Closes the channel. Once the call is called closeRequested will be set immediately to prevent further executions of UserRequests.
        Parameters:
        diagnosis - If someone (either this side or the other side) tries to use a channel that's already closed, they'll get a stack trace indicating that the channel has already been closed. This diagnosis, if provided, will further chained to that exception, providing more contextual information about why the channel was closed.
        Throws:
        IOException
        Since:
        2.8
      • getProperty

        public Object getProperty​(Object key)
        Gets the application specific property set by setProperty(Object, Object). These properties are also accessible from the remote channel via getRemoteProperty(Object).

        This mechanism can be used for one side to discover contextual objects created by the other JVM (as opposed to executing Callable, which cannot have any reference to the context of the remote Channel.

        Parameters:
        key - Key
        Returns:
        The property or null if there is no property for the specified key
      • waitForProperty

        @NonNull
        public Object waitForProperty​(@NonNull
                                      Object key)
                               throws InterruptedException
        Works like getProperty(Object) but wait until some value is set by someone.
        Parameters:
        key - Property key
        Throws:
        IllegalStateException - if the channel is closed. The idea is that channel properties are expected to be the coordination mechanism between two sides of the channel, and this method in particular is a way of one side to wait for the set by the other side of the channel (via waitForRemoteProperty(Object). If we don't abort after the channel shutdown, this method will block forever.
        InterruptedException
      • setProperty

        @CheckForNull
        public Object setProperty​(@NonNull
                                  Object key,
                                  @CheckForNull
                                  Object value)
        Sets the property value on this side of the channel.
        Parameters:
        key - Property key
        value - Value to set. null removes the existing entry without adding a new one.
        Returns:
        Old property value or null if it was not set
        See Also:
        getProperty(Object)
      • setProperty

        public <T> T setProperty​(ChannelProperty<T> key,
                                 T value)
      • getRemoteProperty

        @CheckForNull
        public Object getRemoteProperty​(Object key)
        Gets the property set on the remote peer.
        Returns:
        null if the property of the said key isn't set.
      • getRemoteProperty

        @CheckForNull
        public <T> T getRemoteProperty​(ChannelProperty<T> key)
      • getUnderlyingOutput

        @Deprecated
        public OutputStream getUnderlyingOutput()
        Deprecated.
        Future version of the remoting module may add other modes of creating channel that doesn't involve stream pair. Therefore, we aren't committing to this method. This method isn't a part of the committed API of the channel class.
        Obtain the output stream passed to the constructor.
        Returns:
        While the current version always return a non-null value, the caller must not make that assumption for the above reason. This method may return null in the future version to indicate that the Channel is not sitting on top of a stream pair.
      • createLocalToRemotePortForwarding

        @Deprecated
        public ListeningPort createLocalToRemotePortForwarding​(int recvPort,
                                                               String forwardHost,
                                                               int forwardPort)
                                                        throws IOException,
                                                               InterruptedException
        Deprecated.
        as of 3.39
        Starts a local to remote port forwarding (the equivalent of "ssh -L").
        Parameters:
        recvPort - The port on this local machine that we'll listen to. 0 to let OS pick a random available port. If you specify 0, use ListeningPort.getPort() to figure out the actual assigned port.
        forwardHost - The remote host that the connection will be forwarded to. Connection to this host will be made from the other JVM that this Channel represents.
        forwardPort - The remote port that the connection will be forwarded to.
        Returns:
        Created PortForwarder
        Throws:
        IOException
        InterruptedException
      • createRemoteToLocalPortForwarding

        @Deprecated
        public ListeningPort createRemoteToLocalPortForwarding​(int recvPort,
                                                               String forwardHost,
                                                               int forwardPort)
                                                        throws IOException,
                                                               InterruptedException
        Deprecated.
        as of 3.39
        Starts a remote to local port forwarding (the equivalent of "ssh -R").
        Parameters:
        recvPort - The port on the remote JVM (represented by this Channel) that we'll listen to. 0 to let OS pick a random available port. If you specify 0, use ListeningPort.getPort() to figure out the actual assigned port.
        forwardHost - The remote host that the connection will be forwarded to. Connection to this host will be made from this JVM.
        forwardPort - The remote port that the connection will be forwarded to.
        Returns:
        Created PortForwarder.
        Throws:
        IOException
        InterruptedException
      • getName

        public String getName()
      • dumpExportTable

        public void dumpExportTable​(PrintWriter w)
                             throws IOException
        Dumps the list of exported objects and their allocation traces to the given output.
        Throws:
        IOException
      • startExportRecording

        public hudson.remoting.ExportTable.ExportList startExportRecording()
      • getLastHeard

        public long getLastHeard()
        TODO: this is not safe against clock skew and is called from jenkins core (and potentially plugins)
        See Also:
        lastCommandReceivedAt
      • current

        @CheckForNull
        public static Channel current()
        This method can be invoked during the serialization/deserialization of objects when they are transferred to the remote Channel, as well as during Callable.call() is invoked.
        Returns:
        null if the calling thread is not performing serialization.
      • dumpDiagnosticsForAll

        @Restricted(org.kohsuke.accmod.restrictions.NoExternalUse.class)
        public static void dumpDiagnosticsForAll​(@NonNull
                                                 PrintWriter w)
        Calls dumpDiagnostics(PrintWriter) across all the active channels in this system. Used for diagnostics.
        Parameters:
        w - Output destination
        Since:
        2.62.3 - stable 2.x (restricted), 3.1