transport: Add ReceiveCommandErrorHandler

This gives a chance to handle an exception for a user. For example, when
an IOException is thrown while executing
`walk.parseAny(cmd.getNewId())`, it's always handled as
REJECTED_MISSING_OBJECT. However, IOException can mean a Git storage IO
error. By introducing an error handler class, a user can add a custom
error handler for these cases.

Change-Id: I3e03a536e1d8e137cb0f6e596d71642e72adde9e
Signed-off-by: Masaya Suzuki <masayasuzuki@google.com>
This commit is contained in:
Masaya Suzuki 2019-11-07 18:22:17 -08:00
parent 19293add84
commit a91489f4a8
2 changed files with 111 additions and 17 deletions

View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2019, Google LLC and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package org.eclipse.jgit.transport;
import java.io.IOException;
import java.util.List;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.transport.ReceiveCommand.Result;
/**
* Exception handler for processing {@link ReceiveCommand}.
*
* @since 5.7
*/
public interface ReceiveCommandErrorHandler {
/**
* Handle an exception thrown while validating the new commit ID.
*
* @param cmd
* offending command
* @param e
* exception thrown
*/
default void handleNewIdValidationException(ReceiveCommand cmd,
IOException e) {
cmd.setResult(Result.REJECTED_MISSING_OBJECT, cmd.getNewId().name());
}
/**
* Handle an exception thrown while validating the old commit ID.
*
* @param cmd
* offending command
* @param e
* exception thrown
*/
default void handleOldIdValidationException(ReceiveCommand cmd,
IOException e) {
cmd.setResult(Result.REJECTED_MISSING_OBJECT, cmd.getOldId().name());
}
/**
* Handle an exception thrown while checking if the update is fast-forward.
*
* @param cmd
* offending command
* @param e
* exception thrown
*/
default void handleFastForwardCheckException(ReceiveCommand cmd,
IOException e) {
if (e instanceof MissingObjectException) {
cmd.setResult(Result.REJECTED_MISSING_OBJECT, e.getMessage());
} else {
cmd.setResult(Result.REJECTED_OTHER_REASON);
}
}
/**
* Handle an exception thrown while checking if the update is fast-forward.
*
* @param cmds
* commands being processed
* @param e
* exception thrown
*/
default void handleBatchRefUpdateException(List<ReceiveCommand> cmds,
IOException e) {
for (ReceiveCommand cmd : cmds) {
if (cmd.getResult() == Result.NOT_ATTEMPTED) {
cmd.reject(e);
}
}
}
}

View File

@ -295,6 +295,10 @@ public Set<String> getCapabilities() {
/** Hook to validate the update commands before execution. */ /** Hook to validate the update commands before execution. */
private PreReceiveHook preReceive; private PreReceiveHook preReceive;
private ReceiveCommandErrorHandler receiveCommandErrorHandler = new ReceiveCommandErrorHandler() {
// Use the default implementation.
};
/** Hook to report on the commands after execution. */ /** Hook to report on the commands after execution. */
private PostReceiveHook postReceive; private PostReceiveHook postReceive;
@ -1020,6 +1024,17 @@ public List<ReceiveCommand> getAllCommands() {
return Collections.unmodifiableList(commands); return Collections.unmodifiableList(commands);
} }
/**
* Set an error handler for {@link ReceiveCommand}.
*
* @param receiveCommandErrorHandler
* @since 5.7
*/
public void setReceiveCommandErrorHandler(
ReceiveCommandErrorHandler receiveCommandErrorHandler) {
this.receiveCommandErrorHandler = receiveCommandErrorHandler;
}
/** /**
* Send an error message to the client. * Send an error message to the client.
* <p> * <p>
@ -1726,16 +1741,16 @@ private void validateCommands() {
try { try {
oldObj = walk.parseAny(cmd.getOldId()); oldObj = walk.parseAny(cmd.getOldId());
} catch (IOException e) { } catch (IOException e) {
cmd.setResult(Result.REJECTED_MISSING_OBJECT, receiveCommandErrorHandler
cmd.getOldId().name()); .handleOldIdValidationException(cmd, e);
continue; continue;
} }
try { try {
newObj = walk.parseAny(cmd.getNewId()); newObj = walk.parseAny(cmd.getNewId());
} catch (IOException e) { } catch (IOException e) {
cmd.setResult(Result.REJECTED_MISSING_OBJECT, receiveCommandErrorHandler
cmd.getNewId().name()); .handleNewIdValidationException(cmd, e);
continue; continue;
} }
@ -1743,16 +1758,14 @@ private void validateCommands() {
&& newObj instanceof RevCommit) { && newObj instanceof RevCommit) {
try { try {
if (walk.isMergedInto((RevCommit) oldObj, if (walk.isMergedInto((RevCommit) oldObj,
(RevCommit) newObj)) (RevCommit) newObj)) {
cmd.setTypeFastForwardUpdate(); cmd.setTypeFastForwardUpdate();
else } else {
cmd.setType( cmd.setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
ReceiveCommand.Type.UPDATE_NONFASTFORWARD); }
} catch (MissingObjectException e) {
cmd.setResult(Result.REJECTED_MISSING_OBJECT,
e.getMessage());
} catch (IOException e) { } catch (IOException e) {
cmd.setResult(Result.REJECTED_OTHER_REASON); receiveCommandErrorHandler
.handleFastForwardCheckException(cmd, e);
} }
} else { } else {
cmd.setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD); cmd.setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
@ -1831,11 +1844,9 @@ private void executeCommands() {
try { try {
batch.setPushCertificate(getPushCertificate()); batch.setPushCertificate(getPushCertificate());
batch.execute(walk, updating); batch.execute(walk, updating);
} catch (IOException err) { } catch (IOException e) {
for (ReceiveCommand cmd : toApply) { receiveCommandErrorHandler.handleBatchRefUpdateException(toApply,
if (cmd.getResult() == Result.NOT_ATTEMPTED) e);
cmd.reject(err);
}
} }
} }