Merge "ProtocolV2HookChain: Allow to create a chain of protocol V2 hooks"
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
index 7b952e8..65df6e3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
@@ -24,6 +24,8 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.function.Consumer;
 
 import org.eclipse.jgit.dircache.DirCache;
 import org.eclipse.jgit.dircache.DirCacheBuilder;
@@ -424,22 +426,18 @@ public void testFetchWithNonSupportingServer() throws Exception {
 	 * Invokes UploadPack with protocol v2 and sends it the given lines,
 	 * and returns UploadPack's output stream.
 	 */
-	private ByteArrayInputStream uploadPackV2Setup(RequestPolicy requestPolicy,
-			RefFilter refFilter, ProtocolV2Hook hook, String... inputLines)
+	private ByteArrayInputStream uploadPackV2Setup(
+			Consumer<UploadPack> postConstructionSetup, String... inputLines)
 			throws Exception {
 
 		ByteArrayInputStream send = linesAsInputStream(inputLines);
 
 		server.getConfig().setString("protocol", null, "version", "2");
 		UploadPack up = new UploadPack(server);
-		if (requestPolicy != null)
-			up.setRequestPolicy(requestPolicy);
-		if (refFilter != null)
-			up.setRefFilter(refFilter);
-		up.setExtraParameters(Sets.of("version=2"));
-		if (hook != null) {
-			up.setProtocolV2Hook(hook);
+		if (postConstructionSetup != null) {
+			postConstructionSetup.accept(up);
 		}
+		up.setExtraParameters(Sets.of("version=2"));
 
 		ByteArrayOutputStream recv = new ByteArrayOutputStream();
 		up.upload(send, recv, null);
@@ -453,6 +451,7 @@ private static ByteArrayInputStream linesAsInputStream(String... inputLines)
 		try (ByteArrayOutputStream send = new ByteArrayOutputStream()) {
 			PacketLineOut pckOut = new PacketLineOut(send);
 			for (String line : inputLines) {
+				Objects.requireNonNull(line);
 				if (PacketLineIn.isEnd(line)) {
 					pckOut.end();
 				} else if (PacketLineIn.isDelimiter(line)) {
@@ -470,11 +469,12 @@ private static ByteArrayInputStream linesAsInputStream(String... inputLines)
 	 * Returns UploadPack's output stream, not including the capability
 	 * advertisement by the server.
 	 */
-	private ByteArrayInputStream uploadPackV2(RequestPolicy requestPolicy,
-			RefFilter refFilter, ProtocolV2Hook hook, String... inputLines)
+	private ByteArrayInputStream uploadPackV2(
+			Consumer<UploadPack> postConstructionSetup,
+			String... inputLines)
 			throws Exception {
 		ByteArrayInputStream recvStream =
-				uploadPackV2Setup(requestPolicy, refFilter, hook, inputLines);
+				uploadPackV2Setup(postConstructionSetup, inputLines);
 		PacketLineIn pckIn = new PacketLineIn(recvStream);
 
 		// drain capabilities
@@ -485,7 +485,7 @@ private ByteArrayInputStream uploadPackV2(RequestPolicy requestPolicy,
 	}
 
 	private ByteArrayInputStream uploadPackV2(String... inputLines) throws Exception {
-		return uploadPackV2(null, null, null, inputLines);
+		return uploadPackV2(null, inputLines);
 	}
 
 	private static class TestV2Hook implements ProtocolV2Hook {
@@ -514,8 +514,9 @@ public void onFetch(FetchV2Request req) {
 	@Test
 	public void testV2Capabilities() throws Exception {
 		TestV2Hook hook = new TestV2Hook();
-		ByteArrayInputStream recvStream =
-				uploadPackV2Setup(null, null, hook, PacketLineIn.end());
+		ByteArrayInputStream recvStream = uploadPackV2Setup(
+				(UploadPack up) -> {up.setProtocolV2Hook(hook);},
+				PacketLineIn.end());
 		PacketLineIn pckIn = new PacketLineIn(recvStream);
 		assertThat(hook.capabilitiesRequest, notNullValue());
 		assertThat(pckIn.readString(), is("version 2"));
@@ -536,7 +537,7 @@ private void checkAdvertisedIfAllowed(String configSection, String configName,
 			String fetchCapability) throws Exception {
 		server.getConfig().setBoolean(configSection, null, configName, true);
 		ByteArrayInputStream recvStream =
-				uploadPackV2Setup(null, null, null, PacketLineIn.end());
+				uploadPackV2Setup(null, PacketLineIn.end());
 		PacketLineIn pckIn = new PacketLineIn(recvStream);
 
 		assertThat(pckIn.readString(), is("version 2"));
@@ -558,7 +559,7 @@ private void checkAdvertisedIfAllowed(String configSection, String configName,
 
 	private void checkUnadvertisedIfUnallowed(String fetchCapability) throws Exception {
 		ByteArrayInputStream recvStream =
-				uploadPackV2Setup(null, null, null, PacketLineIn.end());
+				uploadPackV2Setup(null, PacketLineIn.end());
 		PacketLineIn pckIn = new PacketLineIn(recvStream);
 
 		assertThat(pckIn.readString(), is("version 2"));
@@ -605,7 +606,7 @@ public void testV2CapabilitiesRefInWantNotAdvertisedIfAdvertisingForbidden() thr
 		server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true);
 		server.getConfig().setBoolean("uploadpack", null, "advertiserefinwant", false);
 		ByteArrayInputStream recvStream =
-				uploadPackV2Setup(null, null, null, PacketLineIn.end());
+				uploadPackV2Setup(null, PacketLineIn.end());
 		PacketLineIn pckIn = new PacketLineIn(recvStream);
 
 		assertThat(pckIn.readString(), is("version 2"));
@@ -633,7 +634,8 @@ public void testV2LsRefs() throws Exception {
 		remote.update("refs/tags/tag", tag);
 
 		TestV2Hook hook = new TestV2Hook();
-		ByteArrayInputStream recvStream = uploadPackV2(null, null, hook,
+		ByteArrayInputStream recvStream = uploadPackV2(
+				(UploadPack up) -> {up.setProtocolV2Hook(hook);},
 				"command=ls-refs\n", PacketLineIn.end());
 		PacketLineIn pckIn = new PacketLineIn(recvStream);
 
@@ -768,7 +770,7 @@ public void testV2LsRefsServerOptions() throws Exception {
 				PacketLineIn.end() };
 
 		TestV2Hook testHook = new TestV2Hook();
-		uploadPackV2Setup(null, null, testHook, lines);
+		uploadPackV2Setup((UploadPack up) -> {up.setProtocolV2Hook(testHook);}, lines);
 
 		LsRefsV2Request req = testHook.lsRefsRequest;
 		assertEquals(2, req.getServerOptions().size());
@@ -804,14 +806,18 @@ public void testV2FetchRequestPolicyAdvertised() throws Exception {
 		remote.update("branch1", advertized);
 
 		// This works
-		uploadPackV2(RequestPolicy.ADVERTISED, null, null, "command=fetch\n",
-				PacketLineIn.delimiter(), "want " + advertized.name() + "\n",
-				PacketLineIn.end());
+		uploadPackV2(
+			(UploadPack up) -> {up.setRequestPolicy(RequestPolicy.ADVERTISED);},
+			"command=fetch\n",
+			PacketLineIn.delimiter(),
+			"want " + advertized.name() + "\n",
+			PacketLineIn.end());
 
 		// This doesn't
 		UploadPackInternalServerErrorException e = assertThrows(
 				UploadPackInternalServerErrorException.class,
-				() -> uploadPackV2(RequestPolicy.ADVERTISED, null, null,
+				() -> uploadPackV2(
+						(UploadPack up) -> {up.setRequestPolicy(RequestPolicy.ADVERTISED);},
 						"command=fetch\n", PacketLineIn.delimiter(),
 						"want " + unadvertized.name() + "\n",
 						PacketLineIn.end()));
@@ -828,14 +834,18 @@ public void testV2FetchRequestPolicyReachableCommit() throws Exception {
 		remote.update("branch1", advertized);
 
 		// This works
-		uploadPackV2(RequestPolicy.REACHABLE_COMMIT, null, null,
-				"command=fetch\n", PacketLineIn.delimiter(),
-				"want " + reachable.name() + "\n", PacketLineIn.end());
+		uploadPackV2(
+			(UploadPack up) -> {up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);},
+			"command=fetch\n",
+			PacketLineIn.delimiter(),
+			"want " + reachable.name() + "\n",
+				PacketLineIn.end());
 
 		// This doesn't
 		UploadPackInternalServerErrorException e = assertThrows(
 				UploadPackInternalServerErrorException.class,
-				() -> uploadPackV2(RequestPolicy.REACHABLE_COMMIT, null, null,
+				() -> uploadPackV2(
+						(UploadPack up) -> {up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);},
 						"command=fetch\n", PacketLineIn.delimiter(),
 						"want " + unreachable.name() + "\n",
 						PacketLineIn.end()));
@@ -851,15 +861,25 @@ public void testV2FetchRequestPolicyTip() throws Exception {
 		remote.update("secret", tip);
 
 		// This works
-		uploadPackV2(RequestPolicy.TIP, new RejectAllRefFilter(), null,
-				"command=fetch\n", PacketLineIn.delimiter(),
-				"want " + tip.name() + "\n", PacketLineIn.end());
+		uploadPackV2(
+			(UploadPack up) -> {
+				up.setRequestPolicy(RequestPolicy.TIP);
+				up.setRefFilter(new RejectAllRefFilter());
+			},
+			"command=fetch\n",
+			PacketLineIn.delimiter(),
+			"want " + tip.name() + "\n",
+				PacketLineIn.end());
 
 		// This doesn't
 		UploadPackInternalServerErrorException e = assertThrows(
 				UploadPackInternalServerErrorException.class,
-				() -> uploadPackV2(RequestPolicy.TIP, new RejectAllRefFilter(),
-						null, "command=fetch\n", PacketLineIn.delimiter(),
+				() -> uploadPackV2(
+						(UploadPack up) -> {
+							up.setRequestPolicy(RequestPolicy.TIP);
+							up.setRefFilter(new RejectAllRefFilter());
+						},
+						"command=fetch\n", PacketLineIn.delimiter(),
 						"want " + parentOfTip.name() + "\n",
 						PacketLineIn.end()));
 		assertThat(e.getCause().getMessage(),
@@ -875,16 +895,24 @@ public void testV2FetchRequestPolicyReachableCommitTip() throws Exception {
 		remote.update("secret", tip);
 
 		// This works
-		uploadPackV2(RequestPolicy.REACHABLE_COMMIT_TIP,
-				new RejectAllRefFilter(), null, "command=fetch\n",
+		uploadPackV2(
+				(UploadPack up) -> {
+					up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT_TIP);
+					up.setRefFilter(new RejectAllRefFilter());
+				},
+				"command=fetch\n",
 				PacketLineIn.delimiter(), "want " + parentOfTip.name() + "\n",
 				PacketLineIn.end());
 
 		// This doesn't
 		UploadPackInternalServerErrorException e = assertThrows(
 				UploadPackInternalServerErrorException.class,
-				() -> uploadPackV2(RequestPolicy.REACHABLE_COMMIT_TIP,
-						new RejectAllRefFilter(), null, "command=fetch\n",
+				() -> uploadPackV2(
+						(UploadPack up) -> {
+							up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT_TIP);
+							up.setRefFilter(new RejectAllRefFilter());
+						},
+						"command=fetch\n",
 						PacketLineIn.delimiter(),
 						"want " + unreachable.name() + "\n",
 						PacketLineIn.end()));
@@ -898,9 +926,7 @@ public void testV2FetchRequestPolicyAny() throws Exception {
 
 		// Exercise to make sure that even unreachable commits can be fetched
 		uploadPackV2(
-			RequestPolicy.ANY,
-			null,
-			null,
+			(UploadPack up) -> {up.setRequestPolicy(RequestPolicy.ANY);},
 			"command=fetch\n",
 			PacketLineIn.delimiter(),
 			"want " + unreachable.name() + "\n",
@@ -1500,7 +1526,7 @@ public void testV2FetchServerOptions() throws Exception {
 				PacketLineIn.end() };
 
 		TestV2Hook testHook = new TestV2Hook();
-		uploadPackV2Setup(null, null, testHook, lines);
+		uploadPackV2Setup((UploadPack up) -> {up.setProtocolV2Hook(testHook);}, lines);
 
 		FetchV2Request req = testHook.fetchRequest;
 		assertNotNull(req);
@@ -1584,8 +1610,9 @@ private void uploadV2WithTreeDepthFilter(
 		input.add("done\n");
 		input.add(PacketLineIn.end());
 		ByteArrayInputStream recvStream =
-				uploadPackV2(RequestPolicy.ANY, /*refFilter=*/null,
-							 /*hook=*/null, input.toArray(new String[0]));
+				uploadPackV2(
+						(UploadPack up) -> {up.setRequestPolicy(RequestPolicy.ANY);},
+						input.toArray(new String[0]));
 		PacketLineIn pckIn = new PacketLineIn(recvStream);
 		assertThat(pckIn.readString(), is("packfile"));
 		parsePack(recvStream);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java
index 0bdd6ba..79af1eb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java
@@ -551,7 +551,7 @@ public String getValue(String key) {
 	 * @param config
 	 *            to use
 	 */
-	void setConfig(OpenSshConfig config) {
+	synchronized void setConfig(OpenSshConfig config) {
 		this.config = config;
 	}
 }