diff --git a/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java b/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java index 663d053ca5..5db81e521e 100644 --- a/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java +++ b/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java @@ -46,6 +46,7 @@ import sonia.scm.store.ConfigurationStoreFactory; import java.io.File; import java.io.IOException; import java.net.URL; +import java.nio.file.Path; //~--- JDK imports ------------------------------------------------------------ @@ -172,6 +173,6 @@ public abstract class AbstractSimpleRepositoryHandler extends RepositoryLocationResolver { + + private final Class type; + + protected BasicRepositoryLocationResolver(Class type) { + this.type = type; + } + + @Override + public boolean supportsLocationType(Class type) { + return type.isAssignableFrom(this.type); + } +} diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryLocationResolver.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryLocationResolver.java index 737374025d..28b0f34308 100644 --- a/scm-core/src/main/java/sonia/scm/repository/RepositoryLocationResolver.java +++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryLocationResolver.java @@ -1,51 +1,19 @@ package sonia.scm.repository; -import sonia.scm.SCMContextProvider; +public abstract class RepositoryLocationResolver { -import javax.inject.Inject; -import java.nio.file.Path; + public abstract boolean supportsLocationType(Class type); -/** - * A Location Resolver for File based Repository Storage. - *

- * WARNING: The Locations provided with this class may not be used from the plugins to store any plugin specific files. - *

