Expose RefAdvertiser for reuse outside of the transport package

By making this class and its methods public, and the actual writing
abstract, we can reuse this code for other formats like writing an
info/refs file for HTTP transports.

Change-Id: Id0e349c30a0f5a8c1527e0e7383b80243819d9c5
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
This commit is contained in:
Shawn O. Pearce 2009-10-06 19:23:33 -07:00
parent e187618b6b
commit 7ed6805425
3 changed files with 165 additions and 23 deletions

View File

@ -76,6 +76,7 @@
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.ReceiveCommand.Result;
import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
import org.eclipse.jgit.util.io.InterruptTimer;
import org.eclipse.jgit.util.io.TimeoutInputStream;
import org.eclipse.jgit.util.io.TimeoutOutputStream;
@ -519,7 +520,7 @@ public void println() {
private void service() throws IOException {
if (biDirectionalPipe)
sendAdvertisedRefs();
sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut));
else
refs = db.getAllRefs();
recvCommands();
@ -574,9 +575,17 @@ private void unlockPack() {
}
}
private void sendAdvertisedRefs() throws IOException {
/**
* Generate an advertisement of available refs and capabilities.
*
* @param adv
* the advertisement formatter.
* @throws IOException
* the formatter failed to write an advertisement.
*/
public void sendAdvertisedRefs(final RefAdvertiser adv) throws IOException {
final RevFlag advertised = walk.newFlag("ADVERTISED");
final RefAdvertiser adv = new RefAdvertiser(pckOut, walk, advertised);
adv.init(walk, advertised);
adv.advertiseCapability(CAPABILITY_DELETE_REFS);
adv.advertiseCapability(CAPABILITY_REPORT_STATUS);
if (allowOfsDelta)
@ -589,7 +598,7 @@ private void sendAdvertisedRefs() throws IOException {
adv.includeAdditionalHaves();
if (adv.isEmpty())
adv.advertiseId(ObjectId.zeroId(), "capabilities^{}");
pckOut.end();
adv.end();
}
private void recvCommands() throws IOException {

View File

@ -61,12 +61,28 @@
import org.eclipse.jgit.revwalk.RevWalk;
/** Support for the start of {@link UploadPack} and {@link ReceivePack}. */
class RefAdvertiser {
private final PacketLineOut pckOut;
public abstract class RefAdvertiser {
static class PacketLineOutRefAdvertiser extends RefAdvertiser {
private final PacketLineOut pckOut;
private final RevWalk walk;
PacketLineOutRefAdvertiser(PacketLineOut out) {
pckOut = out;
}
private final RevFlag ADVERTISED;
@Override
protected void writeOne(final CharSequence line) throws IOException {
pckOut.writeString(line.toString());
}
@Override
protected void end() throws IOException {
pckOut.end();
}
}
private RevWalk walk;
private RevFlag ADVERTISED;
private final StringBuilder tmpLine = new StringBuilder(100);
@ -78,22 +94,72 @@ class RefAdvertiser {
private boolean first = true;
RefAdvertiser(final PacketLineOut out, final RevWalk protoWalk,
final RevFlag advertisedFlag) {
pckOut = out;
/**
* Initialize a new advertisement formatter.
*
* @param protoWalk
* the RevWalk used to parse objects that are advertised.
* @param advertisedFlag
* flag marked on any advertised objects parsed out of the
* {@code protoWalk}'s object pool, permitting the caller to
* later quickly determine if an object was advertised (or not).
*/
public void init(final RevWalk protoWalk, final RevFlag advertisedFlag) {
walk = protoWalk;
ADVERTISED = advertisedFlag;
}
void setDerefTags(final boolean deref) {
/**
* Toggle tag peeling.
* <p>
* <p>
* This method must be invoked prior to any of the following:
* <ul>
* <li>{@link #send(Collection)}
* <li>{@link #advertiseHave(AnyObjectId)}
* <li>{@link #includeAdditionalHaves()}
* </ul>
*
* @param deref
* true to show the dereferenced value of a tag as the special
* ref <code>$tag^{}</code> ; false to omit it from the output.
*/
public void setDerefTags(final boolean deref) {
derefTags = deref;
}
void advertiseCapability(String name) {
/**
* Add one protocol capability to the initial advertisement.
* <p>
* This method must be invoked prior to any of the following:
* <ul>
* <li>{@link #send(Collection)}
* <li>{@link #advertiseHave(AnyObjectId)}
* <li>{@link #includeAdditionalHaves()}
* </ul>
*
* @param name
* the name of a single protocol capability supported by the
* caller. The set of capabilities are sent to the client in the
* advertisement, allowing the client to later selectively enable
* features it recognizes.
*/
public void advertiseCapability(String name) {
capablities.add(name);
}
void send(final Collection<Ref> refs) throws IOException {
/**
* Format an advertisement for the supplied refs.
*
* @param refs
* zero or more refs to format for the client. The collection is
* copied and sorted before display and therefore may appear in
* any order.
* @throws IOException
* the underlying output stream failed to write out an
* advertisement record.
*/
public void send(final Collection<Ref> refs) throws IOException {
for (final Ref r : RefComparator.sort(refs)) {
final RevObject obj = parseAnyOrNull(r.getObjectId());
if (obj != null) {
@ -104,7 +170,21 @@ void send(final Collection<Ref> refs) throws IOException {
}
}
void advertiseHave(AnyObjectId id) throws IOException {
/**
* Advertise one object is available using the magic {@code .have}.
* <p>
* The magic {@code .have} advertisement is not available for fetching by a
* client, but can be used by a client when considering a delta base
* candidate before transferring data in a push. Within the record created
* by this method the ref name is simply the invalid string {@code .have}.
*
* @param id
* identity of the object that is assumed to exist.
* @throws IOException
* the underlying output stream failed to write out an
* advertisement record.
*/
public void advertiseHave(AnyObjectId id) throws IOException {
RevObject obj = parseAnyOrNull(id);
if (obj != null) {
advertiseAnyOnce(obj, ".have");
@ -113,7 +193,14 @@ void advertiseHave(AnyObjectId id) throws IOException {
}
}
void includeAdditionalHaves() throws IOException {
/**
* Include references of alternate repositories as {@code .have} lines.
*
* @throws IOException
* the underlying output stream failed to write out an
* advertisement record.
*/
public void includeAdditionalHaves() throws IOException {
additionalHaves(walk.getRepository().getObjectDatabase());
}
@ -129,7 +216,8 @@ private void additionalHaves(final Repository alt) throws IOException {
advertiseHave(r.getObjectId());
}
boolean isEmpty() {
/** @return true if no advertisements have been sent yet. */
public boolean isEmpty() {
return first;
}
@ -172,7 +260,22 @@ private void advertiseTag(final RevTag tag, final String refName)
advertiseAny(tag.getObject(), refName);
}
void advertiseId(final AnyObjectId id, final String refName)
/**
* Advertise one object under a specific name.
* <p>
* If the advertised object is a tag, this method does not advertise the
* peeled version of it.
*
* @param id
* the object to advertise.
* @param refName
* name of the reference to advertise the object as, can be any
* string not including the NUL byte.
* @throws IOException
* the underlying output stream failed to write out an
* advertisement record.
*/
public void advertiseId(final AnyObjectId id, final String refName)
throws IOException {
tmpLine.setLength(0);
id.copyTo(tmpId, tmpLine);
@ -190,6 +293,27 @@ void advertiseId(final AnyObjectId id, final String refName)
}
}
tmpLine.append('\n');
pckOut.writeString(tmpLine.toString());
writeOne(tmpLine);
}
/**
* Write a single advertisement line.
*
* @param line
* the advertisement line to be written. The line always ends
* with LF. Never null or the empty string.
* @throws IOException
* the underlying output stream failed to write out an
* advertisement record.
*/
protected abstract void writeOne(CharSequence line) throws IOException;
/**
* Mark the end of the advertisements.
*
* @throws IOException
* the underlying output stream failed to write out an
* advertisement record.
*/
protected abstract void end() throws IOException;
}

View File

@ -70,6 +70,7 @@
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
import org.eclipse.jgit.util.io.InterruptTimer;
import org.eclipse.jgit.util.io.TimeoutInputStream;
import org.eclipse.jgit.util.io.TimeoutOutputStream;
@ -282,7 +283,7 @@ public void upload(final InputStream input, final OutputStream output,
private void service() throws IOException {
if (biDirectionalPipe)
sendAdvertisedRefs();
sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut));
else {
refs = db.getAllRefs();
for (Ref r : refs.values()) {
@ -309,8 +310,16 @@ else if (options.contains(OPTION_MULTI_ACK))
sendPack();
}
private void sendAdvertisedRefs() throws IOException {
final RefAdvertiser adv = new RefAdvertiser(pckOut, walk, ADVERTISED);
/**
* Generate an advertisement of available refs and capabilities.
*
* @param adv
* the advertisement formatter.
* @throws IOException
* the formatter failed to write an advertisement.
*/
public void sendAdvertisedRefs(final RefAdvertiser adv) throws IOException {
adv.init(walk, ADVERTISED);
adv.advertiseCapability(OPTION_INCLUDE_TAG);
adv.advertiseCapability(OPTION_MULTI_ACK_DETAILED);
adv.advertiseCapability(OPTION_MULTI_ACK);
@ -322,7 +331,7 @@ private void sendAdvertisedRefs() throws IOException {
adv.setDerefTags(true);
refs = db.getAllRefs();
adv.send(refs.values());
pckOut.end();
adv.end();
}
private void recvWants() throws IOException {