hudson.remoting.Pipe Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hudson-remoting Show documentation
Show all versions of hudson-remoting Show documentation
Contains the bootstrap code to bridge separate JVMs into a single semi-shared space.
Reusable outside Hudson.
The newest version!
/*******************************************************************************
*
* Copyright (c) 2004-2009 Oracle Corporation.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*
* Kohsuke Kawaguchi
*
*
*******************************************************************************/
package hudson.remoting;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Pipe for the remote {@link Callable} and the local program to talk to each other.
*
*
* There are two kinds of pipes. One is for having a local system write to a remote system,
* and the other is for having a remote system write to a local system. Use
* the different versions of the create method to create the appropriate kind
* of pipes.
*
*
* Once created, {@link Pipe} can be sent to the remote system as a part of a serialization of
* {@link Callable} between {@link Channel}s.
* Once re-instantiated on the remote {@link Channel}, pipe automatically connects
* back to the local instance and perform necessary set up.
*
*
* The local and remote system can then call {@link #getIn()} and {@link #getOut()} to
* read/write bytes.
*
*
* Pipe can be only written by one system and read by the other system. It is an error to
* send one {@link Pipe} to two remote {@link Channel}s, or send one {@link Pipe} to
* the same {@link Channel} twice.
*
* Usage
*
* final Pipe p = Pipe.createLocalToRemote();
*
* channel.callAsync(new Callable() {
* public Object call() {
* InputStream in = p.getIn();
* ... read from in ...
* }
* });
*
* OutputStream out = p.getOut();
* ... write to out ...
*
*
* Implementation Note
*
* For better performance, {@link Pipe} uses lower-level {@link Command} abstraction
* to send data, instead of typed proxy object. This allows the writer to send data
* without blocking until the arrival of the data is confirmed.
*
* @author Kohsuke Kawaguchi
*/
public final class Pipe implements Serializable {
private InputStream in;
private OutputStream out;
private Pipe(InputStream in, OutputStream out) {
this.in = in;
this.out = out;
}
/**
* Gets the reading end of the pipe.
*/
public InputStream getIn() {
return in;
}
/**
* Gets the writing end of the pipe.
*/
public OutputStream getOut() {
return out;
}
/**
* Creates a {@link Pipe} that allows remote system to write and local system to read.
*/
public static Pipe createRemoteToLocal() {
// OutputStream will be created on the target
return new Pipe(new FastPipedInputStream(), null);
}
/**
* Creates a {@link Pipe} that allows local system to write and remote system to read.
*/
public static Pipe createLocalToRemote() {
return new Pipe(null, new ProxyOutputStream());
}
private void writeObject(ObjectOutputStream oos) throws IOException {
if (in != null && out == null) {
// remote will write to local
FastPipedOutputStream pos = new FastPipedOutputStream((FastPipedInputStream) in);
int oid = Channel.current()
.export(pos, false); // this export is unexported in ProxyOutputStream.finalize()
oos.writeBoolean(true); // marker
oos.writeInt(oid);
} else {
// remote will read from local
int oid = Channel.current().export(out);
oos.writeBoolean(false);
oos.writeInt(oid);
}
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
final Channel channel = Channel.current();
assert channel != null;
if (ois.readBoolean()) {
// local will write to remote
in = null;
out = new ProxyOutputStream(channel, ois.readInt());
} else {
// local will read from remote.
// tell the remote system about this local read pipe
// this is the OutputStream that wants to send data to us
final int oidRos = ois.readInt();
// we want 'oidRos' to send data to this PipedOutputStream
FastPipedOutputStream pos = new FastPipedOutputStream();
FastPipedInputStream pis = new FastPipedInputStream(pos);
final int oidPos = channel.export(pos);
// tell 'ros' to connect to our 'pos'.
channel.send(new ConnectCommand(oidRos, oidPos));
out = null;
in = pis;
}
}
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(Pipe.class.getName());
private static class ConnectCommand extends Command {
private final int oidRos;
private final int oidPos;
public ConnectCommand(int oidRos, int oidPos) {
this.oidRos = oidRos;
this.oidPos = oidPos;
}
protected void execute(final Channel channel) {
channel.pipeWriter.submit(new Runnable() {
public void run() {
try {
final ProxyOutputStream ros = (ProxyOutputStream) channel.getExportedObject(oidRos);
channel.unexport(oidRos);
ros.connect(channel, oidPos);
} catch (IOException e) {
logger.log(Level.SEVERE, "Failed to connect to pipe", e);
}
}
});
}
static final long serialVersionUID = -9128735897846418140L;
}
}