- * Please use the {@link sonia.scm.store.DataStoreFactory } and the {@link sonia.scm.store.DataStore} classes to store data
- * Please use the {@link sonia.scm.store.BlobStoreFactory } and the {@link sonia.scm.store.BlobStore} classes to store binary files
- * Please use the {@link sonia.scm.store.ConfigurationStoreFactory} and the {@link sonia.scm.store.ConfigurationStore} classes to store configurations - * - * @author Mohamed Karray - * @since 2.0.0 - */ -public class RepositoryLocationResolver { + protected abstract RepositoryLocationResolverInstance create(Class type); - private final SCMContextProvider contextProvider; - private final RepositoryDAO repositoryDAO; - private final InitialRepositoryLocationResolver initialRepositoryLocationResolver; - - @Inject - public RepositoryLocationResolver(SCMContextProvider contextProvider, RepositoryDAO repositoryDAO, InitialRepositoryLocationResolver initialRepositoryLocationResolver) { - this.contextProvider = contextProvider; - this.repositoryDAO = repositoryDAO; - this.initialRepositoryLocationResolver = initialRepositoryLocationResolver; + public final RepositoryLocationResolverInstance forClass(Class type) { + if (!supportsLocationType(type)) { + throw new IllegalStateException("no support for location of class " + type); + } + return create(type); } - /** - * Returns the path to the repository. - * - * @param repositoryId repository id - * - * @return path of repository - */ - public Path getPath(String repositoryId) { - Path path; - - if (repositoryDAO instanceof PathBasedRepositoryDAO) { - path = ((PathBasedRepositoryDAO) repositoryDAO).getPath(repositoryId); - } else { - path = initialRepositoryLocationResolver.getPath(repositoryId); - } - - return contextProvider.resolve(path); + public interface RepositoryLocationResolverInstance { + T getLocation(String repositoryId); } } diff --git a/scm-dao-xml/src/main/java/sonia/scm/repository/xml/PathBasedRepositoryLocationResolver.java b/scm-dao-xml/src/main/java/sonia/scm/repository/xml/PathBasedRepositoryLocationResolver.java new file mode 100644 index 0000000000..caef4026b7 --- /dev/null +++ b/scm-dao-xml/src/main/java/sonia/scm/repository/xml/PathBasedRepositoryLocationResolver.java @@ -0,0 +1,53 @@ +package sonia.scm.repository.xml; + +import sonia.scm.SCMContextProvider; +import sonia.scm.repository.BasicRepositoryLocationResolver; +import sonia.scm.repository.InitialRepositoryLocationResolver; +import sonia.scm.repository.PathBasedRepositoryDAO; +import sonia.scm.repository.RepositoryDAO; +import sonia.scm.repository.RepositoryLocationResolver; + +import javax.inject.Inject; +import java.nio.file.Path; + +/** + * A Location Resolver for File based Repository Storage. + *

+ * WARNING: The Locations provided with this class may not be used from the plugins to store any plugin specific files. + *

+ * Please use the {@link sonia.scm.store.DataStoreFactory } and the {@link sonia.scm.store.DataStore} classes to store data
+ * Please use the {@link sonia.scm.store.BlobStoreFactory } and the {@link sonia.scm.store.BlobStore} classes to store binary files
+ * Please use the {@link sonia.scm.store.ConfigurationStoreFactory} and the {@link sonia.scm.store.ConfigurationStore} classes to store configurations + * + * @author Mohamed Karray + * @since 2.0.0 + */ +public class PathBasedRepositoryLocationResolver extends BasicRepositoryLocationResolver { + + private final SCMContextProvider contextProvider; + private final InitialRepositoryLocationResolver initialRepositoryLocationResolver; + private final RepositoryDAO repositoryDAO; + + @Inject + public PathBasedRepositoryLocationResolver(SCMContextProvider contextProvider, RepositoryDAO repositoryDAO, InitialRepositoryLocationResolver initialRepositoryLocationResolver) { + super(Path.class); + this.contextProvider = contextProvider; + this.initialRepositoryLocationResolver = initialRepositoryLocationResolver; + this.repositoryDAO = repositoryDAO; + } + + @Override + protected RepositoryLocationResolverInstance create(Class type) { + return repositoryId -> { + Path path; + + if (repositoryDAO instanceof PathBasedRepositoryDAO) { + path = ((PathBasedRepositoryDAO) repositoryDAO).getPath(repositoryId); + } else { + path = initialRepositoryLocationResolver.getPath(repositoryId); + } + + return (T) contextProvider.resolve(path); + }; + } +} diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/FileBasedStoreFactory.java b/scm-dao-xml/src/main/java/sonia/scm/store/FileBasedStoreFactory.java index d37a150723..d31179c1c2 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/FileBasedStoreFactory.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/FileBasedStoreFactory.java @@ -40,6 +40,7 @@ import sonia.scm.repository.RepositoryLocationResolver; import sonia.scm.util.IOUtil; import java.io.File; +import java.nio.file.Path; //~--- JDK imports ------------------------------------------------------------ @@ -58,7 +59,7 @@ public abstract class FileBasedStoreFactory { private RepositoryLocationResolver repositoryLocationResolver; private Store store; - protected FileBasedStoreFactory(SCMContextProvider contextProvider , RepositoryLocationResolver repositoryLocationResolver, Store store) { + protected FileBasedStoreFactory(SCMContextProvider contextProvider, RepositoryLocationResolver repositoryLocationResolver, Store store) { this.contextProvider = contextProvider; this.repositoryLocationResolver = repositoryLocationResolver; this.store = store; @@ -92,7 +93,7 @@ public abstract class FileBasedStoreFactory { * @return the store directory of a specific repository */ private File getStoreDirectory(Store store, Repository repository) { - return new File(repositoryLocationResolver.getPath(repository.getId()).toFile(), store.getRepositoryStoreDirectory()); + return new File(repositoryLocationResolver.forClass(Path.class).getLocation(repository.getId()).toFile(), store.getRepositoryStoreDirectory()); } /** diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/FileBlobStoreFactory.java b/scm-dao-xml/src/main/java/sonia/scm/store/FileBlobStoreFactory.java index 7e2e5a9e29..4a7c9fc713 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/FileBlobStoreFactory.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/FileBlobStoreFactory.java @@ -65,7 +65,7 @@ public class FileBlobStoreFactory extends FileBasedStoreFactory implements BlobS * @param keyGenerator key generator */ @Inject - public FileBlobStoreFactory(SCMContextProvider contextProvider ,RepositoryLocationResolver repositoryLocationResolver, KeyGenerator keyGenerator) { + public FileBlobStoreFactory(SCMContextProvider contextProvider , RepositoryLocationResolver repositoryLocationResolver, KeyGenerator keyGenerator) { super(contextProvider, repositoryLocationResolver, Store.BLOB); this.keyGenerator = keyGenerator; } diff --git a/scm-core/src/test/java/sonia/scm/repository/RepositoryLocationResolverTest.java b/scm-dao-xml/src/test/java/sonia/scm/repository/xml/PathBasedRepositoryLocationResolverTest.java similarity index 64% rename from scm-core/src/test/java/sonia/scm/repository/RepositoryLocationResolverTest.java rename to scm-dao-xml/src/test/java/sonia/scm/repository/xml/PathBasedRepositoryLocationResolverTest.java index 05f9af3773..be42f0f6b9 100644 --- a/scm-core/src/test/java/sonia/scm/repository/RepositoryLocationResolverTest.java +++ b/scm-dao-xml/src/test/java/sonia/scm/repository/xml/PathBasedRepositoryLocationResolverTest.java @@ -1,4 +1,4 @@ -package sonia.scm.repository; +package sonia.scm.repository.xml; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -7,6 +7,9 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.stubbing.Answer; import sonia.scm.SCMContextProvider; +import sonia.scm.repository.InitialRepositoryLocationResolver; +import sonia.scm.repository.PathBasedRepositoryDAO; +import sonia.scm.repository.RepositoryDAO; import java.nio.file.Path; import java.nio.file.Paths; @@ -16,7 +19,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @ExtendWith({MockitoExtension.class}) -class RepositoryLocationResolverTest { +class PathBasedRepositoryLocationResolverTest { @Mock private SCMContextProvider contextProvider; @@ -30,14 +33,13 @@ class RepositoryLocationResolverTest { @Mock private InitialRepositoryLocationResolver initialRepositoryLocationResolver; - @BeforeEach void beforeEach() { when(contextProvider.resolve(any(Path.class))).then((Answer) invocationOnMock -> invocationOnMock.getArgument(0)); } - private RepositoryLocationResolver createResolver(RepositoryDAO pathBasedRepositoryDAO) { - return new RepositoryLocationResolver(contextProvider, pathBasedRepositoryDAO, initialRepositoryLocationResolver); + private PathBasedRepositoryLocationResolver createResolver(RepositoryDAO pathBasedRepositoryDAO) { + return new PathBasedRepositoryLocationResolver(contextProvider, pathBasedRepositoryDAO, initialRepositoryLocationResolver); } @Test @@ -45,8 +47,8 @@ class RepositoryLocationResolverTest { Path repositoryPath = Paths.get("repos", "42"); when(pathBasedRepositoryDAO.getPath("42")).thenReturn(repositoryPath); - RepositoryLocationResolver resolver = createResolver(pathBasedRepositoryDAO); - Path path = resolver.getPath("42"); + PathBasedRepositoryLocationResolver resolver = createResolver(pathBasedRepositoryDAO); + Path path = resolver.forClass(Path.class).getLocation("42"); assertThat(path).isSameAs(repositoryPath); } @@ -56,8 +58,8 @@ class RepositoryLocationResolverTest { Path repositoryPath = Paths.get("r", "42"); when(initialRepositoryLocationResolver.getPath("42")).thenReturn(repositoryPath); - RepositoryLocationResolver resolver = createResolver(repositoryDAO); - Path path = resolver.getPath("42"); + PathBasedRepositoryLocationResolver resolver = createResolver(repositoryDAO); + Path path = resolver.forClass(Path.class).getLocation("42"); assertThat(path).isSameAs(repositoryPath); } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitRepositoryContextResolver.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitRepositoryContextResolver.java index 8acfc68dce..737776f3ac 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitRepositoryContextResolver.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/protocolcommand/git/GitRepositoryContextResolver.java @@ -26,7 +26,7 @@ public class GitRepositoryContextResolver implements RepositoryContextResolver { public RepositoryContext resolve(String[] args) { NamespaceAndName namespaceAndName = extractNamespaceAndName(args); Repository repository = repositoryManager.get(namespaceAndName); - Path path = locationResolver.getPath(repository.getId()).resolve("data"); + Path path = locationResolver.forClass(Path.class).getLocation(repository.getId()).resolve("data"); return new RepositoryContext(repository, path); } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/protocolcommand/git/GitRepositoryContextResolverTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/protocolcommand/git/GitRepositoryContextResolverTest.java index 6ac4cdb54b..7e7566f6f9 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/protocolcommand/git/GitRepositoryContextResolverTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/protocolcommand/git/GitRepositoryContextResolverTest.java @@ -2,6 +2,7 @@ package sonia.scm.protocolcommand.git; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -16,6 +17,7 @@ import java.io.IOException; import java.nio.file.Path; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) @@ -25,7 +27,7 @@ class GitRepositoryContextResolverTest { @Mock RepositoryManager repositoryManager; - @Mock + @Mock(answer = Answers.RETURNS_DEEP_STUBS) RepositoryLocationResolver locationResolver; @InjectMocks @@ -35,7 +37,7 @@ class GitRepositoryContextResolverTest { void shouldResolveCorrectRepository() throws IOException { when(repositoryManager.get(new NamespaceAndName("space", "X"))).thenReturn(REPOSITORY); Path repositoryPath = File.createTempFile("test", "scm").toPath(); - when(locationResolver.getPath("id")).thenReturn(repositoryPath); + when(locationResolver.forClass(any()).getLocation("id")).thenReturn(repositoryPath); RepositoryContext context = resolver.resolve(new String[] {"git", "repo/space/X/something/else"}); diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java index cca89d8eb5..6c31d08e84 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java @@ -37,6 +37,7 @@ package sonia.scm.repository; import org.junit.Assume; import sonia.scm.SCMContext; +import sonia.scm.TempDirRepositoryLocationResolver; import sonia.scm.store.InMemoryConfigurationStoreFactory; import javax.servlet.http.HttpServletRequest; @@ -103,7 +104,7 @@ public final class HgTestUtil PathBasedRepositoryDAO repoDao = mock(PathBasedRepositoryDAO.class); - RepositoryLocationResolver repositoryLocationResolver = new RepositoryLocationResolver(context, repoDao, new InitialRepositoryLocationResolver()); + RepositoryLocationResolver repositoryLocationResolver = new TempDirRepositoryLocationResolver(directory); HgRepositoryHandler handler = new HgRepositoryHandler(new InMemoryConfigurationStoreFactory(), new HgContextProvider(), repositoryLocationResolver, null, null); Path repoDir = directory.toPath(); diff --git a/scm-test/src/main/java/sonia/scm/AbstractTestBase.java b/scm-test/src/main/java/sonia/scm/AbstractTestBase.java index 040b347e4a..48831fb670 100644 --- a/scm-test/src/main/java/sonia/scm/AbstractTestBase.java +++ b/scm-test/src/main/java/sonia/scm/AbstractTestBase.java @@ -91,7 +91,7 @@ public class AbstractTestBase contextProvider = MockUtil.getSCMContextProvider(tempDirectory); fileSystem = new DefaultFileSystem(); InitialRepositoryLocationResolver initialRepoLocationResolver = new InitialRepositoryLocationResolver(); - repositoryLocationResolver = new RepositoryLocationResolver(contextProvider, repositoryDAO, initialRepoLocationResolver); + repositoryLocationResolver = new TempDirRepositoryLocationResolver(tempDirectory); postSetUp(); } @@ -254,4 +254,5 @@ public class AbstractTestBase subjectThreadState = createThreadState(subject); subjectThreadState.bind(); } + } diff --git a/scm-test/src/main/java/sonia/scm/ManagerTestBase.java b/scm-test/src/main/java/sonia/scm/ManagerTestBase.java index 823e88c9fc..3990b05df9 100644 --- a/scm-test/src/main/java/sonia/scm/ManagerTestBase.java +++ b/scm-test/src/main/java/sonia/scm/ManagerTestBase.java @@ -74,7 +74,7 @@ public abstract class ManagerTestBase contextProvider = MockUtil.getSCMContextProvider(temp); InitialRepositoryLocationResolver initialRepositoryLocationResolver = new InitialRepositoryLocationResolver(); RepositoryDAO repoDao = mock(RepositoryDAO.class); - locationResolver = new RepositoryLocationResolver(contextProvider, repoDao ,initialRepositoryLocationResolver); + locationResolver = new TempDirRepositoryLocationResolver(temp); manager = createManager(); manager.init(contextProvider); } diff --git a/scm-test/src/main/java/sonia/scm/TempDirRepositoryLocationResolver.java b/scm-test/src/main/java/sonia/scm/TempDirRepositoryLocationResolver.java new file mode 100644 index 0000000000..1dfc22e15a --- /dev/null +++ b/scm-test/src/main/java/sonia/scm/TempDirRepositoryLocationResolver.java @@ -0,0 +1,21 @@ +package sonia.scm; + +import sonia.scm.repository.BasicRepositoryLocationResolver; +import sonia.scm.repository.RepositoryLocationResolver; + +import java.io.File; +import java.nio.file.Path; + +public class TempDirRepositoryLocationResolver extends BasicRepositoryLocationResolver { + private final File tempDirectory; + + public TempDirRepositoryLocationResolver(File tempDirectory) { + super(Path.class); + this.tempDirectory = tempDirectory; + } + + @Override + protected RepositoryLocationResolverInstance create(Class type) { + return repositoryId -> (T) tempDirectory.toPath(); + } +} diff --git a/scm-test/src/main/java/sonia/scm/repository/SimpleRepositoryHandlerTestBase.java b/scm-test/src/main/java/sonia/scm/repository/SimpleRepositoryHandlerTestBase.java index f48744d460..a03f57f72d 100644 --- a/scm-test/src/main/java/sonia/scm/repository/SimpleRepositoryHandlerTestBase.java +++ b/scm-test/src/main/java/sonia/scm/repository/SimpleRepositoryHandlerTestBase.java @@ -44,6 +44,7 @@ import java.io.IOException; import java.nio.file.Path; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -78,7 +79,10 @@ public abstract class SimpleRepositoryHandlerTestBase extends AbstractTestBase { locationResolver = mock(RepositoryLocationResolver.class); - when(locationResolver.getPath(anyString())).then(ic -> { + RepositoryLocationResolver.RepositoryLocationResolverInstance instanceMock = mock(RepositoryLocationResolver.RepositoryLocationResolverInstance.class); + when(locationResolver.forClass(any())).thenReturn(instanceMock); + + when(instanceMock.getLocation(anyString())).then(ic -> { String id = ic.getArgument(0); return baseDirectory.toPath().resolve(id); }); diff --git a/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerTest.java b/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerTest.java index 1fb50db14f..17569a6763 100644 --- a/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerTest.java +++ b/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerTest.java @@ -59,6 +59,7 @@ import sonia.scm.repository.api.HookContext; import sonia.scm.repository.api.HookContextFactory; import sonia.scm.repository.api.HookFeature; import sonia.scm.repository.spi.HookContextProvider; +import sonia.scm.repository.xml.PathBasedRepositoryLocationResolver; import sonia.scm.repository.xml.XmlRepositoryDAO; import sonia.scm.security.DefaultKeyGenerator; import sonia.scm.security.KeyGenerator; @@ -412,7 +413,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase { Set handlerSet = new HashSet<>(); InitialRepositoryLocationResolver initialRepositoryLocationResolver = new InitialRepositoryLocationResolver(); XmlRepositoryDAO repositoryDAO = new XmlRepositoryDAO(contextProvider, initialRepositoryLocationResolver, fileSystem); - RepositoryLocationResolver repositoryLocationResolver = new RepositoryLocationResolver(contextProvider, repositoryDAO, initialRepositoryLocationResolver); + PathBasedRepositoryLocationResolver repositoryLocationResolver = new PathBasedRepositoryLocationResolver(contextProvider, repositoryDAO, initialRepositoryLocationResolver); ConfigurationStoreFactory factory = new JAXBConfigurationStoreFactory(contextProvider, repositoryLocationResolver); handlerSet.add(new DummyRepositoryHandler(factory, repositoryLocationResolver)); handlerSet.add(new DummyRepositoryHandler(factory, repositoryLocationResolver) {