mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-10 15:35:49 +01:00
Introduce abstraction for repository location
This commit is contained in:
@@ -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<C extends RepositoryConfig
|
||||
}
|
||||
|
||||
private File resolveNativeDirectory(String repositoryId) {
|
||||
return repositoryLocationResolver.getPath(repositoryId).resolve(REPOSITORIES_NATIVE_DIRECTORY).toFile();
|
||||
return repositoryLocationResolver.forClass(Path.class).getLocation(repositoryId).resolve(REPOSITORIES_NATIVE_DIRECTORY).toFile();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package sonia.scm.repository;
|
||||
|
||||
public abstract class BasicRepositoryLocationResolver<T> extends RepositoryLocationResolver {
|
||||
|
||||
private final Class<T> type;
|
||||
|
||||
protected BasicRepositoryLocationResolver(Class<T> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLocationType(Class<?> type) {
|
||||
return type.isAssignableFrom(this.type);
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
* <p>
|
||||
* <b>WARNING:</b> The Locations provided with this class may not be used from the plugins to store any plugin specific files.
|
||||
* <p>
|
||||
* Please use the {@link sonia.scm.store.DataStoreFactory } and the {@link sonia.scm.store.DataStore} classes to store data<br>
|
||||
* Please use the {@link sonia.scm.store.BlobStoreFactory } and the {@link sonia.scm.store.BlobStore} classes to store binary files<br>
|
||||
* 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 <T> RepositoryLocationResolverInstance<T> create(Class<T> 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 <T> RepositoryLocationResolverInstance<T> forClass(Class<T> 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> {
|
||||
T getLocation(String repositoryId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* <b>WARNING:</b> The Locations provided with this class may not be used from the plugins to store any plugin specific files.
|
||||
* <p>
|
||||
* Please use the {@link sonia.scm.store.DataStoreFactory } and the {@link sonia.scm.store.DataStore} classes to store data<br>
|
||||
* Please use the {@link sonia.scm.store.BlobStoreFactory } and the {@link sonia.scm.store.BlobStore} classes to store binary files<br>
|
||||
* 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<Path> {
|
||||
|
||||
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 <T> RepositoryLocationResolverInstance<T> create(Class<T> type) {
|
||||
return repositoryId -> {
|
||||
Path path;
|
||||
|
||||
if (repositoryDAO instanceof PathBasedRepositoryDAO) {
|
||||
path = ((PathBasedRepositoryDAO) repositoryDAO).getPath(repositoryId);
|
||||
} else {
|
||||
path = initialRepositoryLocationResolver.getPath(repositoryId);
|
||||
}
|
||||
|
||||
return (T) contextProvider.resolve(path);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<Path>) 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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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"});
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public abstract class ManagerTestBase<T extends ModelObject>
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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 <T> RepositoryLocationResolverInstance<T> create(Class<T> type) {
|
||||
return repositoryId -> (T) tempDirectory.toPath();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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<Repository> {
|
||||
Set<RepositoryHandler> 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) {
|
||||
|
||||
Reference in New Issue
Block a user