TransportLocal knows how to spin up a thread to allow two repositories in the same process to communicate using the wire protocol. However, it is still tied to local on-disk filesystems, and needs to be able to fork processes if not using the default git-{upload,receive}-pack implementation. Extract out the connection classes so they can be used by other transport implementations. Change-Id: I5db59086740735508c2e70a597c2d1a89014b072tags/v4.0.0.201503231230-m1
@@ -0,0 +1,141 @@ | |||
/* | |||
* Copyright (C) 2015, Google Inc. | |||
* and other copyright owners as documented in the project's IP log. | |||
* | |||
* This program and the accompanying materials are made available | |||
* under the terms of the Eclipse Distribution License v1.0 which | |||
* accompanies this distribution, is reproduced below, and is | |||
* available at http://www.eclipse.org/org/documents/edl-v10.php | |||
* | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or | |||
* without modification, are permitted provided that the following | |||
* conditions are met: | |||
* | |||
* - Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* - Redistributions in binary form must reproduce the above | |||
* copyright notice, this list of conditions and the following | |||
* disclaimer in the documentation and/or other materials provided | |||
* with the distribution. | |||
* | |||
* - Neither the name of the Eclipse Foundation, Inc. nor the | |||
* names of its contributors may be used to endorse or promote | |||
* products derived from this software without specific prior | |||
* written permission. | |||
* | |||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND | |||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | |||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | |||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
package org.eclipse.jgit.transport; | |||
import java.io.IOException; | |||
import java.io.PipedInputStream; | |||
import java.io.PipedOutputStream; | |||
import org.eclipse.jgit.errors.TransportException; | |||
import org.eclipse.jgit.internal.JGitText; | |||
import org.eclipse.jgit.lib.Repository; | |||
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; | |||
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; | |||
import org.eclipse.jgit.transport.resolver.UploadPackFactory; | |||
class InternalFetchConnection<C> extends BasePackFetchConnection { | |||
private Thread worker; | |||
public InternalFetchConnection(PackTransport transport, | |||
final UploadPackFactory<C> uploadPackFactory, | |||
final C req, final Repository remote) throws TransportException { | |||
super(transport); | |||
final PipedInputStream in_r; | |||
final PipedOutputStream in_w; | |||
final PipedInputStream out_r; | |||
final PipedOutputStream out_w; | |||
try { | |||
in_r = new PipedInputStream(); | |||
in_w = new PipedOutputStream(in_r); | |||
out_r = new PipedInputStream() { | |||
// The client (BasePackFetchConnection) can write | |||
// a huge burst before it reads again. We need to | |||
// force the buffer to be big enough, otherwise it | |||
// will deadlock both threads. | |||
{ | |||
buffer = new byte[MIN_CLIENT_BUFFER]; | |||
} | |||
}; | |||
out_w = new PipedOutputStream(out_r); | |||
} catch (IOException err) { | |||
remote.close(); | |||
throw new TransportException(uri, JGitText.get().cannotConnectPipes, err); | |||
} | |||
worker = new Thread("JGit-Upload-Pack") { //$NON-NLS-1$ | |||
public void run() { | |||
try { | |||
final UploadPack rp = uploadPackFactory.create(req, remote); | |||
rp.upload(out_r, in_w, null); | |||
} catch (ServiceNotEnabledException e) { | |||
// Ignored. Client cannot use this repository. | |||
} catch (ServiceNotAuthorizedException e) { | |||
// Ignored. Client cannot use this repository. | |||
} catch (IOException err) { | |||
// Client side of the pipes should report the problem. | |||
err.printStackTrace(); | |||
} catch (RuntimeException err) { | |||
// Client side will notice we went away, and report. | |||
err.printStackTrace(); | |||
} finally { | |||
try { | |||
out_r.close(); | |||
} catch (IOException e2) { | |||
// Ignore close failure, we probably crashed above. | |||
} | |||
try { | |||
in_w.close(); | |||
} catch (IOException e2) { | |||
// Ignore close failure, we probably crashed above. | |||
} | |||
remote.close(); | |||
} | |||
} | |||
}; | |||
worker.start(); | |||
init(in_r, out_w); | |||
readAdvertisedRefs(); | |||
} | |||
@Override | |||
public void close() { | |||
super.close(); | |||
try { | |||
if (worker != null) { | |||
worker.join(); | |||
} | |||
} catch (InterruptedException ie) { | |||
// Stop waiting and return anyway. | |||
} finally { | |||
worker = null; | |||
} | |||
} | |||
} |
@@ -0,0 +1,131 @@ | |||
/* | |||
* Copyright (C) 2015, Google Inc. | |||
* and other copyright owners as documented in the project's IP log. | |||
* | |||
* This program and the accompanying materials are made available | |||
* under the terms of the Eclipse Distribution License v1.0 which | |||
* accompanies this distribution, is reproduced below, and is | |||
* available at http://www.eclipse.org/org/documents/edl-v10.php | |||
* | |||
* All rights reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or | |||
* without modification, are permitted provided that the following | |||
* conditions are met: | |||
* | |||
* - Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* - Redistributions in binary form must reproduce the above | |||
* copyright notice, this list of conditions and the following | |||
* disclaimer in the documentation and/or other materials provided | |||
* with the distribution. | |||
* | |||
* - Neither the name of the Eclipse Foundation, Inc. nor the | |||
* names of its contributors may be used to endorse or promote | |||
* products derived from this software without specific prior | |||
* written permission. | |||
* | |||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND | |||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | |||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | |||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
package org.eclipse.jgit.transport; | |||
import java.io.IOException; | |||
import java.io.PipedInputStream; | |||
import java.io.PipedOutputStream; | |||
import org.eclipse.jgit.errors.TransportException; | |||
import org.eclipse.jgit.internal.JGitText; | |||
import org.eclipse.jgit.lib.Repository; | |||
import org.eclipse.jgit.transport.resolver.ReceivePackFactory; | |||
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; | |||
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; | |||
class InternalPushConnection<C> extends BasePackPushConnection { | |||
private Thread worker; | |||
public InternalPushConnection(PackTransport transport, | |||
final ReceivePackFactory<C> receivePackFactory, | |||
final C req, final Repository remote) throws TransportException { | |||
super(transport); | |||
final PipedInputStream in_r; | |||
final PipedOutputStream in_w; | |||
final PipedInputStream out_r; | |||
final PipedOutputStream out_w; | |||
try { | |||
in_r = new PipedInputStream(); | |||
in_w = new PipedOutputStream(in_r); | |||
out_r = new PipedInputStream(); | |||
out_w = new PipedOutputStream(out_r); | |||
} catch (IOException err) { | |||
remote.close(); | |||
throw new TransportException(uri, JGitText.get().cannotConnectPipes, err); | |||
} | |||
worker = new Thread("JGit-Receive-Pack") { //$NON-NLS-1$ | |||
public void run() { | |||
try { | |||
final ReceivePack rp = receivePackFactory.create(req, remote); | |||
rp.receive(out_r, in_w, System.err); | |||
} catch (ServiceNotEnabledException e) { | |||
// Ignored. Client cannot use this repository. | |||
} catch (ServiceNotAuthorizedException e) { | |||
// Ignored. Client cannot use this repository. | |||
} catch (IOException err) { | |||
// Client side of the pipes should report the problem. | |||
} catch (RuntimeException err) { | |||
// Clients side will notice we went away, and report. | |||
} finally { | |||
try { | |||
out_r.close(); | |||
} catch (IOException e2) { | |||
// Ignore close failure, we probably crashed above. | |||
} | |||
try { | |||
in_w.close(); | |||
} catch (IOException e2) { | |||
// Ignore close failure, we probably crashed above. | |||
} | |||
remote.close(); | |||
} | |||
} | |||
}; | |||
worker.start(); | |||
init(in_r, out_w); | |||
readAdvertisedRefs(); | |||
} | |||
@Override | |||
public void close() { | |||
super.close(); | |||
if (worker != null) { | |||
try { | |||
worker.join(); | |||
} catch (InterruptedException ie) { | |||
// Stop waiting and return anyway. | |||
} finally { | |||
worker = null; | |||
} | |||
} | |||
} | |||
} |
@@ -52,8 +52,6 @@ import java.io.File; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import java.io.PipedInputStream; | |||
import java.io.PipedOutputStream; | |||
import java.util.Collections; | |||
import java.util.Map; | |||
import java.util.Set; | |||
@@ -65,6 +63,8 @@ import org.eclipse.jgit.internal.JGitText; | |||
import org.eclipse.jgit.lib.Repository; | |||
import org.eclipse.jgit.lib.RepositoryBuilder; | |||
import org.eclipse.jgit.lib.RepositoryCache; | |||
import org.eclipse.jgit.transport.resolver.ReceivePackFactory; | |||
import org.eclipse.jgit.transport.resolver.UploadPackFactory; | |||
import org.eclipse.jgit.util.FS; | |||
import org.eclipse.jgit.util.io.MessageWriter; | |||
import org.eclipse.jgit.util.io.SafeBufferedOutputStream; | |||
@@ -168,21 +168,44 @@ class TransportLocal extends Transport implements PackTransport { | |||
return new ReceivePack(dst); | |||
} | |||
private Repository openRepo() throws TransportException { | |||
try { | |||
return new RepositoryBuilder().setGitDir(remoteGitDir).build(); | |||
} catch (IOException err) { | |||
throw new TransportException(uri, JGitText.get().notAGitDirectory); | |||
} | |||
} | |||
@Override | |||
public FetchConnection openFetch() throws TransportException { | |||
final String up = getOptionUploadPack(); | |||
if ("git-upload-pack".equals(up) || "git upload-pack".equals(up)) //$NON-NLS-1$ //$NON-NLS-2$ | |||
return new InternalLocalFetchConnection(); | |||
return new ForkLocalFetchConnection(); | |||
if (!"git-upload-pack".equals(up) //$NON-NLS-1$ | |||
&& !"git upload-pack".equals(up)) //$NON-NLS-1$ | |||
return new ForkLocalFetchConnection(); | |||
UploadPackFactory<Void> upf = new UploadPackFactory<Void>() { | |||
@Override | |||
public UploadPack create(Void req, Repository db) { | |||
return createUploadPack(db); | |||
} | |||
}; | |||
return new InternalFetchConnection<Void>(this, upf, null, openRepo()); | |||
} | |||
@Override | |||
public PushConnection openPush() throws NotSupportedException, | |||
TransportException { | |||
public PushConnection openPush() throws TransportException { | |||
final String rp = getOptionReceivePack(); | |||
if ("git-receive-pack".equals(rp) || "git receive-pack".equals(rp)) //$NON-NLS-1$ //$NON-NLS-2$ | |||
return new InternalLocalPushConnection(); | |||
return new ForkLocalPushConnection(); | |||
if (!"git-receive-pack".equals(rp) //$NON-NLS-1$ | |||
&& !"git receive-pack".equals(rp)) //$NON-NLS-1$ | |||
return new ForkLocalPushConnection(); | |||
ReceivePackFactory<Void> rpf = new ReceivePackFactory<Void>() { | |||
@Override | |||
public ReceivePack create(Void req, Repository db) { | |||
return createReceivePack(db); | |||
} | |||
}; | |||
return new InternalPushConnection<Void>(this, rpf, null, openRepo()); | |||
} | |||
@Override | |||
@@ -214,93 +237,6 @@ class TransportLocal extends Transport implements PackTransport { | |||
} | |||
} | |||
class InternalLocalFetchConnection extends BasePackFetchConnection { | |||
private Thread worker; | |||
InternalLocalFetchConnection() throws TransportException { | |||
super(TransportLocal.this); | |||
final Repository dst; | |||
try { | |||
dst = new RepositoryBuilder().setGitDir(remoteGitDir).build(); | |||
} catch (IOException err) { | |||
throw new TransportException(uri, JGitText.get().notAGitDirectory); | |||
} | |||
final PipedInputStream in_r; | |||
final PipedOutputStream in_w; | |||
final PipedInputStream out_r; | |||
final PipedOutputStream out_w; | |||
try { | |||
in_r = new PipedInputStream(); | |||
in_w = new PipedOutputStream(in_r); | |||
out_r = new PipedInputStream() { | |||
// The client (BasePackFetchConnection) can write | |||
// a huge burst before it reads again. We need to | |||
// force the buffer to be big enough, otherwise it | |||
// will deadlock both threads. | |||
{ | |||
buffer = new byte[MIN_CLIENT_BUFFER]; | |||
} | |||
}; | |||
out_w = new PipedOutputStream(out_r); | |||
} catch (IOException err) { | |||
dst.close(); | |||
throw new TransportException(uri, JGitText.get().cannotConnectPipes, err); | |||
} | |||
worker = new Thread("JGit-Upload-Pack") { //$NON-NLS-1$ | |||
public void run() { | |||
try { | |||
final UploadPack rp = createUploadPack(dst); | |||
rp.upload(out_r, in_w, null); | |||
} catch (IOException err) { | |||
// Client side of the pipes should report the problem. | |||
err.printStackTrace(); | |||
} catch (RuntimeException err) { | |||
// Clients side will notice we went away, and report. | |||
err.printStackTrace(); | |||
} finally { | |||
try { | |||
out_r.close(); | |||
} catch (IOException e2) { | |||
// Ignore close failure, we probably crashed above. | |||
} | |||
try { | |||
in_w.close(); | |||
} catch (IOException e2) { | |||
// Ignore close failure, we probably crashed above. | |||
} | |||
dst.close(); | |||
} | |||
} | |||
}; | |||
worker.start(); | |||
init(in_r, out_w); | |||
readAdvertisedRefs(); | |||
} | |||
@Override | |||
public void close() { | |||
super.close(); | |||
if (worker != null) { | |||
try { | |||
worker.join(); | |||
} catch (InterruptedException ie) { | |||
// Stop waiting and return anyway. | |||
} finally { | |||
worker = null; | |||
} | |||
} | |||
} | |||
} | |||
class ForkLocalFetchConnection extends BasePackFetchConnection { | |||
private Process uploadPack; | |||
@@ -354,83 +290,6 @@ class TransportLocal extends Transport implements PackTransport { | |||
} | |||
} | |||
class InternalLocalPushConnection extends BasePackPushConnection { | |||
private Thread worker; | |||
InternalLocalPushConnection() throws TransportException { | |||
super(TransportLocal.this); | |||
final Repository dst; | |||
try { | |||
dst = new RepositoryBuilder().setGitDir(remoteGitDir).build(); | |||
} catch (IOException err) { | |||
throw new TransportException(uri, JGitText.get().notAGitDirectory); | |||
} | |||
final PipedInputStream in_r; | |||
final PipedOutputStream in_w; | |||
final PipedInputStream out_r; | |||
final PipedOutputStream out_w; | |||
try { | |||
in_r = new PipedInputStream(); | |||
in_w = new PipedOutputStream(in_r); | |||
out_r = new PipedInputStream(); | |||
out_w = new PipedOutputStream(out_r); | |||
} catch (IOException err) { | |||
dst.close(); | |||
throw new TransportException(uri, JGitText.get().cannotConnectPipes, err); | |||
} | |||
worker = new Thread("JGit-Receive-Pack") { //$NON-NLS-1$ | |||
public void run() { | |||
try { | |||
final ReceivePack rp = createReceivePack(dst); | |||
rp.receive(out_r, in_w, System.err); | |||
} catch (IOException err) { | |||
// Client side of the pipes should report the problem. | |||
} catch (RuntimeException err) { | |||
// Clients side will notice we went away, and report. | |||
} finally { | |||
try { | |||
out_r.close(); | |||
} catch (IOException e2) { | |||
// Ignore close failure, we probably crashed above. | |||
} | |||
try { | |||
in_w.close(); | |||
} catch (IOException e2) { | |||
// Ignore close failure, we probably crashed above. | |||
} | |||
dst.close(); | |||
} | |||
} | |||
}; | |||
worker.start(); | |||
init(in_r, out_w); | |||
readAdvertisedRefs(); | |||
} | |||
@Override | |||
public void close() { | |||
super.close(); | |||
if (worker != null) { | |||
try { | |||
worker.join(); | |||
} catch (InterruptedException ie) { | |||
// Stop waiting and return anyway. | |||
} finally { | |||
worker = null; | |||
} | |||
} | |||
} | |||
} | |||
class ForkLocalPushConnection extends BasePackPushConnection { | |||
private Process receivePack; | |||