diff --git a/scm-core/src/main/java/sonia/scm/ContextEntry.java b/scm-core/src/main/java/sonia/scm/ContextEntry.java new file mode 100644 index 0000000000..2dbccbacf7 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/ContextEntry.java @@ -0,0 +1,27 @@ +package sonia.scm; + +import sonia.scm.util.AssertUtil; + +public class ContextEntry { + private final String type; + private final String id; + + ContextEntry(Class type, String id) { + this(type.getSimpleName(), id); + } + + ContextEntry(String type, String id) { + AssertUtil.assertIsNotEmpty(type); + AssertUtil.assertIsNotEmpty(id); + this.type = type; + this.id = id; + } + + public String getType () { + return type; + } + + public String getId () { + return id; + } + } diff --git a/scm-core/src/main/java/sonia/scm/NotFoundException.java b/scm-core/src/main/java/sonia/scm/NotFoundException.java index 37546be0b8..929fa617ad 100644 --- a/scm-core/src/main/java/sonia/scm/NotFoundException.java +++ b/scm-core/src/main/java/sonia/scm/NotFoundException.java @@ -1,10 +1,72 @@ package sonia.scm; +import sonia.scm.repository.Repository; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import static java.util.Collections.unmodifiableList; +import static java.util.stream.Collectors.joining; + public class NotFoundException extends RuntimeException { - public NotFoundException(String type, String id) { - super(type + " with id '" + id + "' not found"); + + private final List context; + + public NotFoundException(Class type, String id) { + this.context = Collections.singletonList(new ContextEntry(type, id)); } - public NotFoundException() { + public NotFoundException(String type, String id) { + this.context = Collections.singletonList(new ContextEntry(type, id)); } + + private NotFoundException(List context) { + this.context = context; + } + + public static NotFoundExceptionBuilder notFound(Class type, String id) { + NotFoundExceptionBuilder builder = new NotFoundExceptionBuilder(); + return builder.in(type, id); + } + + public static NotFoundExceptionBuilder notFound(String type, String id) { + NotFoundExceptionBuilder builder = new NotFoundExceptionBuilder(); + return builder.in(type, id); + } + + public List getContext() { + return unmodifiableList(context); + } + + @Override + public String getMessage() { + return context.stream() + .map(c -> c.getType().toLowerCase() + " with id " + c.getId()) + .collect(joining(" in ", "could not find ", "")); + } + + public static class NotFoundExceptionBuilder { + private final List context = new LinkedList<>(); + + public NotFoundExceptionBuilder in(Repository repository) { + this.in(Repository.class, repository.getNamespaceAndName().logString()); + return this; + } + + public NotFoundExceptionBuilder in(Class type, String id) { + this.context.add(new ContextEntry(type, id)); + return this; + } + + public NotFoundExceptionBuilder in(String type, String id) { + this.context.add(new ContextEntry(type, id)); + return this; + } + + public NotFoundException build() { + return new NotFoundException(context); + } + } + } diff --git a/scm-core/src/main/java/sonia/scm/repository/NamespaceAndName.java b/scm-core/src/main/java/sonia/scm/repository/NamespaceAndName.java index 7b71078f67..fd0a72ad7c 100644 --- a/scm-core/src/main/java/sonia/scm/repository/NamespaceAndName.java +++ b/scm-core/src/main/java/sonia/scm/repository/NamespaceAndName.java @@ -25,9 +25,13 @@ public class NamespaceAndName implements Comparable { return name; } + public String logString() { + return getNamespace() + "/" + getName(); + } + @Override public String toString() { - return getNamespace() + "/" + getName(); + return logString(); } @Override diff --git a/scm-core/src/main/java/sonia/scm/repository/PathNotFoundException.java b/scm-core/src/main/java/sonia/scm/repository/PathNotFoundException.java deleted file mode 100644 index ed62a5967c..0000000000 --- a/scm-core/src/main/java/sonia/scm/repository/PathNotFoundException.java +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of SCM-Manager; 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 REGENTS 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. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.repository; - -//~--- non-JDK imports -------------------------------------------------------- - -import sonia.scm.NotFoundException; -import sonia.scm.util.Util; - -/** - * Signals that the specified path could be found. - * - * @author Sebastian Sdorra - */ -public class PathNotFoundException extends NotFoundException -{ - - /** Field description */ - private static final long serialVersionUID = 4629690181172951809L; - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs a new {@link PathNotFoundException} - * with the specified path. - * - * - * @param path path which could not be found - */ - public PathNotFoundException(String path) - { - super("path", Util.nonNull(path)); - this.path = Util.nonNull(path); - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Return the path which could not be found. - * - * - * @return path which could not be found - */ - public String getPath() - { - return path; - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private String path; -} diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryNotFoundException.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryNotFoundException.java index 9dd866daa4..9863aeddd3 100644 --- a/scm-core/src/main/java/sonia/scm/repository/RepositoryNotFoundException.java +++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryNotFoundException.java @@ -45,7 +45,7 @@ public class RepositoryNotFoundException extends NotFoundException { private static final long serialVersionUID = -6583078808900520166L; - private static final String TYPE_REPOSITORY = "repository"; + private static final String TYPE_REPOSITORY = "Repository"; //~--- constructors --------------------------------------------------------- @@ -55,7 +55,7 @@ public class RepositoryNotFoundException extends NotFoundException * */ public RepositoryNotFoundException(Repository repository) { - super(TYPE_REPOSITORY, repository.getName() + "/" + repository.getNamespace()); + super(Repository.class, repository.getNamespaceAndName().logString()); } public RepositoryNotFoundException(String repositoryId) { @@ -63,6 +63,6 @@ public class RepositoryNotFoundException extends NotFoundException } public RepositoryNotFoundException(NamespaceAndName namespaceAndName) { - super(TYPE_REPOSITORY, namespaceAndName.toString()); + super(Repository.class, namespaceAndName.logString()); } } diff --git a/scm-core/src/main/java/sonia/scm/repository/RevisionNotFoundException.java b/scm-core/src/main/java/sonia/scm/repository/RevisionNotFoundException.java deleted file mode 100644 index 4185e3223d..0000000000 --- a/scm-core/src/main/java/sonia/scm/repository/RevisionNotFoundException.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of SCM-Manager; 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 REGENTS 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. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.repository; - -//~--- non-JDK imports -------------------------------------------------------- - -import sonia.scm.NotFoundException; -import sonia.scm.util.Util; - -/** - * Signals that the specified revision could be found. - * - * @author Sebastian Sdorra - */ -public class RevisionNotFoundException extends NotFoundException { - - /** Field description */ - private static final long serialVersionUID = -5594008535358811998L; - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs a new {@link RevisionNotFoundException} - * with the specified revision. - * - * - * @param revision revision which could not be found - */ - public RevisionNotFoundException(String revision) - { - super("revision", revision); - this.revision = Util.nonNull(revision); - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Return the revision which could not be found. - * - * - * @return revision which could not be found - */ - public String getRevision() - { - return revision; - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private String revision; -} diff --git a/scm-core/src/main/java/sonia/scm/repository/api/BrowseCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/BrowseCommandBuilder.java index fe39aa0a05..3e5166c2f4 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/BrowseCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/BrowseCommandBuilder.java @@ -46,7 +46,6 @@ import sonia.scm.repository.FileObjectNameComparator; import sonia.scm.repository.PreProcessorUtil; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryCacheKey; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.spi.BrowseCommand; import sonia.scm.repository.spi.BrowseCommandRequest; @@ -138,7 +137,7 @@ public final class BrowseCommandBuilder * * @throws IOException */ - public BrowserResult getBrowserResult() throws IOException, RevisionNotFoundException { + public BrowserResult getBrowserResult() throws IOException { BrowserResult result = null; if (disableCache) diff --git a/scm-core/src/main/java/sonia/scm/repository/api/CatCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/CatCommandBuilder.java index bf896efed8..d1e0cbc5f1 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/CatCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/CatCommandBuilder.java @@ -37,9 +37,7 @@ import com.google.common.base.Preconditions; import com.google.common.base.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.repository.PathNotFoundException; import sonia.scm.repository.Repository; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.spi.CatCommand; import sonia.scm.repository.spi.CatCommandRequest; import sonia.scm.util.IOUtil; @@ -107,7 +105,7 @@ public final class CatCommandBuilder * @param outputStream output stream for the content * @param path file path */ - public void retriveContent(OutputStream outputStream, String path) throws IOException, PathNotFoundException, RevisionNotFoundException { + public void retriveContent(OutputStream outputStream, String path) throws IOException { getCatResult(outputStream, path); } @@ -116,7 +114,7 @@ public final class CatCommandBuilder * * @param path file path */ - public InputStream getStream(String path) throws IOException, PathNotFoundException, RevisionNotFoundException { + public InputStream getStream(String path) throws IOException { Preconditions.checkArgument(!Strings.isNullOrEmpty(path), "path is required"); @@ -139,7 +137,7 @@ public final class CatCommandBuilder * * @throws IOException */ - public String getContent(String path) throws IOException, PathNotFoundException, RevisionNotFoundException { + public String getContent(String path) throws IOException { String content = null; ByteArrayOutputStream baos = null; @@ -186,7 +184,7 @@ public final class CatCommandBuilder * @throws IOException */ private void getCatResult(OutputStream outputStream, String path) - throws IOException, PathNotFoundException, RevisionNotFoundException { + throws IOException { Preconditions.checkNotNull(outputStream, "OutputStream is required"); Preconditions.checkArgument(!Strings.isNullOrEmpty(path), "path is required"); diff --git a/scm-core/src/main/java/sonia/scm/repository/api/DiffCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/DiffCommandBuilder.java index c0e9e3f622..d8536f9abe 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/DiffCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/DiffCommandBuilder.java @@ -38,7 +38,6 @@ package sonia.scm.repository.api; import com.google.common.base.Preconditions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.spi.DiffCommand; import sonia.scm.repository.spi.DiffCommandRequest; import sonia.scm.util.IOUtil; @@ -104,7 +103,7 @@ public final class DiffCommandBuilder * * @throws IOException */ - public DiffCommandBuilder retriveContent(OutputStream outputStream) throws IOException, RevisionNotFoundException { + public DiffCommandBuilder retriveContent(OutputStream outputStream) throws IOException { getDiffResult(outputStream); return this; @@ -119,7 +118,7 @@ public final class DiffCommandBuilder * * @throws IOException */ - public String getContent() throws IOException, RevisionNotFoundException { + public String getContent() throws IOException { String content = null; ByteArrayOutputStream baos = null; @@ -199,7 +198,7 @@ public final class DiffCommandBuilder * * @throws IOException */ - private void getDiffResult(OutputStream outputStream) throws IOException, RevisionNotFoundException { + private void getDiffResult(OutputStream outputStream) throws IOException { Preconditions.checkNotNull(outputStream, "OutputStream is required"); Preconditions.checkArgument(request.isValid(), "path and/or revision is required"); diff --git a/scm-core/src/main/java/sonia/scm/repository/api/LogCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/LogCommandBuilder.java index 9c782a781b..f718df5b6e 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/LogCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/LogCommandBuilder.java @@ -46,7 +46,6 @@ import sonia.scm.repository.ChangesetPagingResult; import sonia.scm.repository.PreProcessorUtil; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryCacheKey; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.spi.LogCommand; import sonia.scm.repository.spi.LogCommandRequest; @@ -165,7 +164,7 @@ public final class LogCommandBuilder * * @throws IOException */ - public Changeset getChangeset(String id) throws IOException, RevisionNotFoundException { + public Changeset getChangeset(String id) throws IOException { Changeset changeset; if (disableCache) @@ -224,7 +223,7 @@ public final class LogCommandBuilder * * @throws IOException */ - public ChangesetPagingResult getChangesets() throws IOException, RevisionNotFoundException { + public ChangesetPagingResult getChangesets() throws IOException { ChangesetPagingResult cpr; if (disableCache) diff --git a/scm-core/src/main/java/sonia/scm/repository/api/ModificationsCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/ModificationsCommandBuilder.java index 6459b47cd5..498746cc60 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/ModificationsCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/ModificationsCommandBuilder.java @@ -13,7 +13,6 @@ import sonia.scm.repository.Modifications; import sonia.scm.repository.PreProcessorUtil; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryCacheKey; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.spi.ModificationsCommand; import sonia.scm.repository.spi.ModificationsCommandRequest; @@ -67,7 +66,7 @@ public final class ModificationsCommandBuilder { return this; } - public Modifications getModifications() throws IOException, RevisionNotFoundException { + public Modifications getModifications() throws IOException { Modifications modifications; if (disableCache) { log.info("Get modifications for {} with disabled cache", request); diff --git a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java index fbb1ee6b58..7160c2723d 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java @@ -152,37 +152,6 @@ public final class RepositoryServiceFactory //~--- methods -------------------------------------------------------------- - /** - * Creates a new RepositoryService for the given repository. - * - * - * @param repositoryId id of the repository - * - * @return a implementation of RepositoryService - * for the given type of repository - * - * @throws RepositoryNotFoundException if no repository - * with the given id is available - * @throws RepositoryServiceNotFoundException if no repository service - * implementation for this kind of repository is available - * @throws IllegalArgumentException if the repository id is null or empty - * @throws ScmSecurityException if current user has not read permissions - * for that repository - */ - public RepositoryService create(String repositoryId) throws RepositoryNotFoundException { - Preconditions.checkArgument(!Strings.isNullOrEmpty(repositoryId), - "a non empty repositoryId is required"); - - Repository repository = repositoryManager.get(repositoryId); - - if (repository == null) - { - throw new RepositoryNotFoundException(repositoryId); - } - - return create(repository); - } - /** * Creates a new RepositoryService for the given repository. * diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/BrowseCommand.java b/scm-core/src/main/java/sonia/scm/repository/spi/BrowseCommand.java index 2c9fff589c..ee37d6243e 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/BrowseCommand.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/BrowseCommand.java @@ -36,7 +36,6 @@ package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- import sonia.scm.repository.BrowserResult; -import sonia.scm.repository.RevisionNotFoundException; import java.io.IOException; @@ -60,4 +59,5 @@ public interface BrowseCommand * * @throws IOException */ - BrowserResult getBrowserResult(BrowseCommandRequest request) throws IOException, RevisionNotFoundException;} + BrowserResult getBrowserResult(BrowseCommandRequest request) throws IOException; +} diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/CatCommand.java b/scm-core/src/main/java/sonia/scm/repository/spi/CatCommand.java index 06f242783b..81600230db 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/CatCommand.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/CatCommand.java @@ -33,9 +33,6 @@ package sonia.scm.repository.spi; -import sonia.scm.repository.PathNotFoundException; -import sonia.scm.repository.RevisionNotFoundException; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -47,7 +44,7 @@ import java.io.OutputStream; */ public interface CatCommand { - void getCatResult(CatCommandRequest request, OutputStream output) throws IOException, RevisionNotFoundException, PathNotFoundException; + void getCatResult(CatCommandRequest request, OutputStream output) throws IOException; - InputStream getCatResultStream(CatCommandRequest request) throws IOException, RevisionNotFoundException, PathNotFoundException; + InputStream getCatResultStream(CatCommandRequest request) throws IOException; } diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/DiffCommand.java b/scm-core/src/main/java/sonia/scm/repository/spi/DiffCommand.java index 105f4e48e1..bba42cd86d 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/DiffCommand.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/DiffCommand.java @@ -33,8 +33,6 @@ package sonia.scm.repository.spi; -import sonia.scm.repository.RevisionNotFoundException; - import java.io.IOException; import java.io.OutputStream; @@ -56,5 +54,5 @@ public interface DiffCommand * @throws IOException * @throws RuntimeException */ - public void getDiffResult(DiffCommandRequest request, OutputStream output) throws IOException, RevisionNotFoundException; + public void getDiffResult(DiffCommandRequest request, OutputStream output) throws IOException; } diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/LogCommand.java b/scm-core/src/main/java/sonia/scm/repository/spi/LogCommand.java index e4a7f3437b..f4babcee72 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/LogCommand.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/LogCommand.java @@ -37,7 +37,6 @@ package sonia.scm.repository.spi; import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; -import sonia.scm.repository.RevisionNotFoundException; import java.io.IOException; @@ -50,7 +49,7 @@ import java.io.IOException; */ public interface LogCommand { - Changeset getChangeset(String id) throws IOException, RevisionNotFoundException; + Changeset getChangeset(String id) throws IOException; - ChangesetPagingResult getChangesets(LogCommandRequest request) throws IOException, RevisionNotFoundException; + ChangesetPagingResult getChangesets(LogCommandRequest request) throws IOException; } diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/ModificationsCommand.java b/scm-core/src/main/java/sonia/scm/repository/spi/ModificationsCommand.java index e9b40e8a17..322468f827 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/ModificationsCommand.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/ModificationsCommand.java @@ -32,7 +32,6 @@ package sonia.scm.repository.spi; import sonia.scm.repository.Modifications; -import sonia.scm.repository.RevisionNotFoundException; import java.io.IOException; @@ -46,8 +45,8 @@ import java.io.IOException; */ public interface ModificationsCommand { - Modifications getModifications(String revision) throws IOException, RevisionNotFoundException; + Modifications getModifications(String revision) throws IOException; - Modifications getModifications(ModificationsCommandRequest request) throws IOException, RevisionNotFoundException; + Modifications getModifications(ModificationsCommandRequest request) throws IOException; } diff --git a/scm-core/src/main/java/sonia/scm/web/VndMediaType.java b/scm-core/src/main/java/sonia/scm/web/VndMediaType.java index 7b6d5cb039..e2a2218d34 100644 --- a/scm-core/src/main/java/sonia/scm/web/VndMediaType.java +++ b/scm-core/src/main/java/sonia/scm/web/VndMediaType.java @@ -44,6 +44,7 @@ public class VndMediaType { public static final String ME = PREFIX + "me" + SUFFIX; public static final String SOURCE = PREFIX + "source" + SUFFIX; + public static final String ERROR_TYPE = PREFIX + "error" + SUFFIX; private VndMediaType() { } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java index f194796bdc..2be4c34fb8 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java @@ -50,13 +50,12 @@ import org.eclipse.jgit.treewalk.filter.PathFilter; import org.eclipse.jgit.treewalk.filter.TreeFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.NotFoundException; import sonia.scm.repository.BrowserResult; import sonia.scm.repository.FileObject; import sonia.scm.repository.GitSubModuleParser; import sonia.scm.repository.GitUtil; -import sonia.scm.repository.PathNotFoundException; import sonia.scm.repository.Repository; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.SubRepository; import sonia.scm.util.Util; @@ -103,7 +102,7 @@ public class GitBrowseCommand extends AbstractGitCommand @Override @SuppressWarnings("unchecked") public BrowserResult getBrowserResult(BrowseCommandRequest request) - throws IOException, RevisionNotFoundException { + throws IOException { logger.debug("try to create browse result for {}", request); BrowserResult result; @@ -157,7 +156,7 @@ public class GitBrowseCommand extends AbstractGitCommand */ private FileObject createFileObject(org.eclipse.jgit.lib.Repository repo, BrowseCommandRequest request, ObjectId revId, TreeWalk treeWalk) - throws IOException, RevisionNotFoundException { + throws IOException { FileObject file; try @@ -267,7 +266,7 @@ public class GitBrowseCommand extends AbstractGitCommand private BrowserResult getResult(org.eclipse.jgit.lib.Repository repo, BrowseCommandRequest request, ObjectId revId) - throws IOException, RevisionNotFoundException { + throws IOException { BrowserResult result = null; RevWalk revWalk = null; TreeWalk treeWalk = null; @@ -364,7 +363,7 @@ public class GitBrowseCommand extends AbstractGitCommand private Map getSubRepositories(org.eclipse.jgit.lib.Repository repo, ObjectId revision) - throws IOException, RevisionNotFoundException { + throws IOException { if (logger.isDebugEnabled()) { logger.debug("read submodules of {} at {}", repository.getName(), @@ -378,7 +377,7 @@ public class GitBrowseCommand extends AbstractGitCommand PATH_MODULES, baos); subRepositories = GitSubModuleParser.parse(baos.toString()); } - catch (PathNotFoundException ex) + catch (NotFoundException ex) { logger.trace("could not find .gitmodules", ex); subRepositories = Collections.EMPTY_MAP; @@ -389,7 +388,7 @@ public class GitBrowseCommand extends AbstractGitCommand private SubRepository getSubRepository(org.eclipse.jgit.lib.Repository repo, ObjectId revId, String path) - throws IOException, RevisionNotFoundException { + throws IOException { Map subRepositories = subrepositoryCache.get(revId); if (subRepositories == null) diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitCatCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitCatCommand.java index 4b05098d03..40f044ebde 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitCatCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitCatCommand.java @@ -44,9 +44,8 @@ import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.filter.PathFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.NotFoundException; import sonia.scm.repository.GitUtil; -import sonia.scm.repository.PathNotFoundException; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.util.Util; import java.io.Closeable; @@ -65,7 +64,7 @@ public class GitCatCommand extends AbstractGitCommand implements CatCommand { } @Override - public void getCatResult(CatCommandRequest request, OutputStream output) throws IOException, PathNotFoundException, RevisionNotFoundException { + public void getCatResult(CatCommandRequest request, OutputStream output) throws IOException { logger.debug("try to read content for {}", request); try (ClosableObjectLoaderContainer closableObjectLoaderContainer = getLoader(request)) { closableObjectLoaderContainer.objectLoader.copyTo(output); @@ -73,24 +72,24 @@ public class GitCatCommand extends AbstractGitCommand implements CatCommand { } @Override - public InputStream getCatResultStream(CatCommandRequest request) throws IOException, PathNotFoundException, RevisionNotFoundException { + public InputStream getCatResultStream(CatCommandRequest request) throws IOException { logger.debug("try to read content for {}", request); return new InputStreamWrapper(getLoader(request)); } - void getContent(org.eclipse.jgit.lib.Repository repo, ObjectId revId, String path, OutputStream output) throws IOException, PathNotFoundException, RevisionNotFoundException { + void getContent(org.eclipse.jgit.lib.Repository repo, ObjectId revId, String path, OutputStream output) throws IOException { try (ClosableObjectLoaderContainer closableObjectLoaderContainer = getLoader(repo, revId, path)) { closableObjectLoaderContainer.objectLoader.copyTo(output); } } - private ClosableObjectLoaderContainer getLoader(CatCommandRequest request) throws IOException, PathNotFoundException, RevisionNotFoundException { + private ClosableObjectLoaderContainer getLoader(CatCommandRequest request) throws IOException { org.eclipse.jgit.lib.Repository repo = open(); ObjectId revId = getCommitOrDefault(repo, request.getRevision()); return getLoader(repo, revId, request.getPath()); } - private ClosableObjectLoaderContainer getLoader(Repository repo, ObjectId revId, String path) throws IOException, PathNotFoundException, RevisionNotFoundException { + private ClosableObjectLoaderContainer getLoader(Repository repo, ObjectId revId, String path) throws IOException { TreeWalk treeWalk = new TreeWalk(repo); treeWalk.setRecursive(Util.nonNull(path).contains("/")); @@ -102,7 +101,7 @@ public class GitCatCommand extends AbstractGitCommand implements CatCommand { try { entry = revWalk.parseCommit(revId); } catch (MissingObjectException e) { - throw new RevisionNotFoundException(revId.getName()); + throw NotFoundException.notFound("Revision", revId.getName()).in(repository).build(); } RevTree revTree = entry.getTree(); @@ -120,7 +119,7 @@ public class GitCatCommand extends AbstractGitCommand implements CatCommand { return new ClosableObjectLoaderContainer(loader, treeWalk, revWalk); } else { - throw new PathNotFoundException(path); + throw NotFoundException.notFound("Path", path).in("Revision", revId.getName()).in(repository).build(); } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java index 4e9261f517..40b25ef75a 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java @@ -48,12 +48,12 @@ import org.eclipse.jgit.treewalk.filter.PathFilter; import org.eclipse.jgit.treewalk.filter.TreeFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.NotFoundException; import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; import sonia.scm.repository.GitChangesetConverter; import sonia.scm.repository.GitUtil; import sonia.scm.repository.InternalRepositoryException; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.util.IOUtil; import java.io.IOException; @@ -85,7 +85,6 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand * * @param context * @param repository - * @param repositoryDirectory */ GitLogCommand(GitContext context, sonia.scm.repository.Repository repository) { @@ -162,7 +161,7 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand */ @Override @SuppressWarnings("unchecked") - public ChangesetPagingResult getChangesets(LogCommandRequest request) throws RevisionNotFoundException { + public ChangesetPagingResult getChangesets(LogCommandRequest request) { if (logger.isDebugEnabled()) { logger.debug("fetch changesets for request: {}", request); } @@ -249,7 +248,7 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand } catch (MissingObjectException e) { - throw new RevisionNotFoundException(e.getObjectId().name()); + throw NotFoundException.notFound("Revision", e.getObjectId().getName()).in(repository).build(); } catch (Exception ex) { diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java index d71c85a152..a6e704501e 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java @@ -39,7 +39,6 @@ import org.junit.Test; import sonia.scm.repository.BrowserResult; import sonia.scm.repository.FileObject; import sonia.scm.repository.GitConstants; -import sonia.scm.repository.RevisionNotFoundException; import java.io.IOException; import java.util.List; @@ -63,7 +62,7 @@ public class GitBrowseCommandTest extends AbstractGitCommandTestBase * Test browse command with default branch. */ @Test - public void testDefaultBranch() throws IOException, RevisionNotFoundException { + public void testDefaultBranch() throws IOException { // without default branch, the repository head should be used BrowserResult result = createCommand().getBrowserResult(new BrowseCommandRequest()); assertNotNull(result); @@ -93,7 +92,7 @@ public class GitBrowseCommandTest extends AbstractGitCommandTestBase } @Test - public void testBrowse() throws IOException, RevisionNotFoundException { + public void testBrowse() throws IOException { BrowserResult result = createCommand().getBrowserResult(new BrowseCommandRequest()); @@ -134,7 +133,7 @@ public class GitBrowseCommandTest extends AbstractGitCommandTestBase } @Test - public void testBrowseSubDirectory() throws IOException, RevisionNotFoundException { + public void testBrowseSubDirectory() throws IOException { BrowseCommandRequest request = new BrowseCommandRequest(); request.setPath("c"); @@ -181,7 +180,7 @@ public class GitBrowseCommandTest extends AbstractGitCommandTestBase } @Test - public void testRecusive() throws IOException, RevisionNotFoundException { + public void testRecusive() throws IOException { BrowseCommandRequest request = new BrowseCommandRequest(); request.setRecursive(true); diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java index 3611c9c636..079fcac1da 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java @@ -32,10 +32,13 @@ package sonia.scm.repository.spi; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; +import sonia.scm.NotFoundException; import sonia.scm.repository.GitConstants; -import sonia.scm.repository.PathNotFoundException; -import sonia.scm.repository.RevisionNotFoundException; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -51,9 +54,12 @@ import static org.junit.Assert.assertEquals; * @author Sebastian Sdorra */ public class GitCatCommandTest extends AbstractGitCommandTestBase { - + + @Rule + public final ExpectedException expectedException = ExpectedException.none(); + @Test - public void testDefaultBranch() throws IOException, PathNotFoundException, RevisionNotFoundException { + public void testDefaultBranch() throws IOException { // without default branch, the repository head should be used CatCommandRequest request = new CatCommandRequest(); request.setPath("a.txt"); @@ -66,7 +72,7 @@ public class GitCatCommandTest extends AbstractGitCommandTestBase { } @Test - public void testCat() throws IOException, PathNotFoundException, RevisionNotFoundException { + public void testCat() throws IOException { CatCommandRequest request = new CatCommandRequest(); request.setPath("a.txt"); @@ -75,32 +81,58 @@ public class GitCatCommandTest extends AbstractGitCommandTestBase { } @Test - public void testSimpleCat() throws IOException, PathNotFoundException, RevisionNotFoundException { + public void testSimpleCat() throws IOException { CatCommandRequest request = new CatCommandRequest(); request.setPath("b.txt"); assertEquals("b", execute(request)); } - @Test(expected = PathNotFoundException.class) - public void testUnknownFile() throws IOException, PathNotFoundException, RevisionNotFoundException { + @Test + public void testUnknownFile() throws IOException { CatCommandRequest request = new CatCommandRequest(); request.setPath("unknown"); - execute(request); - } - @Test(expected = RevisionNotFoundException.class) - public void testUnknownRevision() throws IOException, PathNotFoundException, RevisionNotFoundException { - CatCommandRequest request = new CatCommandRequest(); + expectedException.expect(new BaseMatcher() { + @Override + public void describeTo(Description description) { + description.appendText("expected NotFoundException for path"); + } + + @Override + public boolean matches(Object item) { + return "Path".equals(((NotFoundException)item).getContext().get(0).getType()); + } + }); - request.setRevision("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); - request.setPath("a.txt"); execute(request); } @Test - public void testSimpleStream() throws IOException, PathNotFoundException, RevisionNotFoundException { + public void testUnknownRevision() throws IOException { + CatCommandRequest request = new CatCommandRequest(); + + request.setRevision("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + request.setPath("a.txt"); + + expectedException.expect(new BaseMatcher() { + @Override + public void describeTo(Description description) { + description.appendText("expected NotFoundException for revision"); + } + + @Override + public boolean matches(Object item) { + return "Revision".equals(((NotFoundException)item).getContext().get(0).getType()); + } + }); + + execute(request); + } + + @Test + public void testSimpleStream() throws IOException { CatCommandRequest request = new CatCommandRequest(); request.setPath("b.txt"); @@ -113,7 +145,7 @@ public class GitCatCommandTest extends AbstractGitCommandTestBase { catResultStream.close(); } - private String execute(CatCommandRequest request) throws IOException, PathNotFoundException, RevisionNotFoundException { + private String execute(CatCommandRequest request) throws IOException { String content = null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgLogCommandTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgLogCommandTest.java index 29fc46ed57..1daa395e07 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgLogCommandTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgLogCommandTest.java @@ -39,7 +39,6 @@ import org.junit.Test; import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; import sonia.scm.repository.Modifications; -import sonia.scm.repository.RevisionNotFoundException; import java.io.IOException; @@ -151,7 +150,7 @@ public class HgLogCommandTest extends AbstractHgCommandTestBase } @Test - public void testGetCommit() throws IOException, RevisionNotFoundException { + public void testGetCommit() throws IOException { HgLogCommand command = createComamnd(); String revision = "a9bacaf1b7fa0cebfca71fed4e59ed69a6319427"; Changeset c = diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnUtil.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnUtil.java index 480026c27a..deff53eeac 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnUtil.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnUtil.java @@ -49,6 +49,7 @@ import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil; import org.tmatesoft.svn.core.internal.util.SVNXMLUtil; import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.wc.SVNClientManager; +import sonia.scm.NotFoundException; import sonia.scm.util.HttpUtil; import sonia.scm.util.Util; @@ -102,7 +103,7 @@ public final class SvnUtil //~--- methods -------------------------------------------------------------- - public static long parseRevision(String v) throws RevisionNotFoundException { + public static long parseRevision(String v, Repository repository) { long result = -1l; if (!Strings.isNullOrEmpty(v)) @@ -113,7 +114,7 @@ public final class SvnUtil } catch (NumberFormatException ex) { - throw new RevisionNotFoundException(v); + throw NotFoundException.notFound("Revision", v).in(repository).build(); } } @@ -339,7 +340,7 @@ public final class SvnUtil } } - public static long getRevisionNumber(String revision) throws RevisionNotFoundException { + public static long getRevisionNumber(String revision, Repository repository) { // REVIEW Bei SVN wird ohne Revision die -1 genommen, was zu einem Fehler führt long revisionNumber = -1; @@ -351,7 +352,7 @@ public final class SvnUtil } catch (NumberFormatException ex) { - throw new RevisionNotFoundException(revision); + throw NotFoundException.notFound("Revision", revision).in(repository).build(); } } diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBrowseCommand.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBrowseCommand.java index a75adf6b78..d5627e4d8b 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBrowseCommand.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBrowseCommand.java @@ -47,7 +47,6 @@ import org.tmatesoft.svn.core.io.SVNRepository; import sonia.scm.repository.BrowserResult; import sonia.scm.repository.FileObject; import sonia.scm.repository.Repository; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.SubRepository; import sonia.scm.repository.SvnUtil; import sonia.scm.util.Util; @@ -78,9 +77,9 @@ public class SvnBrowseCommand extends AbstractSvnCommand @Override @SuppressWarnings("unchecked") - public BrowserResult getBrowserResult(BrowseCommandRequest request) throws RevisionNotFoundException { + public BrowserResult getBrowserResult(BrowseCommandRequest request) { String path = request.getPath(); - long revisionNumber = SvnUtil.getRevisionNumber(request.getRevision()); + long revisionNumber = SvnUtil.getRevisionNumber(request.getRevision(), repository); if (logger.isDebugEnabled()) { logger.debug("browser repository {} in path {} at revision {}", repository.getName(), path, revisionNumber); diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnCatCommand.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnCatCommand.java index 4936a16a8c..93d52ea907 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnCatCommand.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnCatCommand.java @@ -43,10 +43,9 @@ import org.tmatesoft.svn.core.SVNProperties; import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.wc.SVNClientManager; import org.tmatesoft.svn.core.wc.admin.SVNLookClient; +import sonia.scm.NotFoundException; import sonia.scm.repository.InternalRepositoryException; -import sonia.scm.repository.PathNotFoundException; import sonia.scm.repository.Repository; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.SvnUtil; import java.io.ByteArrayInputStream; @@ -79,7 +78,7 @@ public class SvnCatCommand extends AbstractSvnCommand implements CatCommand //~--- get methods ---------------------------------------------------------- @Override - public void getCatResult(CatCommandRequest request, OutputStream output) throws RevisionNotFoundException, PathNotFoundException { + public void getCatResult(CatCommandRequest request, OutputStream output) { if (logger.isDebugEnabled()) { logger.debug("try to get content for {}", request); @@ -96,14 +95,14 @@ public class SvnCatCommand extends AbstractSvnCommand implements CatCommand else { - long revisionNumber = SvnUtil.getRevisionNumber(revision); + long revisionNumber = SvnUtil.getRevisionNumber(revision, repository); getCatFromRevision(request, output, revisionNumber); } } @Override - public InputStream getCatResultStream(CatCommandRequest request) throws RevisionNotFoundException, PathNotFoundException { + public InputStream getCatResultStream(CatCommandRequest request) { // There seems to be no method creating an input stream as a result, so // we have no other possibility then to copy the content into a buffer and // stream it from there. @@ -112,7 +111,7 @@ public class SvnCatCommand extends AbstractSvnCommand implements CatCommand return new ByteArrayInputStream(output.toByteArray()); } - private void getCatFromRevision(CatCommandRequest request, OutputStream output, long revision) throws PathNotFoundException, RevisionNotFoundException { + private void getCatFromRevision(CatCommandRequest request, OutputStream output, long revision) { logger.debug("try to read content from revision {} and path {}", revision, request.getPath()); @@ -129,12 +128,12 @@ public class SvnCatCommand extends AbstractSvnCommand implements CatCommand } } - private void handleSvnException(CatCommandRequest request, SVNException ex) throws PathNotFoundException, RevisionNotFoundException { + private void handleSvnException(CatCommandRequest request, SVNException ex) { int svnErrorCode = ex.getErrorMessage().getErrorCode().getCode(); if (SVNErrorCode.FS_NOT_FOUND.getCode() == svnErrorCode) { - throw new PathNotFoundException(request.getPath()); + throw NotFoundException.notFound("Path", request.getPath()).in("Revision", request.getRevision()).in(repository).build(); } else if (SVNErrorCode.FS_NO_SUCH_REVISION.getCode() == svnErrorCode) { - throw new RevisionNotFoundException(request.getRevision()); + throw NotFoundException.notFound("Revision", request.getRevision()).in(repository).build(); } else { throw new InternalRepositoryException("could not get content from revision", ex); } diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnDiffCommand.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnDiffCommand.java index 0b65466a42..9e81f1a7a9 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnDiffCommand.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnDiffCommand.java @@ -48,7 +48,6 @@ import org.tmatesoft.svn.core.wc.SVNDiffClient; import org.tmatesoft.svn.core.wc.SVNRevision; import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.Repository; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.SvnUtil; import sonia.scm.repository.api.DiffFormat; import sonia.scm.util.Util; @@ -76,7 +75,7 @@ public class SvnDiffCommand extends AbstractSvnCommand implements DiffCommand } @Override - public void getDiffResult(DiffCommandRequest request, OutputStream output) throws RevisionNotFoundException { + public void getDiffResult(DiffCommandRequest request, OutputStream output) { if (logger.isDebugEnabled()) { logger.debug("create diff for {}", request); @@ -111,7 +110,7 @@ public class SvnDiffCommand extends AbstractSvnCommand implements DiffCommand diffGenerator.setDiffDeleted(true); diffClient.setDiffGenerator(diffGenerator); - long currentRev = SvnUtil.getRevisionNumber(request.getRevision()); + long currentRev = SvnUtil.getRevisionNumber(request.getRevision(), repository); diffClient.setGitDiffFormat(request.getFormat() == DiffFormat.GIT); diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnLogCommand.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnLogCommand.java index 332dcb55a6..a29b8f4a0c 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnLogCommand.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnLogCommand.java @@ -47,7 +47,6 @@ import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.Repository; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.SvnUtil; import sonia.scm.util.Util; @@ -76,7 +75,7 @@ public class SvnLogCommand extends AbstractSvnCommand implements LogCommand @Override @SuppressWarnings("unchecked") - public Changeset getChangeset(String revision) throws RevisionNotFoundException { + public Changeset getChangeset(String revision) { Changeset changeset = null; if (logger.isDebugEnabled()) @@ -86,7 +85,7 @@ public class SvnLogCommand extends AbstractSvnCommand implements LogCommand try { - long revisioNumber = parseRevision(revision); + long revisioNumber = parseRevision(revision, repository); SVNRepository repo = open(); Collection entries = repo.log(null, null, revisioNumber, revisioNumber, true, true); @@ -106,7 +105,7 @@ public class SvnLogCommand extends AbstractSvnCommand implements LogCommand @Override @SuppressWarnings("unchecked") - public ChangesetPagingResult getChangesets(LogCommandRequest request) throws RevisionNotFoundException { + public ChangesetPagingResult getChangesets(LogCommandRequest request) { if (logger.isDebugEnabled()) { logger.debug("fetch changesets for {}", request); @@ -115,8 +114,8 @@ public class SvnLogCommand extends AbstractSvnCommand implements LogCommand ChangesetPagingResult changesets = null; int start = request.getPagingStart(); int limit = request.getPagingLimit(); - long startRevision = parseRevision(request.getStartChangeset()); - long endRevision = parseRevision(request.getEndChangeset()); + long startRevision = parseRevision(request.getStartChangeset(), repository); + long endRevision = parseRevision(request.getEndChangeset(), repository); String[] pathArray = null; if (!Strings.isNullOrEmpty(request.getPath())) diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnModificationsCommand.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnModificationsCommand.java index e6cedc8ebf..00fb1dc5a0 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnModificationsCommand.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnModificationsCommand.java @@ -7,11 +7,9 @@ import org.tmatesoft.svn.core.io.SVNRepository; import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.Modifications; import sonia.scm.repository.Repository; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.SvnUtil; import sonia.scm.util.Util; -import java.io.IOException; import java.util.Collection; @Slf4j @@ -24,11 +22,11 @@ public class SvnModificationsCommand extends AbstractSvnCommand implements Modif @Override @SuppressWarnings("unchecked") - public Modifications getModifications(String revision) throws IOException, RevisionNotFoundException { + public Modifications getModifications(String revision) { Modifications modifications = null; log.debug("get modifications {}", revision); try { - long revisionNumber = SvnUtil.parseRevision(revision); + long revisionNumber = SvnUtil.parseRevision(revision, repository); SVNRepository repo = open(); Collection entries = repo.log(null, null, revisionNumber, revisionNumber, true, true); @@ -42,7 +40,7 @@ public class SvnModificationsCommand extends AbstractSvnCommand implements Modif } @Override - public Modifications getModifications(ModificationsCommandRequest request) throws IOException, RevisionNotFoundException { + public Modifications getModifications(ModificationsCommandRequest request) { return getModifications(request.getRevision()); } diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnBrowseCommandTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnBrowseCommandTest.java index c4c658ea7a..d76e4f3e12 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnBrowseCommandTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnBrowseCommandTest.java @@ -38,7 +38,6 @@ package sonia.scm.repository.spi; import org.junit.Test; import sonia.scm.repository.BrowserResult; import sonia.scm.repository.FileObject; -import sonia.scm.repository.RevisionNotFoundException; import java.io.IOException; import java.util.List; @@ -59,7 +58,7 @@ public class SvnBrowseCommandTest extends AbstractSvnCommandTestBase { @Test - public void testBrowse() throws RevisionNotFoundException { + public void testBrowse() { List foList = getRootFromTip(new BrowseCommandRequest()); FileObject a = getFileObject(foList, "a.txt"); @@ -83,7 +82,7 @@ public class SvnBrowseCommandTest extends AbstractSvnCommandTestBase * @throws IOException */ @Test - public void testBrowseSubDirectory() throws RevisionNotFoundException { + public void testBrowseSubDirectory() { BrowseCommandRequest request = new BrowseCommandRequest(); request.setPath("c"); @@ -130,7 +129,7 @@ public class SvnBrowseCommandTest extends AbstractSvnCommandTestBase } @Test - public void testDisableLastCommit() throws RevisionNotFoundException { + public void testDisableLastCommit() { BrowseCommandRequest request = new BrowseCommandRequest(); request.setDisableLastCommit(true); @@ -144,7 +143,7 @@ public class SvnBrowseCommandTest extends AbstractSvnCommandTestBase } @Test - public void testRecursive() throws RevisionNotFoundException { + public void testRecursive() { BrowseCommandRequest request = new BrowseCommandRequest(); request.setRecursive(true); BrowserResult result = createCommand().getBrowserResult(request); @@ -203,7 +202,7 @@ public class SvnBrowseCommandTest extends AbstractSvnCommandTestBase return a; } - private List getRootFromTip(BrowseCommandRequest request) throws RevisionNotFoundException { + private List getRootFromTip(BrowseCommandRequest request) { BrowserResult result = createCommand().getBrowserResult(request); assertNotNull(result); diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnCatCommandTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnCatCommandTest.java index 3980b3e558..e899c63b19 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnCatCommandTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnCatCommandTest.java @@ -32,9 +32,12 @@ package sonia.scm.repository.spi; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.junit.Rule; import org.junit.Test; -import sonia.scm.repository.PathNotFoundException; -import sonia.scm.repository.RevisionNotFoundException; +import org.junit.rules.ExpectedException; +import sonia.scm.NotFoundException; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -46,8 +49,11 @@ import static org.junit.Assert.assertEquals; public class SvnCatCommandTest extends AbstractSvnCommandTestBase { + @Rule + public final ExpectedException expectedException = ExpectedException.none(); + @Test - public void testCat() throws PathNotFoundException, RevisionNotFoundException { + public void testCat() { CatCommandRequest request = new CatCommandRequest(); request.setPath("a.txt"); @@ -56,35 +62,59 @@ public class SvnCatCommandTest extends AbstractSvnCommandTestBase { } @Test - public void testSimpleCat() throws PathNotFoundException, RevisionNotFoundException { + public void testSimpleCat() { CatCommandRequest request = new CatCommandRequest(); request.setPath("c/d.txt"); assertEquals("d", execute(request)); } - @Test(expected = PathNotFoundException.class) - public void testUnknownFile() throws PathNotFoundException, RevisionNotFoundException { + @Test + public void testUnknownFile() { CatCommandRequest request = new CatCommandRequest(); request.setPath("unknown"); request.setRevision("1"); - execute(request); - } + expectedException.expect(new BaseMatcher() { + @Override + public void describeTo(Description description) { + description.appendText("expected NotFoundException for path"); + } - @Test(expected = RevisionNotFoundException.class) - public void testUnknownRevision() throws PathNotFoundException, RevisionNotFoundException { - CatCommandRequest request = new CatCommandRequest(); - - request.setPath("a.txt"); - request.setRevision("42"); + @Override + public boolean matches(Object item) { + return "Path".equals(((NotFoundException)item).getContext().get(0).getType()); + } + }); execute(request); } @Test - public void testSimpleStream() throws IOException, PathNotFoundException, RevisionNotFoundException { + public void testUnknownRevision() { + CatCommandRequest request = new CatCommandRequest(); + + request.setPath("a.txt"); + request.setRevision("42"); + + expectedException.expect(new BaseMatcher() { + @Override + public void describeTo(Description description) { + description.appendText("expected NotFoundException for revision"); + } + + @Override + public boolean matches(Object item) { + return "Revision".equals(((NotFoundException)item).getContext().get(0).getType()); + } + }); + + execute(request); + } + + @Test + public void testSimpleStream() throws IOException { CatCommandRequest request = new CatCommandRequest(); request.setPath("a.txt"); request.setRevision("1"); @@ -98,7 +128,7 @@ public class SvnCatCommandTest extends AbstractSvnCommandTestBase { catResultStream.close(); } - private String execute(CatCommandRequest request) throws PathNotFoundException, RevisionNotFoundException { + private String execute(CatCommandRequest request) { String content = null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnLogCommandTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnLogCommandTest.java index a55138f151..f2511a9ad9 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnLogCommandTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnLogCommandTest.java @@ -38,7 +38,6 @@ import org.junit.Test; import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; import sonia.scm.repository.Modifications; -import sonia.scm.repository.RevisionNotFoundException; import java.io.IOException; @@ -57,7 +56,7 @@ public class SvnLogCommandTest extends AbstractSvnCommandTestBase { @Test - public void testGetAll() throws RevisionNotFoundException { + public void testGetAll() { ChangesetPagingResult result = createCommand().getChangesets(new LogCommandRequest()); @@ -67,7 +66,7 @@ public class SvnLogCommandTest extends AbstractSvnCommandTestBase } @Test - public void testGetAllByPath() throws RevisionNotFoundException { + public void testGetAllByPath() { LogCommandRequest request = new LogCommandRequest(); request.setPath("a.txt"); @@ -83,7 +82,7 @@ public class SvnLogCommandTest extends AbstractSvnCommandTestBase } @Test - public void testGetAllWithLimit() throws RevisionNotFoundException { + public void testGetAllWithLimit() { LogCommandRequest request = new LogCommandRequest(); request.setPagingLimit(2); @@ -106,7 +105,7 @@ public class SvnLogCommandTest extends AbstractSvnCommandTestBase } @Test - public void testGetAllWithPaging() throws RevisionNotFoundException { + public void testGetAllWithPaging() { LogCommandRequest request = new LogCommandRequest(); request.setPagingStart(1); @@ -130,7 +129,7 @@ public class SvnLogCommandTest extends AbstractSvnCommandTestBase } @Test - public void testGetCommit() throws RevisionNotFoundException, IOException { + public void testGetCommit() { Changeset c = createCommand().getChangeset("3"); assertNotNull(c); @@ -151,7 +150,7 @@ public class SvnLogCommandTest extends AbstractSvnCommandTestBase } @Test - public void testGetRange() throws RevisionNotFoundException { + public void testGetRange() { LogCommandRequest request = new LogCommandRequest(); request.setStartChangeset("2"); diff --git a/scm-webapp/src/main/java/sonia/scm/ManagerDaoAdapter.java b/scm-webapp/src/main/java/sonia/scm/ManagerDaoAdapter.java index 7979eca0e7..4fdd74063e 100644 --- a/scm-webapp/src/main/java/sonia/scm/ManagerDaoAdapter.java +++ b/scm-webapp/src/main/java/sonia/scm/ManagerDaoAdapter.java @@ -30,7 +30,7 @@ public class ManagerDaoAdapter { afterUpdate.handle(notModified); } else { - throw new NotFoundException(); + throw new NotFoundException(object.getClass(), object.getId()); } } @@ -58,7 +58,7 @@ public class ManagerDaoAdapter { dao.delete(toDelete); afterDelete.handle(toDelete); } else { - throw new NotFoundException(); + throw new NotFoundException(toDelete.getClass(), toDelete.getId()); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/BrowserStreamingOutput.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/BrowserStreamingOutput.java index 2c78df627b..d2ce744c19 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/BrowserStreamingOutput.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/BrowserStreamingOutput.java @@ -2,8 +2,6 @@ package sonia.scm.api.rest.resources; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.repository.PathNotFoundException; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.api.CatCommandBuilder; import sonia.scm.repository.api.RepositoryService; import sonia.scm.util.IOUtil; @@ -34,18 +32,6 @@ public class BrowserStreamingOutput implements StreamingOutput { public void write(OutputStream output) throws IOException { try { builder.retriveContent(output, path); - } catch (PathNotFoundException ex) { - if (logger.isWarnEnabled()) { - logger.warn("could not find path {}", ex.getPath()); - } - - throw new WebApplicationException(Response.Status.NOT_FOUND); - } catch (RevisionNotFoundException ex) { - if (logger.isWarnEnabled()) { - logger.warn("could not find revision {}", ex.getRevision()); - } - - throw new WebApplicationException(Response.Status.NOT_FOUND); } finally { IOUtil.close(repositoryService); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/DiffStreamingOutput.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/DiffStreamingOutput.java index db29725917..c5ce925797 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/DiffStreamingOutput.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/DiffStreamingOutput.java @@ -37,7 +37,6 @@ package sonia.scm.api.rest.resources; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.api.DiffCommandBuilder; import sonia.scm.repository.api.RepositoryService; import sonia.scm.util.IOUtil; @@ -95,22 +94,6 @@ public class DiffStreamingOutput implements StreamingOutput { builder.retriveContent(output); } - catch (RevisionNotFoundException ex) - { - if (logger.isWarnEnabled()) - { - logger.warn("could not find revision {}", ex.getRevision()); - } - - throw new WebApplicationException(Response.Status.NOT_FOUND); - } -// catch (RepositoryException ex) -// { -// logger.error("could not write content to page", ex); -// -// throw new WebApplicationException(ex, -// Response.Status.INTERNAL_SERVER_ERROR); -// } finally { IOUtil.close(repositoryService); diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java index 8a5ba8b1e3..6bd20688d6 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java @@ -50,6 +50,7 @@ import sonia.scm.NotFoundException; import sonia.scm.NotSupportedFeatuerException; import sonia.scm.Type; import sonia.scm.api.rest.RestActionUploadResult; +import sonia.scm.api.v2.resources.RepositoryResource; import sonia.scm.repository.*; import sonia.scm.repository.api.Command; import sonia.scm.repository.api.RepositoryService; diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java deleted file mode 100644 index 4a9fd9e38f..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java +++ /dev/null @@ -1,1067 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of SCM-Manager; 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 REGENTS 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. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.api.rest.resources; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.common.base.Strings; -import com.google.inject.Inject; -import com.google.inject.Singleton; -import com.webcohesion.enunciate.metadata.rs.ResponseCode; -import com.webcohesion.enunciate.metadata.rs.ResponseHeader; -import com.webcohesion.enunciate.metadata.rs.StatusCodes; -import com.webcohesion.enunciate.metadata.rs.TypeHint; -import org.apache.shiro.SecurityUtils; -import org.apache.shiro.authz.AuthorizationException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sonia.scm.NotFoundException; -import sonia.scm.repository.BlameResult; -import sonia.scm.repository.Branches; -import sonia.scm.repository.BrowserResult; -import sonia.scm.repository.Changeset; -import sonia.scm.repository.ChangesetPagingResult; -import sonia.scm.repository.HealthChecker; -import sonia.scm.repository.Permission; -import sonia.scm.repository.Repository; -import sonia.scm.repository.RepositoryIsNotArchivedException; -import sonia.scm.repository.RepositoryManager; -import sonia.scm.repository.RepositoryNotFoundException; -import sonia.scm.repository.Tags; -import sonia.scm.repository.api.BlameCommandBuilder; -import sonia.scm.repository.api.BrowseCommandBuilder; -import sonia.scm.repository.api.CatCommandBuilder; -import sonia.scm.repository.api.CommandNotSupportedException; -import sonia.scm.repository.api.DiffCommandBuilder; -import sonia.scm.repository.api.DiffFormat; -import sonia.scm.repository.api.LogCommandBuilder; -import sonia.scm.repository.api.RepositoryService; -import sonia.scm.repository.api.RepositoryServiceFactory; -import sonia.scm.util.AssertUtil; -import sonia.scm.util.HttpUtil; -import sonia.scm.util.IOUtil; -import sonia.scm.util.Util; - -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.GenericEntity; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Request; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.core.StreamingOutput; -import javax.ws.rs.core.UriInfo; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; - -//~--- JDK imports ------------------------------------------------------------ - -/** - * Repository related RESTful Web Service Endpoint. - * - * @author Sebastian Sdorra - */ -@Singleton -@Path("repositories") -public class RepositoryResource extends AbstractManagerResource -{ - - /** Field description */ - public static final String PATH_PART = "repositories"; - - /** the logger for RepositoryResource */ - private static final Logger logger = - LoggerFactory.getLogger(RepositoryResource.class); - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs ... - * - * @param repositoryManager - * @param servicefactory - * @param healthChecker - */ - @Inject - public RepositoryResource(RepositoryManager repositoryManager, - RepositoryServiceFactory servicefactory, HealthChecker healthChecker) - { - super(repositoryManager, Repository.class); - this.repositoryManager = repositoryManager; - this.servicefactory = servicefactory; - this.healthChecker = healthChecker; - setDisableCache(false); - } - - //~--- methods -------------------------------------------------------------- - - /** - * Creates a new repository.Note: This method requires admin privileges. - * - * @param uriInfo current uri informations - * @param repository the repository to be created - * - * @return empty response with location header to the new repository - */ - @POST - @StatusCodes({ - @ResponseCode(code = 201, condition = "success", additionalHeaders = { - @ResponseHeader(name = "Location", description = "uri to the new created repository") - }), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Override - public Response create(@Context UriInfo uriInfo, Repository repository) - { - return super.create(uriInfo, repository); - } - - /** - * Deletes a repository. Note: This method requires owner privileges. - * - * @param id the id of the repository to delete. - * - * @return - */ - @DELETE - @Path("{id}") - @StatusCodes({ - @ResponseCode(code = 204, condition = "delete success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no owner privileges"), - @ResponseCode(code = 404, condition = "could not find repository"), - @ResponseCode( - code = 412, - condition = "precondition failed, the repository is not archived, this error occurs only with enabled repository archive" - ), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Override - public Response delete(@PathParam("id") String id) - { - Response response; - Repository repository = manager.get(id); - - if (repository != null) - { - preDelete(repository); - - try - { - manager.delete(repository); - response = Response.noContent().build(); - } - catch (RepositoryIsNotArchivedException ex) - { - logger.warn("non archived repository could not be deleted", ex); - response = Response.status(Response.Status.PRECONDITION_FAILED).build(); - } - catch (AuthorizationException ex) - { - logger.warn("delete not allowed", ex); - response = Response.status(Response.Status.FORBIDDEN).build(); - } catch (NotFoundException e) { - // there is nothing to do because delete should be idempotent - response = Response.ok().build(); - } -// catch (IOException ex) -// { -// logger.error("error during delete", ex); -// response = Response.serverError().build(); -// } - } - else - { - logger.warn("could not find repository {}", id); - response = Response.status(Status.NOT_FOUND).build(); - } - - return response; - } - - /** - * Re run repository health checks. - * - * @param id id of the repository - * - * @return - */ - @POST - @StatusCodes({ - @ResponseCode(code = 200, condition = "re run success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no owner privileges"), - @ResponseCode(code = 404, condition = "could not find repository"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Path("{id}/healthcheck") - public Response runHealthChecks(@PathParam("id") String id) - { - Response response; - - try - { - healthChecker.check(id); - // TODO should return 204 instead of 200 - response = Response.ok().build(); - } - catch (RepositoryNotFoundException ex) - { - logger.warn("could not find repository ".concat(id), ex); - response = Response.status(Status.NOT_FOUND).build(); - } catch (NotFoundException e) { - logger.error("error occured during health check", e); - response = Response.serverError().build(); - } - - return response; - } - - /** - * Modifies the given repository. Note: This method requires owner privileges. - * - * @param id id of the repository to be modified - * @param repository repository object to modify - * - * @return - */ - @PUT - @Path("{id}") - @StatusCodes({ - @ResponseCode(code = 204, condition = "update successful"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no owner privileges"), - @ResponseCode(code = 404, condition = "could not find repository"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Override - public Response update(@PathParam("id") String id, Repository repository) - { - return super.update(id, repository); - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Returns the {@link Repository} with the specified id. - * - * @param request the current request - * @param id the id/name of the user - * - * @return the {@link Repository} with the specified id - */ - @GET - @Path("{id}") - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 404, condition = "not found, no repository with the specified id available"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(Repository.class) - @Override - public Response get(@Context Request request, @PathParam("id") String id) - { - return super.get(request, id); - } - - /** - * Returns all repositories. - * - * @param request the current request - * @param start the start value for paging - * @param limit the limit value for paging - * @param sortby sort parameter - * @param desc sort direction desc or aesc - * - * @return all repositories - */ - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(Repository[].class) - @Override - public Response getAll(@Context Request request, @DefaultValue("0") - @QueryParam("start") int start, @DefaultValue("-1") - @QueryParam("limit") int limit, @QueryParam("sortby") String sortby, - @DefaultValue("false") - @QueryParam("desc") boolean desc) - { - return super.getAll(request, start, limit, sortby, desc); - } - - /** - * Returns a annotate/blame view for the given path. - * - * @param id the id of the repository - * @param revision the revision of the file - * @param path the path of the file - * - * @return a annotate/blame view for the given path - * - * @throws IOException - */ - @GET - @Path("{id}/blame") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 400, condition = "bad request, the blame feature is not supported by this type of repositories."), - @ResponseCode(code = 404, condition = "not found, the repository or the path could not be found"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(BlameResult.class) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getBlame(@PathParam("id") String id, - @QueryParam("revision") String revision, @QueryParam("path") String path) - throws IOException - { - Response response = null; - RepositoryService service = null; - - try - { - AssertUtil.assertIsNotNull(path); - service = servicefactory.create(id); - - BlameCommandBuilder builder = service.getBlameCommand(); - - if (!Strings.isNullOrEmpty(revision)) - { - builder.setRevision(revision); - } - - BlameResult blamePagingResult = builder.getBlameResult(path); - - if (blamePagingResult != null) - { - response = Response.ok(blamePagingResult).build(); - } - else - { - response = Response.ok().build(); - } - } - catch (IllegalStateException ex) - { - response = Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); - } - catch (RepositoryNotFoundException ex) - { - response = Response.status(Response.Status.NOT_FOUND).build(); - } - catch (CommandNotSupportedException ex) - { - response = Response.status(Response.Status.BAD_REQUEST).build(); - } - finally - { - IOUtil.close(service); - } - - return response; - } - - /** - * Returns all {@link Branches} of a repository. - * - * @param id the id of the repository - * - * @return all {@link Branches} of a repository - * - * @throws IOException - * - * @since 1.18 - */ - @GET - @Path("{id}/branches") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 400, condition = "bad request, the branch feature is not supported by this type of repositories."), - @ResponseCode(code = 404, condition = "not found, the repository could not be found"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(Branches.class) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getBranches(@PathParam("id") String id) - throws IOException - { - Response response = null; - RepositoryService service = null; - - try - { - service = servicefactory.create(id); - - Branches branches = service.getBranchesCommand().getBranches(); - - if (branches != null) - { - response = Response.ok(branches).build(); - } - else - { - response = Response.status(Status.NOT_FOUND).build(); - } - } - catch (RepositoryNotFoundException ex) - { - response = Response.status(Response.Status.NOT_FOUND).build(); - } - catch (CommandNotSupportedException ex) - { - response = Response.status(Response.Status.BAD_REQUEST).build(); - } - finally - { - IOUtil.close(service); - } - - return response; - } - - /** - * Returns a list of folders and files for the given folder. - * - * @param id the id of the repository - * @param revision the revision of the file - * @param path the path of the folder - * @param disableLastCommit true disables fetch of last commit message - * @param disableSubRepositoryDetection true disables sub repository detection - * @param recursive true to enable recursive browsing - * - * @return a list of folders and files for the given folder - * - * @throws IOException - */ - @GET - @Path("{id}/browse") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 400, condition = "bad request, the browse feature is not supported by this type of repositories."), - @ResponseCode(code = 404, condition = "not found, the repository or the path could not be found"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(BrowserResult.class) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - //J- - public Response getBrowserResult( - @PathParam("id") String id, - @QueryParam("revision") String revision, - @QueryParam("path") String path, - @QueryParam("disableLastCommit") @DefaultValue("false") boolean disableLastCommit, - @QueryParam("disableSubRepositoryDetection") @DefaultValue("false") boolean disableSubRepositoryDetection, - @QueryParam("recursive") @DefaultValue("false") boolean recursive) - throws IOException - //J+ - { - Response response = null; - RepositoryService service = null; - - try - { - service = servicefactory.create(id); - - BrowseCommandBuilder builder = service.getBrowseCommand(); - - if (!Strings.isNullOrEmpty(revision)) - { - builder.setRevision(revision); - } - - if (!Strings.isNullOrEmpty(path)) - { - builder.setPath(path); - } - - //J- - builder.setDisableLastCommit(disableLastCommit) - .setDisableSubRepositoryDetection(disableSubRepositoryDetection) - .setRecursive(recursive); - //J+ - - BrowserResult result = builder.getBrowserResult(); - - if (result != null) - { - response = Response.ok(result).build(); - } - else - { - response = Response.status(Response.Status.NOT_FOUND).build(); - } - } - catch (NotFoundException ex) - { - response = Response.status(Response.Status.NOT_FOUND).build(); - } - catch (CommandNotSupportedException ex) - { - response = Response.status(Response.Status.BAD_REQUEST).build(); - } - finally - { - IOUtil.close(service); - } - - return response; - } - - /** - * Returns the {@link Changeset} from the given repository - * with the specified revision. - * - * @param id the id of the repository - * @param revision the revision of the changeset - * - * @return a {@link Changeset} from the given repository - * - * @throws IOException - */ - @GET - @Path("{id}/changeset/{revision}") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 400, condition = "bad request, the changeset feature is not supported by this type of repositories."), - @ResponseCode(code = 404, condition = "not found, the repository or the revision could not be found"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(Changeset.class) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getChangeset(@PathParam("id") String id, - @PathParam("revision") String revision) - throws IOException - { - Response response = null; - - if (Util.isNotEmpty(id) && Util.isNotEmpty(revision)) - { - RepositoryService service = null; - - try - { - service = servicefactory.create(id); - - Changeset changeset = service.getLogCommand().getChangeset(revision); - - if (changeset != null) - { - response = Response.ok(changeset).build(); - } - else - { - response = Response.status(Status.NOT_FOUND).build(); - } - } - catch (NotFoundException ex) - { - response = Response.status(Response.Status.NOT_FOUND).build(); - } - catch (CommandNotSupportedException ex) - { - response = Response.status(Response.Status.BAD_REQUEST).build(); - } - finally - { - IOUtil.close(service); - } - } - else - { - logger.warn("id or revision is empty"); - response = Response.status(Status.BAD_REQUEST).build(); - } - - return response; - } - - /** - * Returns a list of {@link Changeset} for the given repository. - * - * @param id the id of the repository - * @param path path of a file - * @param revision the revision of the file specified by the path parameter - * @param branch name of the branch - * @param start the start value for paging - * @param limit the limit value for paging - * - * @return a list of {@link Changeset} for the given repository - * - * @throws IOException - */ - @GET - @Path("{id}/changesets") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 400, condition = "bad request, the changeset feature is not supported by this type of repositories."), - @ResponseCode(code = 404, condition = "not found, the repository or the path could not be found"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(ChangesetPagingResult.class) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - //J- - public Response getChangesets( - @PathParam("id") String id, - @QueryParam("path") String path, - @QueryParam("revision") String revision, - @QueryParam("branch") String branch, - @DefaultValue("0") @QueryParam("start") int start, - @DefaultValue("20") @QueryParam("limit") int limit - ) throws IOException - //J+ - { - Response response = null; - RepositoryService service = null; - - try - { - ChangesetPagingResult changesets; - - service = servicefactory.create(id); - - LogCommandBuilder builder = service.getLogCommand(); - - if (!Strings.isNullOrEmpty(path)) - { - builder.setPath(path); - } - - if (!Strings.isNullOrEmpty(revision)) - { - builder.setStartChangeset(revision); - } - - if (!Strings.isNullOrEmpty(branch)) - { - builder.setBranch(branch); - } - - changesets = - builder.setPagingStart(start).setPagingLimit(limit).getChangesets(); - - if (changesets != null) - { - response = Response.ok(changesets).build(); - } - else - { - response = Response.ok().build(); - } - } - catch (NotFoundException ex) - { - response = Response.status(Response.Status.NOT_FOUND).build(); - } - catch (CommandNotSupportedException ex) - { - response = Response.status(Response.Status.BAD_REQUEST).build(); - } - finally - { - IOUtil.close(service); - } - - return response; - } - - /** - * Returns the content of a file. - * - * @param id the id of the repository - * @param revision the revision of the file - * @param path path to the file - * - * @return the content of a file - */ - @GET - @Path("{id}/content") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 400, condition = "bad request, the content feature is not supported by this type of repositories."), - @ResponseCode(code = 404, condition = "not found, the repository or the path could not be found"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(StreamingOutput.class) - @Produces({ MediaType.APPLICATION_OCTET_STREAM }) - public Response getContent(@PathParam("id") String id, - @QueryParam("revision") String revision, @QueryParam("path") String path) - { - Response response; - StreamingOutput output; - RepositoryService service; - - try - { - service = servicefactory.create(id); - - CatCommandBuilder builder = service.getCatCommand(); - - if (!Strings.isNullOrEmpty(revision)) - { - builder.setRevision(revision); - } - - output = new BrowserStreamingOutput(service, builder, path); - - /** - * protection for crlf injection - * see https://bitbucket.org/sdorra/scm-manager/issue/320/crlf-injection-vulnerability-in-diff-api - */ - path = HttpUtil.removeCRLFInjectionChars(path); - - String contentDispositionName = getContentDispositionNameFromPath(path); - - response = Response.ok(output).header("Content-Disposition", - contentDispositionName).build(); - } - catch (RepositoryNotFoundException ex) - { - logger.warn("could not find repository browser for respository {}", id); - response = Response.status(Response.Status.NOT_FOUND).build(); - } - catch (CommandNotSupportedException ex) - { - response = Response.status(Response.Status.BAD_REQUEST).build(); - } - catch (Exception ex) - { - logger.error("could not retrive content", ex); - response = createErrorResponse(ex); - } - - return response; - } - - /** - * Returns the modifications of a {@link Changeset}. - * - * @param id the id of the repository - * @param revision the revision of the file - * @param path path to the file - * @param format - * - * @return the modifications of a {@link Changeset} - * - * @throws IOException - */ - @GET - @Path("{id}/diff") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 400, condition = "bad request, the diff feature is not supported by this type of repositories."), - @ResponseCode(code = 404, condition = "not found, the repository or the path could not be found"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(DiffStreamingOutput.class) - @Produces(MediaType.APPLICATION_OCTET_STREAM) - public Response getDiff(@PathParam("id") String id, - @QueryParam("revision") String revision, @QueryParam("path") String path, - @QueryParam("format") DiffFormat format) - throws IOException - { - AssertUtil.assertIsNotEmpty(id); - AssertUtil.assertIsNotEmpty(revision); - - /** - * check for a crlf injection attack - * see https://bitbucket.org/sdorra/scm-manager/issue/320/crlf-injection-vulnerability-in-diff-api - */ - HttpUtil.checkForCRLFInjection(revision); - - RepositoryService service; - Response response; - - try - { - service = servicefactory.create(id); - - DiffCommandBuilder builder = service.getDiffCommand(); - - if (!Strings.isNullOrEmpty(revision)) - { - builder.setRevision(revision); - } - - if (!Strings.isNullOrEmpty(path)) - { - builder.setPath(path); - } - - if (format != null) - { - builder.setFormat(format); - } - - String name = service.getRepository().getName().concat("-").concat( - revision).concat(".diff"); - String contentDispositionName = getContentDispositionName(name); - - response = Response.ok(new DiffStreamingOutput(service, - builder)).header("Content-Disposition", contentDispositionName).build(); - } - catch (RepositoryNotFoundException ex) - { - response = Response.status(Response.Status.NOT_FOUND).build(); - } - catch (CommandNotSupportedException ex) - { - response = Response.status(Response.Status.BAD_REQUEST).build(); - } - catch (Exception ex) - { - logger.error("could not create diff", ex); - response = createErrorResponse(ex); - } - - return response; - } - - /** - * Returns all {@link Tags} of a repository. - * - * @param id the id of the repository - * - * @return all {@link Tags} of a repository - * - * @throws IOException - * - * @since 1.18 - */ - @GET - @Path("{id}/tags") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 400, condition = "bad request, the tag feature is not supported by this type of repositories."), - @ResponseCode(code = 404, condition = "not found, the repository could not be found"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(Tags.class) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getTags(@PathParam("id") String id) - throws IOException - { - Response response = null; - RepositoryService service = null; - - try - { - service = servicefactory.create(id); - - Tags tags = service.getTagsCommand().getTags(); - - if (tags != null) - { - response = Response.ok(tags).build(); - } - else - { - response = Response.status(Status.NOT_FOUND).build(); - } - } - catch (RepositoryNotFoundException ex) - { - response = Response.status(Response.Status.NOT_FOUND).build(); - } - catch (CommandNotSupportedException ex) - { - response = Response.status(Response.Status.BAD_REQUEST).build(); - } - finally - { - IOUtil.close(service); - } - - return response; - } - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param items - * - * @return - */ - @Override - protected GenericEntity> createGenericEntity( - Collection items) - { - return new GenericEntity>(items) {} - ; - } - - /** - * Method description - * - * - * - * @param repositories - * @return - */ - @Override - protected Collection prepareForReturn( - Collection repositories) - { - for (Repository repository : repositories) - { - prepareForReturn(repository); - } - - return repositories; - } - - /** - * Method description - * - * - * @param repository - * - * @return - */ - @Override - protected Repository prepareForReturn(Repository repository) - { - if (SecurityUtils.getSubject().isPermitted( - "repository:modify:".concat(repository.getId()))) - { - if (repository.getPermissions() == null) - { - repository.setPermissions(new ArrayList()); - } - } - else - { - logger.trace("remove properties and permissions from repository, " - + "because the user is not privileged"); - - repository.setProperties(null); - repository.setPermissions(null); - repository.setHealthCheckFailures(null); - } - - return repository; - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @param item - * - * @return - */ - @Override - protected String getId(Repository item) - { - return item.getId(); - } - - /** - * Method description - * - * - * @return - */ - @Override - protected String getPathPart() - { - return PATH_PART; - } - - /** - * Method description - * - * - * - * @param name - * - * @return - */ - private String getContentDispositionName(String name) - { - return HttpUtil.createContentDispositionAttachmentHeader(name); - } - - /** - * Method description - * - * - * @param path - * - * @return - */ - private String getContentDispositionNameFromPath(String path) - { - String name = path; - int index = path.lastIndexOf('/'); - - if (index >= 0) - { - name = path.substring(index + 1); - } - - return getContentDispositionName(name); - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private final HealthChecker healthChecker; - - /** Field description */ - private final RepositoryManager repositoryManager; - - /** Field description */ - private final RepositoryServiceFactory servicefactory; -} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java index 5f57d1bcc8..e308aeda25 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java @@ -5,6 +5,7 @@ import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; import sonia.scm.NotFoundException; import sonia.scm.PageResult; +import sonia.scm.repository.Branch; import sonia.scm.repository.Branches; import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; @@ -105,7 +106,7 @@ public class BranchRootResource { .stream() .anyMatch(branch -> branchName.equals(branch.getName())); if (!branchExists){ - throw new NotFoundException("branch", branchName); + throw NotFoundException.notFound(Branch.class, branchName).in(Repository.class, namespace + "/" + name).build(); } Repository repository = repositoryService.getRepository(); RepositoryPermissions.read(repository).check(); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetRootResource.java index 8900183f5b..bae521ae4c 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetRootResource.java @@ -11,8 +11,6 @@ import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryNotFoundException; import sonia.scm.repository.RepositoryPermissions; -import sonia.scm.repository.RevisionNotFoundException; -import sonia.scm.repository.api.LogCommandBuilder; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; import sonia.scm.web.VndMediaType; @@ -56,7 +54,7 @@ public class ChangesetRootResource { @Produces(VndMediaType.CHANGESET_COLLECTION) @TypeHint(CollectionDto.class) public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name, @DefaultValue("0") @QueryParam("page") int page, - @DefaultValue("10") @QueryParam("pageSize") int pageSize) throws IOException, RevisionNotFoundException, RepositoryNotFoundException { + @DefaultValue("10") @QueryParam("pageSize") int pageSize) throws IOException { try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { Repository repository = repositoryService.getRepository(); RepositoryPermissions.read(repository).check(); @@ -89,7 +87,7 @@ public class ChangesetRootResource { @Produces(VndMediaType.CHANGESET) @TypeHint(ChangesetDto.class) @Path("{id}") - public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("id") String id) throws IOException, RevisionNotFoundException, RepositoryNotFoundException { + public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("id") String id) throws IOException { try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { Repository repository = repositoryService.getRepository(); RepositoryPermissions.read(repository).check(); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ContentResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ContentResource.java index dc7c305823..9680a319a5 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ContentResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ContentResource.java @@ -6,10 +6,9 @@ import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.NotFoundException; import sonia.scm.repository.NamespaceAndName; -import sonia.scm.repository.PathNotFoundException; import sonia.scm.repository.RepositoryNotFoundException; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; import sonia.scm.util.IOUtil; @@ -64,8 +63,8 @@ public class ContentResource { try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { Response.ResponseBuilder responseBuilder = Response.ok(stream); return createContentHeader(namespace, name, revision, path, repositoryService, responseBuilder); - } catch (RepositoryNotFoundException e) { - LOG.debug("path '{}' not found in repository {}/{}", path, namespace, name, e); + } catch (NotFoundException e) { + LOG.debug(e.getMessage()); return Response.status(Status.NOT_FOUND).build(); } } @@ -75,14 +74,8 @@ public class ContentResource { try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { repositoryService.getCatCommand().setRevision(revision).retriveContent(os, path); os.close(); - } catch (RepositoryNotFoundException e) { - LOG.debug("repository {}/{} not found", path, namespace, name, e); - throw new WebApplicationException(Status.NOT_FOUND); - } catch (PathNotFoundException e) { - LOG.debug("path '{}' not found in repository {}/{}", path, namespace, name, e); - throw new WebApplicationException(Status.NOT_FOUND); - } catch (RevisionNotFoundException e) { - LOG.debug("revision '{}' not found in repository {}/{}", revision, namespace, name, e); + } catch (NotFoundException e) { + LOG.debug(e.getMessage()); throw new WebApplicationException(Status.NOT_FOUND); } }; @@ -111,8 +104,8 @@ public class ContentResource { try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { Response.ResponseBuilder responseBuilder = Response.ok(); return createContentHeader(namespace, name, revision, path, repositoryService, responseBuilder); - } catch (RepositoryNotFoundException e) { - LOG.debug("path '{}' not found in repository {}/{}", path, namespace, name, e); + } catch (NotFoundException e) { + LOG.debug(e.getMessage()); return Response.status(Status.NOT_FOUND).build(); } } @@ -120,12 +113,6 @@ public class ContentResource { private Response createContentHeader(String namespace, String name, String revision, String path, RepositoryService repositoryService, Response.ResponseBuilder responseBuilder) { try { appendContentHeader(path, getHead(revision, path, repositoryService), responseBuilder); - } catch (PathNotFoundException e) { - LOG.debug("path '{}' not found in repository {}/{}", path, namespace, name, e); - return Response.status(Status.NOT_FOUND).build(); - } catch (RevisionNotFoundException e) { - LOG.debug("revision '{}' not found in repository {}/{}", revision, namespace, name, e); - return Response.status(Status.NOT_FOUND).build(); } catch (IOException e) { LOG.info("error reading repository resource {} from {}/{}", path, namespace, name, e); return Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); @@ -139,7 +126,7 @@ public class ContentResource { contentType.getLanguage().ifPresent(language -> responseBuilder.header("Language", language)); } - private byte[] getHead(String revision, String path, RepositoryService repositoryService) throws IOException, PathNotFoundException, RevisionNotFoundException { + private byte[] getHead(String revision, String path, RepositoryService repositoryService) throws IOException { InputStream stream = repositoryService.getCatCommand().setRevision(revision).getStream(path); try { byte[] buffer = new byte[HEAD_BUFFER_SIZE]; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DiffRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DiffRootResource.java index cb78c961a2..212a993f28 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DiffRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DiffRootResource.java @@ -4,7 +4,6 @@ import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import sonia.scm.NotFoundException; import sonia.scm.repository.NamespaceAndName; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; import sonia.scm.util.HttpUtil; @@ -15,7 +14,6 @@ import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import javax.ws.rs.core.StreamingOutput; @@ -54,13 +52,9 @@ public class DiffRootResource { HttpUtil.checkForCRLFInjection(revision); try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { StreamingOutput responseEntry = output -> { - try { - repositoryService.getDiffCommand() - .setRevision(revision) - .retriveContent(output); - } catch (RevisionNotFoundException e) { - throw new WebApplicationException(Response.Status.NOT_FOUND); - } + repositoryService.getDiffCommand() + .setRevision(revision) + .retriveContent(output); }; return Response.ok(responseEntry) .header(HEADER_CONTENT_DISPOSITION, HttpUtil.createContentDispositionAttachmentHeader(String.format("%s-%s.diff", name, revision))) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ErrorDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ErrorDto.java new file mode 100644 index 0000000000..8e97eabf8e --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ErrorDto.java @@ -0,0 +1,34 @@ +package sonia.scm.api.v2.resources; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Getter; +import sonia.scm.ContextEntry; +import sonia.scm.NotFoundException; + +import java.util.List; + +@Getter +public class ErrorDto { + private final String transactionId; + private final String errorCode; + private final List context; + private final String message; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private final String url; + + private ErrorDto(String transactionId, String errorCode, List context, String message) { + this(transactionId, errorCode, context, message, null); + } + private ErrorDto(String transactionId, String errorCode, List context, String message, String url) { + this.transactionId = transactionId; + this.errorCode = errorCode; + this.context = context; + this.message = message; + this.url = url; + } + + static ErrorDto from(NotFoundException notFoundException) { + return new ErrorDto("todo", "todo", notFoundException.getContext(), notFoundException.getMessage()); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileHistoryRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileHistoryRootResource.java index e38a6f699a..ae4bcb1064 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileHistoryRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileHistoryRootResource.java @@ -11,7 +11,6 @@ import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryNotFoundException; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; import sonia.scm.web.VndMediaType; @@ -51,8 +50,6 @@ public class FileHistoryRootResource { * @param pageSize pagination * @return all changesets related to the given file starting with the given revision * @throws IOException on io error - * @throws RevisionNotFoundException on missing revision - * @throws RepositoryNotFoundException on missing repository */ @GET @Path("{revision}/{path: .*}") @@ -69,7 +66,7 @@ public class FileHistoryRootResource { @PathParam("revision") String revision, @PathParam("path") String path, @DefaultValue("0") @QueryParam("page") int page, - @DefaultValue("10") @QueryParam("pageSize") int pageSize) throws IOException, RevisionNotFoundException, RepositoryNotFoundException { + @DefaultValue("10") @QueryParam("pageSize") int pageSize) throws IOException { try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { log.info("Get changesets of the file {} and revision {}", path, revision); Repository repository = repositoryService.getRepository(); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IdResourceManagerAdapter.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IdResourceManagerAdapter.java index 9fe3d8284d..533bc49da0 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IdResourceManagerAdapter.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IdResourceManagerAdapter.java @@ -5,6 +5,7 @@ import sonia.scm.AlreadyExistsException; import sonia.scm.ConcurrentModificationException; import sonia.scm.Manager; import sonia.scm.ModelObject; +import sonia.scm.NotFoundException; import sonia.scm.PageResult; import javax.ws.rs.core.Response; @@ -22,6 +23,7 @@ class IdResourceManagerAdapter { private final Manager manager; + private final String type; private final SingleResourceManagerAdapter singleAdapter; private final CollectionResourceManagerAdapter collectionAdapter; @@ -30,6 +32,7 @@ class IdResourceManagerAdapter(manager, type); collectionAdapter = new CollectionResourceManagerAdapter<>(manager, type); + this.type = type.getSimpleName(); } Response get(String id, Function mapToDto) { @@ -56,8 +59,8 @@ class IdResourceManagerAdapter> loadBy(String id) { - return () -> Optional.ofNullable(manager.get(id)); + private Supplier loadBy(String id) { + return () -> Optional.ofNullable(manager.get(id)).orElseThrow(() -> new NotFoundException(type, id)); } private Predicate idStaysTheSame(String id) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ModificationsRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ModificationsRootResource.java index 28f855f40c..eaf165cda1 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ModificationsRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ModificationsRootResource.java @@ -3,11 +3,8 @@ package sonia.scm.api.v2.resources; import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; -import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.Modifications; import sonia.scm.repository.NamespaceAndName; -import sonia.scm.repository.RepositoryNotFoundException; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; import sonia.scm.web.VndMediaType; @@ -46,7 +43,7 @@ public class ModificationsRootResource { @Produces(VndMediaType.MODIFICATIONS) @TypeHint(ModificationsDto.class) @Path("{revision}") - public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision) throws IOException, RevisionNotFoundException, RepositoryNotFoundException , InternalRepositoryException { + public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision) throws IOException { try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { Modifications modifications = repositoryService.getModificationsCommand() .revision(revision) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/NotFoundExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/NotFoundExceptionMapper.java index 04264a7629..05c5eccec3 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/NotFoundExceptionMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/NotFoundExceptionMapper.java @@ -32,18 +32,20 @@ package sonia.scm.api.v2.resources; import sonia.scm.NotFoundException; -import sonia.scm.api.rest.StatusExceptionMapper; +import sonia.scm.web.VndMediaType; +import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; /** * @since 2.0.0 */ @Provider -public class NotFoundExceptionMapper extends StatusExceptionMapper { - - public NotFoundExceptionMapper() { - super(NotFoundException.class, Response.Status.NOT_FOUND); +public class NotFoundExceptionMapper implements ExceptionMapper { + @Override + public Response toResponse(NotFoundException exception) { + return Response.status(Response.Status.NOT_FOUND).entity(ErrorDto.from(exception)).type(VndMediaType.ERROR_TYPE).build(); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java index b1ea912acf..71f0e03516 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java @@ -109,7 +109,7 @@ public class PermissionRootResource { .filter(filterPermission(permissionName)) .map(permission -> modelToDtoMapper.map(permission, repository)) .findFirst() - .orElseThrow(NotFoundException::new) + .orElseThrow(() -> NotFoundException.notFound(Permission.class, namespace).in(Repository.class, namespace + "/" + name).build()) ).build(); } @@ -164,7 +164,7 @@ public class PermissionRootResource { RepositoryPermissions.permissionWrite(repository).check(); String extractedPermissionName = getPermissionName(permissionName); if (!isPermissionExist(new PermissionDto(extractedPermissionName, isGroupPermission(permissionName)), repository)) { - throw new NotFoundException("permission", extractedPermissionName); + throw NotFoundException.notFound(Permission.class, namespace).in(Repository.class, namespace + "/" + name).build(); } permission.setGroupPermission(isGroupPermission(permissionName)); if (!extractedPermissionName.equals(permission.getName())) { @@ -174,7 +174,7 @@ public class PermissionRootResource { .stream() .filter(filterPermission(permissionName)) .findFirst() - .orElseThrow(NotFoundException::new); + .orElseThrow(() -> NotFoundException.notFound(Permission.class, namespace).in(Repository.class, namespace + "/" + name).build()); dtoToModelMapper.modify(existingPermission, permission); manager.modify(repository); log.info("the permission with name: {} is updated.", permissionName); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java index 56bb50d4e6..77df2d8e50 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java @@ -203,8 +203,8 @@ public class RepositoryResource { } } - private Supplier> loadBy(String namespace, String name) { - return () -> Optional.ofNullable(manager.get(new NamespaceAndName(namespace, name))); + private Supplier loadBy(String namespace, String name) { + return () -> Optional.ofNullable(manager.get(new NamespaceAndName(namespace, name))).orElseThrow(() -> new NotFoundException(Repository.class, namespace + "/" + name)); } private Predicate nameAndNamespaceStaysTheSame(String namespace, String name) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SingleResourceManagerAdapter.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SingleResourceManagerAdapter.java index d484567bf8..334dc72b20 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SingleResourceManagerAdapter.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SingleResourceManagerAdapter.java @@ -38,7 +38,10 @@ class SingleResourceManagerAdapter Optional.empty()); } - SingleResourceManagerAdapter(Manager manager, Class type, Function> errorHandler) { + SingleResourceManagerAdapter( + Manager manager, + Class type, + Function> errorHandler) { super(manager, type); this.errorHandler = errorHandler; } @@ -47,25 +50,16 @@ class SingleResourceManagerAdapter> reader, Function mapToDto) { - return reader.get() - .map(mapToDto) - .map(Response::ok) - .map(Response.ResponseBuilder::build) - .orElseThrow(NotFoundException::new); - } - public Response update(Supplier> reader, Function applyChanges, Predicate hasSameKey, Consumer checker) throws NotFoundException, ConcurrentModificationException { - MODEL_OBJECT existingModelObject = reader.get().orElseThrow(NotFoundException::new); - checker.accept(existingModelObject); - return update(reader,applyChanges,hasSameKey); + Response get(Supplier reader, Function mapToDto) { + return Response.ok(mapToDto.apply(reader.get())).build(); } /** * Update the model object for the given id according to the given function and returns a corresponding http response. * This handles all corner cases, eg. no matching object for the id or missing privileges. */ - public Response update(Supplier> reader, Function applyChanges, Predicate hasSameKey) throws NotFoundException, ConcurrentModificationException { - MODEL_OBJECT existingModelObject = reader.get().orElseThrow(NotFoundException::new); + Response update(Supplier reader, Function applyChanges, Predicate hasSameKey) throws ConcurrentModificationException { + MODEL_OBJECT existingModelObject = reader.get(); MODEL_OBJECT changedModelObject = applyChanges.apply(existingModelObject); if (!hasSameKey.test(changedModelObject)) { return Response.status(BAD_REQUEST).entity("illegal change of id").build(); @@ -81,11 +75,13 @@ class SingleResourceManagerAdapter updated.getLastModified()); } - public Response delete(Supplier> reader) { - return reader.get() - .map(MODEL_OBJECT::getId) - .map(this::delete) - .orElse(null); + public Response delete(Supplier reader) { + try { + return delete(reader.get().getId()); + } catch (NotFoundException e) { + // due to idempotency of delete this does not matter here. + return null; + } } @Override diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java index e3cf17d3a4..42be4daf89 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java @@ -1,10 +1,8 @@ package sonia.scm.api.v2.resources; -import sonia.scm.NotFoundException; import sonia.scm.repository.BrowserResult; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.RepositoryNotFoundException; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.api.BrowseCommandBuilder; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; @@ -33,14 +31,14 @@ public class SourceRootResource { @GET @Produces(VndMediaType.SOURCE) @Path("") - public Response getAllWithoutRevision(@PathParam("namespace") String namespace, @PathParam("name") String name) throws RevisionNotFoundException, RepositoryNotFoundException, IOException { + public Response getAllWithoutRevision(@PathParam("namespace") String namespace, @PathParam("name") String name) throws IOException { return getSource(namespace, name, "/", null); } @GET @Produces(VndMediaType.SOURCE) @Path("{revision}") - public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision) throws RevisionNotFoundException, RepositoryNotFoundException, IOException { + public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision) throws IOException { return getSource(namespace, name, "/", revision); } @@ -51,7 +49,7 @@ public class SourceRootResource { return getSource(namespace, name, path, revision); } - private Response getSource(String namespace, String repoName, String path, String revision) throws IOException, RevisionNotFoundException, RepositoryNotFoundException { + private Response getSource(String namespace, String repoName, String path, String revision) throws IOException { NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, repoName); try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) { BrowseCommandBuilder browseCommand = repositoryService.getBrowseCommand(); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagNotFoundException.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagNotFoundException.java deleted file mode 100644 index b572d20c5b..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagNotFoundException.java +++ /dev/null @@ -1,7 +0,0 @@ -package sonia.scm.api.v2.resources; - -import sonia.scm.NotFoundException; - -public class TagNotFoundException extends NotFoundException { - -} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagRootResource.java index b3601f7709..6b4f8b2f96 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagRootResource.java @@ -3,6 +3,7 @@ package sonia.scm.api.v2.resources; import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; +import sonia.scm.NotFoundException; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryNotFoundException; @@ -47,7 +48,7 @@ public class TagRootResource { }) @Produces(VndMediaType.TAG_COLLECTION) @TypeHint(CollectionDto.class) - public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name) throws IOException, RepositoryNotFoundException { + public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name) throws IOException { try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { Tags tags = getTags(repositoryService); if (tags != null && tags.getTags() != null) { @@ -72,7 +73,7 @@ public class TagRootResource { @Produces(VndMediaType.TAG) @TypeHint(TagDto.class) @Path("{tagName}") - public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("tagName") String tagName) throws IOException, RepositoryNotFoundException, TagNotFoundException { + public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("tagName") String tagName) throws IOException { NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name); try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) { Tags tags = getTags(repositoryService); @@ -80,7 +81,7 @@ public class TagRootResource { Tag tag = tags.getTags().stream() .filter(t -> tagName.equals(t.getName())) .findFirst() - .orElseThrow(TagNotFoundException::new); + .orElseThrow(() -> createNotFoundException(namespace, name, tagName)); return Response.ok(tagToTagDtoMapper.map(tag, namespaceAndName)).build(); } else { return Response.status(Response.Status.INTERNAL_SERVER_ERROR) @@ -90,6 +91,10 @@ public class TagRootResource { } } + private NotFoundException createNotFoundException(String namespace, String name, String tagName) { + return NotFoundException.notFound("Tag", tagName).in("Repository", namespace + "/" + name).build(); + } + private Tags getTags(RepositoryService repositoryService) throws IOException { Repository repository = repositoryService.getRepository(); RepositoryPermissions.read(repository).check(); diff --git a/scm-webapp/src/main/java/sonia/scm/group/DefaultGroupManager.java b/scm-webapp/src/main/java/sonia/scm/group/DefaultGroupManager.java index 236c6d0ebc..662d9407ca 100644 --- a/scm-webapp/src/main/java/sonia/scm/group/DefaultGroupManager.java +++ b/scm-webapp/src/main/java/sonia/scm/group/DefaultGroupManager.java @@ -172,7 +172,7 @@ public class DefaultGroupManager extends AbstractGroupManager if (fresh == null) { - throw new NotFoundException("group", group.getId()); + throw new NotFoundException(Group.class, group.getId()); } fresh.copyProperties(group); diff --git a/scm-webapp/src/main/java/sonia/scm/user/DefaultUserManager.java b/scm-webapp/src/main/java/sonia/scm/user/DefaultUserManager.java index 4efaaa1d1c..0f2f45ff0b 100644 --- a/scm-webapp/src/main/java/sonia/scm/user/DefaultUserManager.java +++ b/scm-webapp/src/main/java/sonia/scm/user/DefaultUserManager.java @@ -219,7 +219,7 @@ public class DefaultUserManager extends AbstractUserManager if (fresh == null) { - throw new NotFoundException(); + throw new NotFoundException(User.class, user.getName()); } fresh.copyProperties(user); @@ -419,7 +419,7 @@ public class DefaultUserManager extends AbstractUserManager public void overwritePassword(String userId, String newPassword) { User user = get(userId); if (user == null) { - throw new NotFoundException(); + throw new NotFoundException(User.class, userId); } if (!isTypeDefault(user)) { throw new ChangePasswordNotAllowedException(user.getType()); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ContentResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ContentResourceTest.java index 3d898119fb..bb7b0e5458 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ContentResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ContentResourceTest.java @@ -8,8 +8,8 @@ import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import sonia.scm.NotFoundException; import sonia.scm.repository.NamespaceAndName; -import sonia.scm.repository.PathNotFoundException; import sonia.scm.repository.RepositoryNotFoundException; import sonia.scm.repository.api.CatCommandBuilder; import sonia.scm.repository.api.RepositoryService; @@ -59,7 +59,7 @@ public class ContentResourceTest { // defaults for unknown things doThrow(new RepositoryNotFoundException("x")).when(repositoryServiceFactory).create(not(eq(existingNamespaceAndName))); - doThrow(new PathNotFoundException("x")).when(catCommand).getStream(any()); + doThrow(new NotFoundException("Test", "X")).when(catCommand).getStream(any()); } @Test @@ -175,7 +175,7 @@ public class ContentResourceTest { } @Override - public void close() throws IOException { + public void close() { closed = true; } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DiffResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DiffResourceTest.java index fe22c944c4..406547b951 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DiffResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DiffResourceTest.java @@ -17,11 +17,11 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import sonia.scm.NotFoundException; import sonia.scm.api.rest.AuthorizationExceptionMapper; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryNotFoundException; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.api.DiffCommandBuilder; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; @@ -62,7 +62,7 @@ public class DiffResourceTest extends RepositoryTestBase { @Before - public void prepareEnvironment() throws Exception { + public void prepareEnvironment() { diffRootResource = new DiffRootResource(serviceFactory); super.diffRootResource = Providers.of(diffRootResource); dispatcher.getRegistry().addSingletonResource(getRepositoryRootResource()); @@ -108,7 +108,7 @@ public class DiffResourceTest extends RepositoryTestBase { @Test public void shouldGet404OnMissingRepository() throws URISyntaxException, RepositoryNotFoundException { - when(serviceFactory.create(any(NamespaceAndName.class))).thenThrow(RepositoryNotFoundException.class); + when(serviceFactory.create(any(NamespaceAndName.class))).thenThrow(new NotFoundException("Text", "x")); MockHttpRequest request = MockHttpRequest .get(DIFF_URL + "revision") .accept(VndMediaType.DIFF); @@ -120,20 +120,22 @@ public class DiffResourceTest extends RepositoryTestBase { @Test public void shouldGet404OnMissingRevision() throws Exception { when(diffCommandBuilder.setRevision(anyString())).thenReturn(diffCommandBuilder); - when(diffCommandBuilder.retriveContent(any())).thenThrow(RevisionNotFoundException.class); + when(diffCommandBuilder.retriveContent(any())).thenThrow(new NotFoundException("Text", "x")); MockHttpRequest request = MockHttpRequest .get(DIFF_URL + "revision") .accept(VndMediaType.DIFF); MockHttpResponse response = new MockHttpResponse(); + dispatcher.invoke(request, response); + assertEquals(404, response.getStatus()); } @Test public void shouldGet400OnCrlfInjection() throws Exception { when(diffCommandBuilder.setRevision(anyString())).thenReturn(diffCommandBuilder); - when(diffCommandBuilder.retriveContent(any())).thenThrow(RevisionNotFoundException.class); + when(diffCommandBuilder.retriveContent(any())).thenThrow(new NotFoundException("Text", "x")); MockHttpRequest request = MockHttpRequest .get(DIFF_URL + "ny%0D%0ASet-cookie:%20Tamper=3079675143472450634") diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileHistoryResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileHistoryResourceTest.java index 934e05d0d1..4f00f9ba5b 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileHistoryResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileHistoryResourceTest.java @@ -18,6 +18,7 @@ import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import sonia.scm.NotFoundException; import sonia.scm.api.rest.AuthorizationExceptionMapper; import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; @@ -26,7 +27,6 @@ import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.Person; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryNotFoundException; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.api.LogCommandBuilder; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; @@ -79,7 +79,7 @@ public class FileHistoryResourceTest extends RepositoryTestBase { @Before - public void prepareEnvironment() throws Exception { + public void prepareEnvironment() { fileHistoryCollectionToDtoMapper = new FileHistoryCollectionToDtoMapper(changesetToChangesetDtoMapper, resourceLinks); fileHistoryRootResource = new FileHistoryRootResource(serviceFactory, fileHistoryCollectionToDtoMapper); super.fileHistoryRootResource = Providers.of(fileHistoryRootResource); @@ -134,7 +134,7 @@ public class FileHistoryResourceTest extends RepositoryTestBase { @Test public void shouldGet404OnMissingRepository() throws URISyntaxException, RepositoryNotFoundException { - when(serviceFactory.create(any(NamespaceAndName.class))).thenThrow(RepositoryNotFoundException.class); + when(serviceFactory.create(any(NamespaceAndName.class))).thenThrow(new NotFoundException("Text", "x")); MockHttpRequest request = MockHttpRequest .get(FILE_HISTORY_URL + "revision/a.txt") .accept(VndMediaType.CHANGESET_COLLECTION); @@ -152,7 +152,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(RevisionNotFoundException.class); + when(logCommandBuilder.getChangesets()).thenThrow(new NotFoundException("Text", "x")); MockHttpRequest request = MockHttpRequest .get(FILE_HISTORY_URL + id + "/" + path) diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java index c84a74bc92..cca7493162 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java @@ -14,7 +14,6 @@ import sonia.scm.repository.BrowserResult; import sonia.scm.repository.FileObject; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.RepositoryNotFoundException; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.api.BrowseCommandBuilder; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; @@ -69,7 +68,7 @@ public class SourceRootResourceTest extends RepositoryTestBase { } @Test - public void shouldReturnSources() throws URISyntaxException, IOException, RevisionNotFoundException { + public void shouldReturnSources() throws URISyntaxException, IOException { BrowserResult result = createBrowserResult(); when(browseCommandBuilder.getBrowserResult()).thenReturn(result); MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/sources"); @@ -83,7 +82,7 @@ public class SourceRootResourceTest extends RepositoryTestBase { @Test public void shouldReturn404IfRepoNotFound() throws URISyntaxException, RepositoryNotFoundException { - when(serviceFactory.create(new NamespaceAndName("idont", "exist"))).thenThrow(RepositoryNotFoundException.class); + when(serviceFactory.create(new NamespaceAndName("idont", "exist"))).thenThrow(new RepositoryNotFoundException("abc")); MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "idont/exist/sources"); MockHttpResponse response = new MockHttpResponse(); @@ -92,7 +91,7 @@ public class SourceRootResourceTest extends RepositoryTestBase { } @Test - public void shouldGetResultForSingleFile() throws URISyntaxException, IOException, RevisionNotFoundException { + public void shouldGetResultForSingleFile() throws URISyntaxException, IOException { BrowserResult browserResult = new BrowserResult(); browserResult.setRevision("revision"); FileObject fileObject = new FileObject(); @@ -111,7 +110,7 @@ public class SourceRootResourceTest extends RepositoryTestBase { @Test public void shouldGet404ForSingleFileIfRepoNotFound() throws URISyntaxException, RepositoryNotFoundException { - when(serviceFactory.create(new NamespaceAndName("idont", "exist"))).thenThrow(RepositoryNotFoundException.class); + when(serviceFactory.create(new NamespaceAndName("idont", "exist"))).thenThrow(new RepositoryNotFoundException("abc")); MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "idont/exist/sources/revision/fileabc"); MockHttpResponse response = new MockHttpResponse(); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java index b363b66bfb..1451498124 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java @@ -185,7 +185,7 @@ public class UserRootResourceTest { .content(content.getBytes()); MockHttpResponse response = new MockHttpResponse(); - doThrow(NotFoundException.class).when(userManager).overwritePassword(any(), any()); + doThrow(new NotFoundException("Test", "x")).when(userManager).overwritePassword(any(), any()); dispatcher.invoke(request, response); diff --git a/scm-webapp/src/test/java/sonia/scm/web/protocol/HttpProtocolServletTest.java b/scm-webapp/src/test/java/sonia/scm/web/protocol/HttpProtocolServletTest.java index 077020f60c..c22491d045 100644 --- a/scm-webapp/src/test/java/sonia/scm/web/protocol/HttpProtocolServletTest.java +++ b/scm-webapp/src/test/java/sonia/scm/web/protocol/HttpProtocolServletTest.java @@ -63,7 +63,7 @@ public class HttpProtocolServletTest { when(userAgentParser.parse(request)).thenReturn(userAgent); when(userAgent.isBrowser()).thenReturn(false); NamespaceAndName existingRepo = new NamespaceAndName("space", "repo"); - when(serviceFactory.create(not(eq(existingRepo)))).thenThrow(RepositoryNotFoundException.class); + when(serviceFactory.create(not(eq(existingRepo)))).thenThrow(new RepositoryNotFoundException("x")); when(serviceFactory.create(existingRepo)).thenReturn(repositoryService); when(requestProvider.get()).thenReturn(httpServletRequest); }