Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[JENKINS-48133] - ChannelStateException now recoreds channel name and…
… info when possible.

ChannelStateException has not been released yet, so the API compatibility is not broken.
  • Loading branch information
oleg-nenashev committed Nov 21, 2017
1 parent 2d5624c commit 86d4f80
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 11 deletions.
4 changes: 2 additions & 2 deletions src/main/java/hudson/remoting/Callable.java
Expand Up @@ -63,7 +63,7 @@ default Channel getChannelOrFail() throws ChannelStateException {
// and leaks in ExportTable.
//TODO: maybe there is a way to actually diagnose this case?
final Thread t = Thread.currentThread();
throw new ChannelStateException("The calling thread " + t + " has no associated channel. "
throw new ChannelStateException(null, "The calling thread " + t + " has no associated channel. "
+ "The current object " + this + " is " + SerializableOnlyOverRemoting.class +
", but it is likely being serialized/deserialized without the channel");
}
Expand All @@ -85,7 +85,7 @@ default Channel getChannelOrFail() throws ChannelStateException {
default Channel getOpenChannelOrFail() throws ChannelStateException {
final Channel ch = getChannelOrFail();
if (ch.isClosingOrClosed()) {
throw new ChannelClosedException("The associated channel " + ch + " is closing down or has closed down", ch.getCloseRequestCause());
throw new ChannelClosedException(ch, "The associated channel " + ch + " is closing down or has closed down", ch.getCloseRequestCause());
}
return ch;
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/hudson/remoting/Channel.java
Expand Up @@ -669,7 +669,7 @@ private ExecutorService createPipeWriterExecutor() {
@SuppressFBWarnings(value = "VO_VOLATILE_INCREMENT", justification = "The method is synchronized, no other usages. See https://sourceforge.net/p/findbugs/bugs/1032/")
/*package*/ synchronized void send(Command cmd) throws IOException {
if(outClosed!=null)
throw new ChannelClosedException(outClosed);
throw new ChannelClosedException(this, outClosed);
if(logger.isLoggable(Level.FINE))
logger.fine("Send "+cmd);

Expand Down
35 changes: 32 additions & 3 deletions src/main/java/hudson/remoting/ChannelClosedException.java
Expand Up @@ -19,11 +19,25 @@ public class ChannelClosedException extends ChannelStateException {
*/
@Deprecated
public ChannelClosedException() {
super("channel is already closed");
this(null, "channel is already closed", null);
}

/**
* @deprecated Use {@link #ChannelClosedException(Channel, Throwable)}
*/
@Deprecated
public ChannelClosedException(Throwable cause) {
super("channel is already closed", cause);
this((Channel) null, cause);
}

/**
* Constructor.
* @param channel Reference to the channel. {@code null} if the channel is unknown.
* @param cause Cause
* @since TODO
*/
public ChannelClosedException(@CheckForNull Channel channel, @CheckForNull Throwable cause) {
super(channel, "channel is already closed", cause);
}

/**
Expand All @@ -33,8 +47,23 @@ public ChannelClosedException(Throwable cause) {
* @param cause Cause of the channel close/termination.
* May be {@code null} if it cannot be determined when the exception is constructed.
* @since 3.11
* @deprecated Use {@link #ChannelClosedException(Channel, String, Throwable)}
*/
@Deprecated
public ChannelClosedException(@Nonnull String message, @CheckForNull Throwable cause) {
super(message, cause);
this(null, message, cause);
}

/**
* Constructor.
*
* @param channel Reference to the channel. {@code null} if the channel is unknown.
* @param message Message
* @param cause Cause of the channel close/termination.
* May be {@code null} if it cannot be determined when the exception is constructed.
* @since TODO
*/
public ChannelClosedException(@CheckForNull Channel channel, @Nonnull String message, @CheckForNull Throwable cause) {
super(channel, message, cause);
}
}
2 changes: 1 addition & 1 deletion src/main/java/hudson/remoting/Request.java
Expand Up @@ -117,7 +117,7 @@ public void checkIfCanBeExecutedOnChannel(@Nonnull Channel channel) throws IOExc
final Throwable senderCloseCause = channel.getSenderCloseCause();
if (senderCloseCause != null) {
// Sender is closed, we won't be able to send anything
throw new ChannelClosedException(senderCloseCause);
throw new ChannelClosedException(channel, senderCloseCause);
}
}

Expand Down
57 changes: 55 additions & 2 deletions src/main/java/org/jenkinsci/remoting/ChannelStateException.java
Expand Up @@ -26,21 +26,74 @@

package org.jenkinsci.remoting;

import hudson.remoting.Channel;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.lang.ref.WeakReference;

/**
* Indicates invalid state of the channel during the operation.
*
* Exception stores the channel reference is a {@link WeakReference}, so the information may be deallocated at any moment.
* Former channel name can be retrieved via {@link #getChannelName()}.
* The reference also will not be serialized.
*
* @author Oleg Nenashev
* @since TODO
*/
public class ChannelStateException extends IOException {

public ChannelStateException(String message) {
@Nonnull
private final String channelName;

@CheckForNull
private final transient WeakReference<Channel> channelRef;

public ChannelStateException(@CheckForNull Channel channel, @Nonnull String message) {
super(message);
channelRef = channel != null ? new WeakReference<>(channel) : null;
channelName = channel != null ? channel.getName() : "unknown";
}

public ChannelStateException(String message, Throwable cause) {
public ChannelStateException(@CheckForNull Channel channel, String message, @CheckForNull Throwable cause) {
super(message, cause);
channelRef = channel != null ? new WeakReference<>(channel) : null;
channelName = channel != null ? channel.getName() : "unknown";
}

@CheckForNull
public WeakReference<Channel> getChannelRef() {
return channelRef;
}

/**
* Gets channel name.
* @return Channel name ot {@code unknown} if it is not known.
*/
@Nonnull
public String getChannelName() {
return channelName;
}

/**
* Gets channel associated with the exception.
*
* The channel reference is a {@link WeakReference}, so the information may be deallocated at any moment.
* Former channel name can be retrieved via {@link #getChannelName()}.
* The reference also will not be serialized.
* @return Channel reference if it is available. {@code null} otherwise.
*/
@CheckForNull
public Channel getChannel() {
return channelRef != null ? channelRef.get() : null;
}

@Override
public String getMessage() {
Channel ch = getChannel();
String infoForMessage = ch != null ? ch.toString() : channelName;
return String.format("Channel \"%s\": %s", infoForMessage, super.getMessage());
}
}
Expand Up @@ -291,15 +291,17 @@ public ByteBufferCommandTransport(Capability remoteCapability) {
*/
@Override
protected void write(ByteBuffer header, ByteBuffer data) throws IOException {
//TODO: Any way to get channel information here
if (isWriteOpen()) {
try {
ChannelApplicationLayer.this.write(header);
ChannelApplicationLayer.this.write(data);
} catch (ClosedChannelException e) {
throw new ChannelClosedException(e);
// Probably it should be another exception type at all
throw new ChannelClosedException(null, "Protocol stack cannot write data anymore. ChannelApplicationLayer reports that the NIO Channel is closed", e);
}
} else {
throw new ChannelClosedException(new ClosedChannelException());
throw new ChannelClosedException(null, "Protocol stack cannot write data anymore. It is not open for write", null);
}
}

Expand Down

0 comments on commit 86d4f80

Please sign in to comment.