Add context to InternalRepositoryException

This commit is contained in:
René Pfeuffer
2018-11-06 09:14:20 +01:00
parent f1c9fe56f0
commit cf05235dfa
33 changed files with 106 additions and 73 deletions

View File

@@ -35,6 +35,14 @@ public class ContextEntry {
public static class ContextBuilder {
private final List<ContextEntry> context = new LinkedList<>();
public static List<ContextEntry> noContext() {
return new ContextBuilder().build();
}
public static List<ContextEntry> only(String type, String id) {
return new ContextBuilder().in(type, id).build();
}
public static ContextBuilder entity(Repository repository) {
return new ContextBuilder().in(repository.getNamespaceAndName());
}

View File

@@ -13,6 +13,11 @@ public abstract class ExceptionWithContext extends RuntimeException {
this.context = context;
}
public ExceptionWithContext(List<ContextEntry> context, String message, Exception cause) {
super(message, cause);
this.context = context;
}
public List<ContextEntry> getContext() {
return unmodifiableList(context);
}

View File

@@ -40,6 +40,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.AlreadyExistsException;
import sonia.scm.ConfigurationException;
import sonia.scm.ContextEntry;
import sonia.scm.io.CommandResult;
import sonia.scm.io.ExtendedCommand;
import sonia.scm.io.FileSystem;
@@ -128,7 +129,7 @@ public abstract class AbstractSimpleRepositoryHandler<C extends RepositoryConfig
try {
fileSystem.destroy(directory);
} catch (IOException e) {
throw new InternalRepositoryException("could not delete repository directory", e);
throw new InternalRepositoryException(ContextEntry.ContextBuilder.entity("directory", directory.toString()).in(repository), "could not delete repository directory", e);
}
cleanupEmptyDirectories(config.getRepositoryDirectory(),
directory.getParentFile());

View File

@@ -1,15 +1,30 @@
package sonia.scm.repository;
public class InternalRepositoryException extends RuntimeException {
public InternalRepositoryException(Throwable ex) {
super(ex);
import sonia.scm.ContextEntry;
import sonia.scm.ExceptionWithContext;
import java.util.List;
public class InternalRepositoryException extends ExceptionWithContext {
public InternalRepositoryException(ContextEntry.ContextBuilder context, String message) {
this(context, message, null);
}
public InternalRepositoryException(String msg, Exception ex) {
super(msg, ex);
public InternalRepositoryException(ContextEntry.ContextBuilder context, String message, Exception cause) {
this(context.build(), message, cause);
}
public InternalRepositoryException(String message) {
super(message);
public InternalRepositoryException(Repository repository, String message, Exception cause) {
this(ContextEntry.ContextBuilder.entity(repository), message, cause);
}
public InternalRepositoryException(List<ContextEntry> context, String message, Exception cause) {
super(context, message, cause);
}
@Override
public String getCode() {
return null;
}
}

View File

@@ -55,6 +55,7 @@ import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.FS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.ContextEntry;
import sonia.scm.util.HttpUtil;
import sonia.scm.util.Util;
import sonia.scm.web.GitUserAgentProvider;
@@ -203,7 +204,7 @@ public final class GitUtil
}
catch (GitAPIException ex)
{
throw new InternalRepositoryException("could not fetch", ex);
throw new InternalRepositoryException(ContextEntry.ContextBuilder.entity("remote", directory.toString()).in(remoteRepository), "could not fetch", ex);
}
}

View File

@@ -160,7 +160,7 @@ public abstract class AbstractGitIncomingOutgoingCommand
}
catch (Exception ex)
{
throw new InternalRepositoryException("could not execute incoming command", ex);
throw new InternalRepositoryException(repository, "could not execute incoming command", ex);
}
finally
{
@@ -200,13 +200,7 @@ public abstract class AbstractGitIncomingOutgoingCommand
{
if (e.getKey().startsWith(prefix))
{
if (ref != null)
{
throw new InternalRepositoryException("could not find remote branch");
}
ref = e.getValue();
break;
}
}

View File

@@ -114,7 +114,7 @@ public abstract class AbstractGitPushOrPullCommand extends AbstractGitCommand
}
catch (Exception ex)
{
throw new InternalRepositoryException("could not execute push/pull command", ex);
throw new InternalRepositoryException(repository, "could not execute push/pull command", ex);
}
return counter;

View File

@@ -55,6 +55,8 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static sonia.scm.ContextEntry.ContextBuilder.entity;
//~--- JDK imports ------------------------------------------------------------
/**
@@ -108,9 +110,8 @@ public class GitBlameCommand extends AbstractGitCommand implements BlameCommand
if (gitBlameResult == null)
{
throw new InternalRepositoryException(
"could not create blame result for path ".concat(
request.getPath()));
throw new InternalRepositoryException(entity("path", request.getPath()).in(repository),
"could not create blame result for path");
}
List<BlameLine> blameLines = new ArrayList<BlameLine>();
@@ -150,7 +151,7 @@ public class GitBlameCommand extends AbstractGitCommand implements BlameCommand
}
catch (GitAPIException ex)
{
throw new InternalRepositoryException("could not create blame view", ex);
throw new InternalRepositoryException(repository, "could not create blame view", ex);
}
return result;

View File

@@ -102,7 +102,7 @@ public class GitBranchesCommand extends AbstractGitCommand
}
catch (GitAPIException ex)
{
throw new InternalRepositoryException("could not read branches", ex);
throw new InternalRepositoryException(repository, "could not read branches", ex);
}
return branches;

View File

@@ -254,7 +254,7 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand
}
catch (Exception ex)
{
throw new InternalRepositoryException("could not create change log", ex);
throw new InternalRepositoryException(repository, "could not create change log", ex);
}
finally
{

View File

@@ -17,6 +17,8 @@ import java.io.IOException;
import java.text.MessageFormat;
import java.util.List;
import static sonia.scm.ContextEntry.ContextBuilder.entity;
@Slf4j
public class GitModificationsCommand extends AbstractGitCommand implements ModificationsCommand {
@@ -26,7 +28,7 @@ public class GitModificationsCommand extends AbstractGitCommand implements Modif
}
private Modifications createModifications(TreeWalk treeWalk, RevCommit commit, RevWalk revWalk, String revision)
throws IOException, UnsupportedModificationTypeException {
throws IOException {
treeWalk.reset();
treeWalk.setRecursive(true);
if (commit.getParentCount() > 0) {
@@ -73,12 +75,7 @@ public class GitModificationsCommand extends AbstractGitCommand implements Modif
}
} catch (IOException ex) {
log.error("could not open repository", ex);
throw new InternalRepositoryException(ex);
} catch (UnsupportedModificationTypeException ex) {
log.error("Unsupported modification type", ex);
throw new InternalRepositoryException(ex);
throw new InternalRepositoryException(entity(repository), "could not open repository", ex);
} finally {
GitUtil.release(revWalk);
GitUtil.close(gitRepository);
@@ -100,7 +97,7 @@ public class GitModificationsCommand extends AbstractGitCommand implements Modif
} else if (type == DiffEntry.ChangeType.DELETE) {
modifications.getRemoved().add(entry.getOldPath());
} else {
throw new UnsupportedModificationTypeException(MessageFormat.format("The modification type: {0} is not supported.", type));
throw new UnsupportedModificationTypeException(entity(repository), MessageFormat.format("The modification type: {0} is not supported.", type));
}
}
}

View File

@@ -249,7 +249,7 @@ public class GitPullCommand extends AbstractGitPushOrPullCommand
}
catch (GitAPIException ex)
{
throw new InternalRepositoryException("error durring pull", ex);
throw new InternalRepositoryException(repository, "error during pull", ex);
}
return response;

View File

@@ -95,7 +95,7 @@ public class GitTagsCommand extends AbstractGitCommand implements TagsCommand
}
catch (GitAPIException ex)
{
throw new InternalRepositoryException("could not read tags from repository", ex);
throw new InternalRepositoryException(repository, "could not read tags from repository", ex);
}
finally
{

View File

@@ -1,9 +1,10 @@
package sonia.scm.repository.spi;
import sonia.scm.ContextEntry;
import sonia.scm.repository.InternalRepositoryException;
public class UnsupportedModificationTypeException extends InternalRepositoryException {
public UnsupportedModificationTypeException(String message) {
super(message);
public UnsupportedModificationTypeException(ContextEntry.ContextBuilder entity, String message) {
super(entity, message);
}
}

View File

@@ -272,7 +272,7 @@ public class AbstractHgHandler
} catch (JAXBException ex) {
logger.error("could not parse result", ex);
throw new InternalRepositoryException("could not parse result", ex);
throw new InternalRepositoryException(repository, "could not parse result", ex);
}
}

View File

@@ -36,6 +36,9 @@ package sonia.scm.repository.spi;
import com.aragost.javahg.commands.ExecutionException;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closeables;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.ContextEntry;
import sonia.scm.repository.InternalRepositoryException;
import sonia.scm.repository.Repository;
import sonia.scm.web.HgUtil;
@@ -46,6 +49,8 @@ import java.io.OutputStream;
public class HgCatCommand extends AbstractCommand implements CatCommand {
private static final Logger log = LoggerFactory.getLogger(HgCatCommand.class);
HgCatCommand(HgCommandContext context, Repository repository) {
super(context, repository);
}
@@ -70,7 +75,8 @@ public class HgCatCommand extends AbstractCommand implements CatCommand {
try {
return cmd.execute(request.getPath());
} catch (ExecutionException e) {
throw new InternalRepositoryException(e);
log.error("could not execute cat command", e);
throw new InternalRepositoryException(ContextEntry.ContextBuilder.entity(getRepository()), "could not execute cat command", e);
}
}
}

View File

@@ -103,7 +103,7 @@ public class HgIncomingCommand extends AbstractCommand
}
else
{
throw new InternalRepositoryException("could not execute incoming command", ex);
throw new InternalRepositoryException(getRepository(), "could not execute incoming command", ex);
}
}

View File

@@ -103,7 +103,7 @@ public class HgOutgoingCommand extends AbstractCommand
}
else
{
throw new InternalRepositoryException("could not execute outgoing command", ex);
throw new InternalRepositoryException(getRepository(), "could not execute outgoing command", ex);
}
}

View File

@@ -97,7 +97,7 @@ public class HgPullCommand extends AbstractHgPushOrPullCommand
}
catch (ExecutionException ex)
{
throw new InternalRepositoryException("could not execute push command", ex);
throw new InternalRepositoryException(getRepository(), "could not execute push command", ex);
}
return new PullResponse(result.size());

View File

@@ -97,7 +97,7 @@ public class HgPushCommand extends AbstractHgPushOrPullCommand
}
catch (ExecutionException ex)
{
throw new InternalRepositoryException("could not execute push command", ex);
throw new InternalRepositoryException(getRepository(), "could not execute push command", ex);
}
return new PushResponse(result.size());

View File

@@ -56,6 +56,8 @@ import sonia.scm.util.Util;
import java.io.File;
import static sonia.scm.ContextEntry.ContextBuilder.entity;
//~--- JDK imports ------------------------------------------------------------
/**
@@ -173,7 +175,8 @@ public class SvnRepositoryHandler
}
catch (SVNException ex)
{
throw new InternalRepositoryException(ex);
logger.error("could not create svn repository", ex);
throw new InternalRepositoryException(entity(repository), "could not create repository", ex);
}
finally
{

View File

@@ -98,7 +98,7 @@ public class SvnBlameCommand extends AbstractSvnCommand implements BlameCommand
}
catch (SVNException ex)
{
throw new InternalRepositoryException("could not create blame result", ex);
throw new InternalRepositoryException(repository, "could not create blame result", ex);
}
return new BlameResult(blameLines.size(), blameLines);

View File

@@ -137,7 +137,7 @@ public class SvnCatCommand extends AbstractSvnCommand implements CatCommand
} else if (SVNErrorCode.FS_NO_SUCH_REVISION.getCode() == svnErrorCode) {
throw notFound(entity("Revision", request.getRevision()).in(repository));
} else {
throw new InternalRepositoryException("could not get content from revision", ex);
throw new InternalRepositoryException(repository, "could not get content from revision", ex);
}
}
@@ -157,7 +157,7 @@ public class SvnCatCommand extends AbstractSvnCommand implements CatCommand
}
catch (SVNException ex)
{
throw new InternalRepositoryException("could not get content from transaction", ex);
throw new InternalRepositoryException(repository, "could not get content from transaction", ex);
}
finally
{

View File

@@ -120,7 +120,7 @@ public class SvnDiffCommand extends AbstractSvnCommand implements DiffCommand
}
catch (SVNException ex)
{
throw new InternalRepositoryException("could not create diff", ex);
throw new InternalRepositoryException(repository, "could not create diff", ex);
}
finally
{

View File

@@ -97,7 +97,7 @@ public class SvnLogCommand extends AbstractSvnCommand implements LogCommand
}
catch (SVNException ex)
{
throw new InternalRepositoryException("could not open repository", ex);
throw new InternalRepositoryException(repository, "could not open repository", ex);
}
return changeset;
@@ -139,7 +139,7 @@ public class SvnLogCommand extends AbstractSvnCommand implements LogCommand
}
catch (SVNException ex)
{
throw new InternalRepositoryException("could not open repository", ex);
throw new InternalRepositoryException(repository, "could not open repository", ex);
}
return changesets;

View File

@@ -34,7 +34,7 @@ public class SvnModificationsCommand extends AbstractSvnCommand implements Modif
modifications = SvnUtil.createModifications(entries.iterator().next(), revision);
}
} catch (SVNException ex) {
throw new InternalRepositoryException("could not open repository", ex);
throw new InternalRepositoryException(repository, "could not open repository", ex);
}
return modifications;
}

View File

@@ -26,7 +26,7 @@ public class ContextualExceptionMapper<E extends ExceptionWithContext> implement
@Override
public Response toResponse(E exception) {
logger.debug("map {} to status code {}", type.getSimpleName(), status.getStatusCode());
logger.debug("map {} to status code {}", type.getSimpleName(), status.getStatusCode(), exception);
return Response.status(status)
.entity(mapper.map(exception))
.type(VndMediaType.ERROR_TYPE)

View File

@@ -7,7 +7,6 @@ import lombok.extern.slf4j.Slf4j;
import sonia.scm.PageResult;
import sonia.scm.repository.Changeset;
import sonia.scm.repository.ChangesetPagingResult;
import sonia.scm.repository.InternalRepositoryException;
import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.Repository;
import sonia.scm.repository.api.RepositoryService;
@@ -24,6 +23,9 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import java.io.IOException;
import static sonia.scm.ContextEntry.ContextBuilder.entity;
import static sonia.scm.NotFoundException.notFound;
@Slf4j
public class FileHistoryRootResource {
@@ -66,7 +68,8 @@ public class FileHistoryRootResource {
@PathParam("path") String path,
@DefaultValue("0") @QueryParam("page") int page,
@DefaultValue("10") @QueryParam("pageSize") int pageSize) throws IOException {
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name);
try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) {
log.info("Get changesets of the file {} and revision {}", path, revision);
Repository repository = repositoryService.getRepository();
ChangesetPagingResult changesets = new PagedLogCommandBuilder(repositoryService)
@@ -80,9 +83,9 @@ public class FileHistoryRootResource {
PageResult<Changeset> pageResult = new PageResult<>(changesets.getChangesets(), changesets.getTotal());
return Response.ok(fileHistoryCollectionToDtoMapper.map(page, pageSize, pageResult, repository, revision, path)).build();
} else {
String message = String.format("for the revision %s and the file %s there is no changesets", revision, path);
String message = String.format("for the revision %s and the file %s there are no changesets", revision, path);
log.error(message);
throw new InternalRepositoryException(message);
throw notFound(entity("path", path).in("revision", revision).in(namespaceAndName));
}
}
}

View File

@@ -1,15 +1,17 @@
package sonia.scm.api.v2.resources;
import sonia.scm.api.rest.StatusExceptionMapper;
import sonia.scm.api.rest.ContextualExceptionMapper;
import sonia.scm.repository.InternalRepositoryException;
import javax.inject.Inject;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
@Provider
public class InternalRepositoryExceptionMapper extends StatusExceptionMapper<InternalRepositoryException> {
public class InternalRepositoryExceptionMapper extends ContextualExceptionMapper<InternalRepositoryException> {
public InternalRepositoryExceptionMapper() {
super(InternalRepositoryException.class, Response.Status.INTERNAL_SERVER_ERROR);
@Inject
public InternalRepositoryExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) {
super(InternalRepositoryException.class, Response.Status.INTERNAL_SERVER_ERROR, mapper);
}
}

View File

@@ -143,7 +143,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager {
try {
getHandler(newRepository).create(newRepository);
} catch (AlreadyExistsException e) {
throw new InternalRepositoryException("directory for repository does already exist", e);
throw new InternalRepositoryException(repository, "directory for repository does already exist", e);
}
}
fireEvent(HandlerEventType.BEFORE_CREATE, newRepository);
@@ -353,9 +353,9 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager {
RepositoryHandler handler = handlerMap.get(type);
if (handler == null) {
throw new InternalRepositoryException("could not find handler for " + type);
throw new InternalRepositoryException(entity(repository), "could not find handler for " + type);
} else if (!handler.isConfigured()) {
throw new InternalRepositoryException("handler is not configured for type " + type);
throw new InternalRepositoryException(entity(repository), "handler is not configured for type " + type);
}
return handler;

View File

@@ -17,7 +17,7 @@ public class DispatcherMock {
dispatcher.getProviderFactory().register(new AlreadyExistsExceptionMapper(mapper));
dispatcher.getProviderFactory().register(new ConcurrentModificationExceptionMapper(mapper));
dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(InternalRepositoryExceptionMapper.class);
dispatcher.getProviderFactory().register(new InternalRepositoryExceptionMapper(mapper));
dispatcher.getProviderFactory().register(new ChangePasswordNotAllowedExceptionMapper(mapper));
dispatcher.getProviderFactory().register(new InvalidPasswordExceptionMapper(mapper));
dispatcher.getProviderFactory().registerProvider(IllegalArgumentExceptionMapper.class);

View File

@@ -8,7 +8,6 @@ import org.apache.shiro.util.ThreadContext;
import org.apache.shiro.util.ThreadState;
import org.assertj.core.util.Lists;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.junit.After;
@@ -18,9 +17,8 @@ import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.ContextEntry;
import sonia.scm.NotFoundException;
import sonia.scm.api.rest.AuthorizationExceptionMapper;
import sonia.scm.api.v2.NotFoundExceptionMapper;
import sonia.scm.repository.Changeset;
import sonia.scm.repository.ChangesetPagingResult;
import sonia.scm.repository.InternalRepositoryException;
@@ -168,7 +166,7 @@ public class FileHistoryResourceTest extends RepositoryTestBase {
when(logCommandBuilder.setPagingLimit(anyInt())).thenReturn(logCommandBuilder);
when(logCommandBuilder.setStartChangeset(eq(id))).thenReturn(logCommandBuilder);
when(logCommandBuilder.setPath(eq(path))).thenReturn(logCommandBuilder);
when(logCommandBuilder.getChangesets()).thenThrow(InternalRepositoryException.class);
when(logCommandBuilder.getChangesets()).thenThrow(new InternalRepositoryException(ContextEntry.ContextBuilder.noContext(), "", new RuntimeException()));
MockHttpRequest request = MockHttpRequest
.get(FILE_HISTORY_URL + id + "/" + path)
@@ -179,7 +177,7 @@ public class FileHistoryResourceTest extends RepositoryTestBase {
}
@Test
public void shouldGet500OnNullChangesets() throws Exception {
public void shouldGet404OnNullChangesets() throws Exception {
String id = "revision_123";
String path = "root_dir/sub_dir/file-to-inspect.txt";
@@ -194,6 +192,6 @@ public class FileHistoryResourceTest extends RepositoryTestBase {
.accept(VndMediaType.CHANGESET_COLLECTION);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(500, response.getStatus());
assertEquals(404, response.getStatus());
}
}

View File

@@ -8,7 +8,6 @@ import org.apache.shiro.util.ThreadContext;
import org.apache.shiro.util.ThreadState;
import org.assertj.core.util.Lists;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.junit.After;
@@ -18,8 +17,6 @@ import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.api.rest.AuthorizationExceptionMapper;
import sonia.scm.api.v2.NotFoundExceptionMapper;
import sonia.scm.repository.InternalRepositoryException;
import sonia.scm.repository.Modifications;
import sonia.scm.repository.NamespaceAndName;
@@ -38,6 +35,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static sonia.scm.ContextEntry.ContextBuilder.noContext;
@Slf4j
@RunWith(MockitoJUnitRunner.Silent.class)
@@ -72,7 +70,7 @@ public class ModificationsResourceTest extends RepositoryTestBase {
@Before
public void prepareEnvironment() throws Exception {
public void prepareEnvironment() {
modificationsRootResource = new ModificationsRootResource(serviceFactory, modificationsToDtoMapper);
super.modificationsRootResource = Providers.of(modificationsRootResource);
dispatcher = DispatcherMock.createDispatcher(getRepositoryRootResource());
@@ -106,7 +104,7 @@ public class ModificationsResourceTest extends RepositoryTestBase {
@Test
public void shouldGet500OnModificationsCommandError() throws Exception {
when(modificationsCommandBuilder.revision(any())).thenReturn(modificationsCommandBuilder);
when(modificationsCommandBuilder.getModifications()).thenThrow(InternalRepositoryException.class);
when(modificationsCommandBuilder.getModifications()).thenThrow(new InternalRepositoryException(noContext(), "", new RuntimeException()));
MockHttpRequest request = MockHttpRequest
.get(MODIFICATIONS_URL + "revision")