mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-03-06 12:20:56 +01:00
re implement XmlRepositoryDAO
This commit is contained in:
24
pom.xml
24
pom.xml
@@ -142,6 +142,11 @@
|
||||
<artifactId>junit-vintage-engine</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit-pioneer</groupId>
|
||||
<artifactId>junit-pioneer</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-core</artifactId>
|
||||
@@ -159,6 +164,11 @@
|
||||
<artifactId>mockito-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-junit-jupiter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
@@ -325,6 +335,13 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit-pioneer</groupId>
|
||||
<artifactId>junit-pioneer</artifactId>
|
||||
<version>0.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-core</artifactId>
|
||||
@@ -346,6 +363,13 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-junit-jupiter</artifactId>
|
||||
<version>${mockito.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
|
||||
@@ -35,6 +35,7 @@ package sonia.scm;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
@@ -43,6 +44,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
|
||||
@@ -105,8 +107,26 @@ public class BasicContextProvider implements SCMContextProvider
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
BasicContextProvider(File baseDirectory, String version, Stage stage) {
|
||||
this.baseDirectory = baseDirectory;
|
||||
this.version = version;
|
||||
this.stage = stage;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
|
||||
@Override
|
||||
public Path resolve(Path path) {
|
||||
if (path.isAbsolute()) {
|
||||
return path;
|
||||
}
|
||||
|
||||
return baseDirectory.toPath().resolve(path);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
||||
@@ -37,6 +37,7 @@ package sonia.scm;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* The main class for retrieving the home and the version of the SCM-Manager.
|
||||
@@ -65,6 +66,17 @@ public interface SCMContextProvider extends Closeable
|
||||
*/
|
||||
public File getBaseDirectory();
|
||||
|
||||
/**
|
||||
* Resolves the given path against the base directory.
|
||||
*
|
||||
* @param path path to resolve
|
||||
*
|
||||
* @return absolute resolved path
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
Path resolve(Path path);
|
||||
|
||||
/**
|
||||
* Returns the current stage of SCM-Manager.
|
||||
*
|
||||
|
||||
@@ -168,6 +168,6 @@ public abstract class AbstractSimpleRepositoryHandler<C extends RepositoryConfig
|
||||
}
|
||||
|
||||
private File resolveNativeDirectory(String repositoryId) {
|
||||
return new File(repositoryLocationResolver.getRepositoryDirectory(repositoryId), REPOSITORIES_NATIVE_DIRECTORY);
|
||||
return repositoryLocationResolver.getPath(repositoryId).resolve(REPOSITORIES_NATIVE_DIRECTORY).toFile();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package sonia.scm.repository;
|
||||
|
||||
import sonia.scm.SCMContextProvider;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* A Location Resolver for File based Repository Storage.
|
||||
@@ -19,35 +17,17 @@ import java.io.File;
|
||||
*/
|
||||
public class InitialRepositoryLocationResolver {
|
||||
|
||||
public static final String DEFAULT_REPOSITORY_PATH = "repositories";
|
||||
private static final String DEFAULT_REPOSITORY_PATH = "repositories";
|
||||
|
||||
private final SCMContextProvider context;
|
||||
|
||||
@Inject
|
||||
public InitialRepositoryLocationResolver(SCMContextProvider context) {
|
||||
this.context = context;
|
||||
/**
|
||||
* Returns the initial path to repository.
|
||||
*
|
||||
* @param repositoryId id of the repository
|
||||
*
|
||||
* @return initial path of repository
|
||||
*/
|
||||
public Path getPath(String repositoryId) {
|
||||
return Paths.get(DEFAULT_REPOSITORY_PATH, repositoryId);
|
||||
}
|
||||
|
||||
public InitialRepositoryLocation getRelativeRepositoryPath(String repositoryId) {
|
||||
String relativePath = DEFAULT_REPOSITORY_PATH + File.separator + repositoryId;
|
||||
return new InitialRepositoryLocation(new File(context.getBaseDirectory(), relativePath), relativePath);
|
||||
}
|
||||
|
||||
public static class InitialRepositoryLocation {
|
||||
private final File absolutePath;
|
||||
private final String relativePath;
|
||||
|
||||
public InitialRepositoryLocation(File absolutePath, String relativePath) {
|
||||
this.absolutePath = absolutePath;
|
||||
this.relativePath = relativePath;
|
||||
}
|
||||
|
||||
public File getAbsolutePath() {
|
||||
return absolutePath;
|
||||
}
|
||||
|
||||
public String getRelativePath() {
|
||||
return relativePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package sonia.scm.repository;
|
||||
|
||||
import groovy.lang.Singleton;
|
||||
import sonia.scm.SCMContextProvider;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* A Location Resolver for File based Repository Storage.
|
||||
@@ -20,20 +22,33 @@ import java.io.File;
|
||||
@Singleton
|
||||
public class RepositoryLocationResolver {
|
||||
|
||||
private RepositoryDAO repositoryDAO;
|
||||
private InitialRepositoryLocationResolver initialRepositoryLocationResolver;
|
||||
private final SCMContextProvider contextProvider;
|
||||
private final RepositoryDAO repositoryDAO;
|
||||
private final InitialRepositoryLocationResolver initialRepositoryLocationResolver;
|
||||
|
||||
@Inject
|
||||
public RepositoryLocationResolver(RepositoryDAO repositoryDAO, InitialRepositoryLocationResolver initialRepositoryLocationResolver) {
|
||||
public RepositoryLocationResolver(SCMContextProvider contextProvider, RepositoryDAO repositoryDAO, InitialRepositoryLocationResolver initialRepositoryLocationResolver) {
|
||||
this.contextProvider = contextProvider;
|
||||
this.repositoryDAO = repositoryDAO;
|
||||
this.initialRepositoryLocationResolver = initialRepositoryLocationResolver;
|
||||
}
|
||||
|
||||
public File getRepositoryDirectory(String repositoryId) {
|
||||
/**
|
||||
* 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) {
|
||||
PathBasedRepositoryDAO pathBasedRepositoryDAO = (PathBasedRepositoryDAO) repositoryDAO;
|
||||
return pathBasedRepositoryDAO.getPath(repositoryId).toFile();
|
||||
path = ((PathBasedRepositoryDAO) repositoryDAO).getPath(repositoryId);
|
||||
} else {
|
||||
path = initialRepositoryLocationResolver.getPath(repositoryId);
|
||||
}
|
||||
return initialRepositoryLocationResolver.getRelativeRepositoryPath(repositoryId).getAbsolutePath();
|
||||
|
||||
return contextProvider.resolve(path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ import javax.xml.stream.XMLStreamWriter;
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.31
|
||||
*/
|
||||
public final class IndentXMLStreamWriter implements XMLStreamWriter
|
||||
public final class IndentXMLStreamWriter implements XMLStreamWriter, AutoCloseable
|
||||
{
|
||||
|
||||
/** line separator */
|
||||
@@ -475,7 +475,7 @@ public final class IndentXMLStreamWriter implements XMLStreamWriter
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** indent string */
|
||||
private String indent = " ";
|
||||
private String indent = " ";
|
||||
|
||||
/** current level */
|
||||
private int level = 0;
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package sonia.scm;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junitpioneer.jupiter.TempDirectory;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@ExtendWith(TempDirectory.class)
|
||||
class BasicContextProviderTest {
|
||||
|
||||
private Path baseDirectory;
|
||||
|
||||
private BasicContextProvider context;
|
||||
|
||||
@BeforeEach
|
||||
void setUpContext(@TempDirectory.TempDir Path baseDirectory) {
|
||||
this.baseDirectory = baseDirectory;
|
||||
context = new BasicContextProvider(baseDirectory.toFile(), "x.y.z", Stage.PRODUCTION);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnAbsolutePathAsIs(@TempDirectory.TempDir Path path) {
|
||||
Path absolutePath = path.toAbsolutePath();
|
||||
Path resolved = context.resolve(absolutePath);
|
||||
|
||||
assertThat(resolved).isSameAs(absolutePath);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveRelatePath() {
|
||||
Path path = Paths.get("repos", "42");
|
||||
Path resolved = context.resolve(path);
|
||||
|
||||
assertThat(resolved).isAbsolute();
|
||||
assertThat(resolved).startsWithRaw(baseDirectory);
|
||||
assertThat(resolved).endsWithRaw(path);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +1,23 @@
|
||||
package sonia.scm.repository;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import sonia.scm.SCMContextProvider;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class InitialRepositoryLocationResolverTest {
|
||||
|
||||
@Mock
|
||||
private SCMContextProvider context;
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
||||
@Before
|
||||
public void init() throws IOException {
|
||||
when(context.getBaseDirectory()).thenReturn(temporaryFolder.newFolder());
|
||||
}
|
||||
@ExtendWith({MockitoExtension.class})
|
||||
class InitialRepositoryLocationResolverTest {
|
||||
|
||||
@Test
|
||||
public void shouldComputeInitialDirectory() {
|
||||
InitialRepositoryLocationResolver resolver = new InitialRepositoryLocationResolver(context);
|
||||
InitialRepositoryLocationResolver.InitialRepositoryLocation directory = resolver.getRelativeRepositoryPath("ABC");
|
||||
void shouldComputeInitialPath() {
|
||||
InitialRepositoryLocationResolver resolver = new InitialRepositoryLocationResolver();
|
||||
Path path = resolver.getPath("42");
|
||||
|
||||
assertThat(directory.getAbsolutePath()).isEqualTo(new File(context.getBaseDirectory(), "repositories/ABC"));
|
||||
assertThat(directory.getRelativePath()).isEqualTo( "repositories/ABC");
|
||||
assertThat(path).isRelative();
|
||||
assertThat(path.toString()).isEqualTo("repositories" + File.separator + "42");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package sonia.scm.repository;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import sonia.scm.SCMContextProvider;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith({MockitoExtension.class})
|
||||
class RepositoryLocationResolverTest {
|
||||
|
||||
@Mock
|
||||
private SCMContextProvider contextProvider;
|
||||
|
||||
@Mock
|
||||
private PathBasedRepositoryDAO pathBasedRepositoryDAO;
|
||||
|
||||
@Mock
|
||||
private RepositoryDAO repositoryDAO;
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnPathFromDao() {
|
||||
Path repositoryPath = Paths.get("repos", "42");
|
||||
when(pathBasedRepositoryDAO.getPath("42")).thenReturn(repositoryPath);
|
||||
|
||||
RepositoryLocationResolver resolver = createResolver(pathBasedRepositoryDAO);
|
||||
Path path = resolver.getPath("42");
|
||||
|
||||
assertThat(path).isSameAs(repositoryPath);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnInitialPathIfDaoIsNotPathBased() {
|
||||
Path repositoryPath = Paths.get("r", "42");
|
||||
when(initialRepositoryLocationResolver.getPath("42")).thenReturn(repositoryPath);
|
||||
|
||||
RepositoryLocationResolver resolver = createResolver(repositoryDAO);
|
||||
Path path = resolver.getPath("42");
|
||||
|
||||
assertThat(path).isSameAs(repositoryPath);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -89,7 +89,7 @@ public class IndentXMLStreamWriterTest
|
||||
StringBuilder buffer = new StringBuilder("<?xml version=\"1.0\" ?>");
|
||||
buffer.append(IndentXMLStreamWriter.LINE_SEPARATOR);
|
||||
buffer.append("<root>").append(IndentXMLStreamWriter.LINE_SEPARATOR);
|
||||
buffer.append(" <message>Hello</message>");
|
||||
buffer.append(" <message>Hello</message>");
|
||||
buffer.append(IndentXMLStreamWriter.LINE_SEPARATOR);
|
||||
buffer.append("</root>").append(IndentXMLStreamWriter.LINE_SEPARATOR);
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package sonia.scm.repository.xml;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.ContextEntry;
|
||||
import sonia.scm.repository.InternalRepositoryException;
|
||||
import sonia.scm.repository.Repository;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
import java.nio.file.Path;
|
||||
|
||||
class MetadataStore {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MetadataStore.class);
|
||||
|
||||
private final JAXBContext jaxbContext;
|
||||
|
||||
MetadataStore() {
|
||||
try {
|
||||
jaxbContext = JAXBContext.newInstance(Repository.class);
|
||||
} catch (JAXBException ex) {
|
||||
throw new IllegalStateException("failed to create jaxb context for repository", ex);
|
||||
}
|
||||
}
|
||||
|
||||
Repository read(Path path) {
|
||||
LOG.trace("read repository metadata from {}", path);
|
||||
try {
|
||||
return (Repository) jaxbContext.createUnmarshaller().unmarshal(path.toFile());
|
||||
} catch (JAXBException ex) {
|
||||
throw new InternalRepositoryException(
|
||||
ContextEntry.ContextBuilder.entity(Path.class, path.toString()).build(), "failed read repository metadata", ex
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void write(Path path, Repository repository) {
|
||||
LOG.trace("write repository metadata of {} to {}", repository.getNamespaceAndName(), path);
|
||||
try {
|
||||
Marshaller marshaller = jaxbContext.createMarshaller();
|
||||
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
|
||||
marshaller.marshal(repository, path.toFile());
|
||||
} catch (JAXBException ex) {
|
||||
throw new InternalRepositoryException(repository, "failed write repository metadata", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package sonia.scm.repository.xml;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.ContextEntry;
|
||||
import sonia.scm.repository.InternalRepositoryException;
|
||||
import sonia.scm.xml.IndentXMLStreamWriter;
|
||||
import sonia.scm.xml.XmlStreams;
|
||||
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamReader;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Map;
|
||||
|
||||
class PathDatabase {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PathDatabase.class);
|
||||
|
||||
private static final String ENCODING = "UTF-8";
|
||||
private static final String VERSION = "1.0";
|
||||
|
||||
private static final String ELEMENT_REPOSITORIES = "repositories";
|
||||
private static final String ATTRIBUTE_CREATION_TIME = "creation-time";
|
||||
private static final String ATTRIBUTE_LAST_MODIFIED = "last-modified";
|
||||
|
||||
private static final String ELEMENT_REPOSITORY = "repository";
|
||||
private static final String ATTRIBUTE_ID = "id";
|
||||
|
||||
private final Path storePath;
|
||||
|
||||
PathDatabase(Path storePath){
|
||||
this.storePath = storePath;
|
||||
}
|
||||
|
||||
void write(Long creationTime, Long lastModified, Map<String, Path> pathDatabase) {
|
||||
ensureParentDirectoryExists();
|
||||
LOG.trace("write repository path database to {}", storePath);
|
||||
|
||||
try (IndentXMLStreamWriter writer = XmlStreams.createWriter(storePath)) {
|
||||
writer.writeStartDocument(ENCODING, VERSION);
|
||||
|
||||
writeRepositoriesStart(writer, creationTime, lastModified);
|
||||
for (Map.Entry<String, Path> e : pathDatabase.entrySet()) {
|
||||
writeRepository(writer, e.getKey(), e.getValue());
|
||||
}
|
||||
writer.writeEndElement();
|
||||
|
||||
writer.writeEndDocument();
|
||||
} catch (XMLStreamException | IOException ex) {
|
||||
throw new InternalRepositoryException(
|
||||
ContextEntry.ContextBuilder.entity(Path.class, storePath.toString()).build(),
|
||||
"failed to write repository path database",
|
||||
ex
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureParentDirectoryExists() {
|
||||
Path parent = storePath.getParent();
|
||||
if (!Files.exists(parent)) {
|
||||
try {
|
||||
Files.createDirectories(parent);
|
||||
} catch (IOException ex) {
|
||||
throw new InternalRepositoryException(
|
||||
ContextEntry.ContextBuilder.entity(Path.class, parent.toString()).build(),
|
||||
"failed to create parent directory",
|
||||
ex
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeRepositoriesStart(XMLStreamWriter writer, Long creationTime, Long lastModified) throws XMLStreamException {
|
||||
writer.writeStartElement(ELEMENT_REPOSITORIES);
|
||||
writer.writeAttribute(ATTRIBUTE_CREATION_TIME, String.valueOf(creationTime));
|
||||
writer.writeAttribute(ATTRIBUTE_LAST_MODIFIED, String.valueOf(lastModified));
|
||||
}
|
||||
|
||||
private void writeRepository(XMLStreamWriter writer, String id, Path value) throws XMLStreamException {
|
||||
writer.writeStartElement(ELEMENT_REPOSITORY);
|
||||
writer.writeAttribute(ATTRIBUTE_ID, id);
|
||||
writer.writeCharacters(value.toString());
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
||||
void read(OnRepositories onRepositories, OnRepository onRepository) {
|
||||
LOG.trace("read repository path database from {}", storePath);
|
||||
XMLStreamReader reader = null;
|
||||
try {
|
||||
reader = XmlStreams.createReader(storePath);
|
||||
|
||||
while (reader.hasNext()) {
|
||||
int eventType = reader.next();
|
||||
|
||||
if (eventType == XMLStreamReader.START_ELEMENT) {
|
||||
String element = reader.getLocalName();
|
||||
if (ELEMENT_REPOSITORIES.equals(element)) {
|
||||
readRepositories(reader, onRepositories);
|
||||
} else if (ELEMENT_REPOSITORY.equals(element)) {
|
||||
readRepository(reader, onRepository);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (XMLStreamException | IOException ex) {
|
||||
throw new InternalRepositoryException(
|
||||
ContextEntry.ContextBuilder.entity(Path.class, storePath.toString()).build(),
|
||||
"failed to read repository path database",
|
||||
ex
|
||||
);
|
||||
} finally {
|
||||
XmlStreams.close(reader);
|
||||
}
|
||||
}
|
||||
|
||||
private void readRepository(XMLStreamReader reader, OnRepository onRepository) throws XMLStreamException {
|
||||
String id = reader.getAttributeValue(null, ATTRIBUTE_ID);
|
||||
Path path = Paths.get(reader.getElementText());
|
||||
onRepository.handle(id, path);
|
||||
}
|
||||
|
||||
private void readRepositories(XMLStreamReader reader, OnRepositories onRepositories) {
|
||||
String creationTime = reader.getAttributeValue(null, ATTRIBUTE_CREATION_TIME);
|
||||
String lastModified = reader.getAttributeValue(null, ATTRIBUTE_LAST_MODIFIED);
|
||||
onRepositories.handle(Long.parseLong(creationTime), Long.parseLong(lastModified));
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface OnRepositories {
|
||||
|
||||
void handle(Long creationTime, Long lastModified);
|
||||
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface OnRepository {
|
||||
|
||||
void handle(String id, Path path);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -33,156 +33,229 @@ package sonia.scm.repository.xml;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.inject.Singleton;
|
||||
import sonia.scm.ContextEntry;
|
||||
import sonia.scm.SCMContextProvider;
|
||||
import sonia.scm.io.FileSystem;
|
||||
import sonia.scm.repository.InitialRepositoryLocationResolver;
|
||||
import sonia.scm.repository.InitialRepositoryLocationResolver.InitialRepositoryLocation;
|
||||
import sonia.scm.repository.InternalRepositoryException;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.PathBasedRepositoryDAO;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.store.ConfigurationStoreFactory;
|
||||
import sonia.scm.xml.AbstractXmlDAO;
|
||||
import sonia.scm.store.StoreConstants;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Clock;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
@Singleton
|
||||
public class XmlRepositoryDAO
|
||||
extends AbstractXmlDAO<Repository, XmlRepositoryDatabase>
|
||||
implements PathBasedRepositoryDAO {
|
||||
public class XmlRepositoryDAO implements PathBasedRepositoryDAO {
|
||||
|
||||
public static final String STORE_NAME = "repositories";
|
||||
private static final String STORE_NAME = "repositories";
|
||||
|
||||
private final PathDatabase pathDatabase;
|
||||
private final MetadataStore metadataStore = new MetadataStore();
|
||||
|
||||
private InitialRepositoryLocationResolver initialRepositoryLocationResolver;
|
||||
private final FileSystem fileSystem;
|
||||
private final SCMContextProvider context;
|
||||
private final InitialRepositoryLocationResolver locationResolver;
|
||||
private final FileSystem fileSystem;
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
@VisibleForTesting
|
||||
Clock clock = Clock.systemUTC();
|
||||
|
||||
private Long creationTime;
|
||||
private Long lastModified;
|
||||
|
||||
private Map<String, Path> pathById;
|
||||
private Map<String, Repository> byId;
|
||||
private Map<NamespaceAndName, Repository> byNamespaceAndName;
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
* @param storeFactory
|
||||
* @param fileSystem
|
||||
* @param context
|
||||
*/
|
||||
@Inject
|
||||
public XmlRepositoryDAO(ConfigurationStoreFactory storeFactory, InitialRepositoryLocationResolver initialRepositoryLocationResolver, FileSystem fileSystem, SCMContextProvider context) {
|
||||
super(storeFactory.getStore(XmlRepositoryDatabase.class, STORE_NAME));
|
||||
this.initialRepositoryLocationResolver = initialRepositoryLocationResolver;
|
||||
this.fileSystem = fileSystem;
|
||||
public XmlRepositoryDAO(SCMContextProvider context, InitialRepositoryLocationResolver locationResolver, FileSystem fileSystem) {
|
||||
this.context = context;
|
||||
this.locationResolver = locationResolver;
|
||||
this.fileSystem = fileSystem;
|
||||
|
||||
this.creationTime = clock.millis();
|
||||
|
||||
this.pathById = new LinkedHashMap<>();
|
||||
this.byId = new LinkedHashMap<>();
|
||||
this.byNamespaceAndName = new LinkedHashMap<>();
|
||||
|
||||
pathDatabase = new PathDatabase(createStorePath());
|
||||
read();
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
private void read() {
|
||||
Path storePath = createStorePath();
|
||||
|
||||
@Override
|
||||
public boolean contains(NamespaceAndName namespaceAndName) {
|
||||
return db.contains(namespaceAndName);
|
||||
if (!Files.exists(storePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
pathDatabase.read(this::loadDates, this::loadRepository);
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public Repository get(NamespaceAndName namespaceAndName) {
|
||||
return db.get(namespaceAndName);
|
||||
private void loadDates(Long creationTime, Long lastModified) {
|
||||
this.creationTime = creationTime;
|
||||
this.lastModified = lastModified;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
private void loadRepository(String id, Path repositoryPath) {
|
||||
Path metadataPath = createMetadataPath(context.resolve(repositoryPath));
|
||||
|
||||
Repository repository = metadataStore.read(metadataPath);
|
||||
|
||||
byId.put(id, repository);
|
||||
byNamespaceAndName.put(repository.getNamespaceAndName(), repository);
|
||||
pathById.put(id, repositoryPath);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
Path createStorePath() {
|
||||
return context.getBaseDirectory()
|
||||
.toPath()
|
||||
.resolve(StoreConstants.CONFIG_DIRECTORY_NAME)
|
||||
.resolve(STORE_NAME.concat(StoreConstants.FILE_EXTENSION));
|
||||
}
|
||||
|
||||
|
||||
@VisibleForTesting
|
||||
Path createMetadataPath(Path repositoryPath) {
|
||||
return repositoryPath.resolve(StoreConstants.REPOSITORY_METADATA.concat(StoreConstants.FILE_EXTENSION));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modify(Repository repository) {
|
||||
RepositoryPath repositoryPath = findExistingRepositoryPath(repository.getId()).orElseThrow(() -> new InternalRepositoryException(repository, "path object for repository not found"));
|
||||
repositoryPath.setRepository(repository);
|
||||
repositoryPath.setToBeSynchronized(true);
|
||||
storeDB();
|
||||
public String getType() {
|
||||
return "xml";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getCreationTime() {
|
||||
return creationTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getLastModified() {
|
||||
return lastModified;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(Repository repository) {
|
||||
InitialRepositoryLocation initialLocation = initialRepositoryLocationResolver.getRelativeRepositoryPath(repository.getId());
|
||||
Repository clone = repository.clone();
|
||||
|
||||
Path repositoryPath = locationResolver.getPath(repository.getId());
|
||||
Path resolvedPath = context.resolve(repositoryPath);
|
||||
|
||||
try {
|
||||
fileSystem.create(initialLocation.getAbsolutePath());
|
||||
fileSystem.create(resolvedPath.toFile());
|
||||
|
||||
Path metadataPath = createMetadataPath(resolvedPath);
|
||||
metadataStore.write(metadataPath, repository);
|
||||
|
||||
synchronized (this) {
|
||||
pathById.put(repository.getId(), repositoryPath);
|
||||
|
||||
byId.put(repository.getId(), clone);
|
||||
byNamespaceAndName.put(repository.getNamespaceAndName(), clone);
|
||||
|
||||
writePathDatabase();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new InternalRepositoryException(repository, "could not create directory for repository data: " + initialLocation.getAbsolutePath(), e);
|
||||
}
|
||||
RepositoryPath repositoryPath = new RepositoryPath(initialLocation.getRelativePath(), repository.getId(), repository.clone());
|
||||
repositoryPath.setToBeSynchronized(true);
|
||||
synchronized (store) {
|
||||
db.add(repositoryPath);
|
||||
storeDB();
|
||||
throw new InternalRepositoryException(repository, "failed to create filesystem", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void writePathDatabase() {
|
||||
lastModified = clock.millis();
|
||||
pathDatabase.write(creationTime, lastModified, pathById);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Repository repository) {
|
||||
return byId.containsKey(repository.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(NamespaceAndName namespaceAndName) {
|
||||
return byNamespaceAndName.containsKey(namespaceAndName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String id) {
|
||||
return byId.containsKey(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Repository get(NamespaceAndName namespaceAndName) {
|
||||
return byNamespaceAndName.get(namespaceAndName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Repository get(String id) {
|
||||
RepositoryPath repositoryPath = db.get(id);
|
||||
if (repositoryPath != null) {
|
||||
return repositoryPath.getRepository();
|
||||
}
|
||||
return null;
|
||||
return byId.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Repository> getAll() {
|
||||
return db.getRepositories();
|
||||
return ImmutableList.copyOf(byNamespaceAndName.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
* @param repository
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected Repository clone(Repository repository) {
|
||||
return repository.clone();
|
||||
public void modify(Repository repository) {
|
||||
Repository clone = repository.clone();
|
||||
|
||||
synchronized (this) {
|
||||
// remove old namespaceAndName from map, in case of rename
|
||||
Repository prev = byId.put(clone.getId(), clone);
|
||||
if (prev != null) {
|
||||
byNamespaceAndName.remove(prev.getNamespaceAndName());
|
||||
}
|
||||
byNamespaceAndName.put(clone.getNamespaceAndName(), clone);
|
||||
|
||||
writePathDatabase();
|
||||
}
|
||||
|
||||
Path repositoryPath = context.resolve(getPath(repository.getId()));
|
||||
Path metadataPath = createMetadataPath(repositoryPath);
|
||||
metadataStore.write(metadataPath, clone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Repository repository) {
|
||||
Path directory = getPath(repository.getId());
|
||||
super.delete(repository);
|
||||
try {
|
||||
fileSystem.destroy(directory.toFile());
|
||||
} catch (IOException e) {
|
||||
throw new InternalRepositoryException(repository, "could not delete repository directory", e);
|
||||
}
|
||||
}
|
||||
Path path;
|
||||
synchronized (this) {
|
||||
Repository prev = byId.remove(repository.getId());
|
||||
if (prev != null) {
|
||||
byNamespaceAndName.remove(prev.getNamespaceAndName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected XmlRepositoryDatabase createNewDatabase() {
|
||||
return new XmlRepositoryDatabase();
|
||||
path = pathById.remove(repository.getId());
|
||||
|
||||
writePathDatabase();
|
||||
}
|
||||
|
||||
path = context.resolve(path);
|
||||
|
||||
try {
|
||||
fileSystem.destroy(path.toFile());
|
||||
} catch (IOException e) {
|
||||
throw new InternalRepositoryException(repository, "failed to destroy filesystem", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getPath(String repositoryId) {
|
||||
return context
|
||||
.getBaseDirectory()
|
||||
.toPath()
|
||||
.resolve(
|
||||
findExistingRepositoryPath(repositoryId)
|
||||
.map(RepositoryPath::getPath)
|
||||
.orElseThrow(() -> new InternalRepositoryException(ContextEntry.ContextBuilder.entity("repository", repositoryId), "could not find base directory for repository")));
|
||||
}
|
||||
|
||||
private Optional<RepositoryPath> findExistingRepositoryPath(String repositoryId) {
|
||||
return db.values().stream()
|
||||
.filter(repoPath -> repoPath.getId().equals(repositoryId))
|
||||
.findAny();
|
||||
return pathById.get(repositoryId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,32 +35,14 @@ package sonia.scm.store;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.security.KeyGenerator;
|
||||
import sonia.scm.xml.IndentXMLStreamWriter;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import sonia.scm.xml.XmlStreams;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBElement;
|
||||
@@ -68,11 +50,14 @@ import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.stream.XMLInputFactory;
|
||||
import javax.xml.stream.XMLOutputFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamReader;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -255,74 +240,6 @@ public class JAXBConfigurationEntryStore<V> implements ConfigurationEntryStore<V
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param writer
|
||||
*/
|
||||
private void close(XMLStreamWriter writer)
|
||||
{
|
||||
if (writer != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
writer.close();
|
||||
}
|
||||
catch (XMLStreamException ex)
|
||||
{
|
||||
logger.error("could not close writer", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param reader
|
||||
*/
|
||||
private void close(XMLStreamReader reader)
|
||||
{
|
||||
if (reader != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
reader.close();
|
||||
}
|
||||
catch (XMLStreamException ex)
|
||||
{
|
||||
logger.error("could not close reader", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
private Reader createReader() throws FileNotFoundException
|
||||
{
|
||||
return new InputStreamReader(new FileInputStream(file), Charsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
private Writer createWriter() throws FileNotFoundException
|
||||
{
|
||||
return new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
@@ -333,15 +250,13 @@ public class JAXBConfigurationEntryStore<V> implements ConfigurationEntryStore<V
|
||||
{
|
||||
logger.debug("load configuration from {}", file);
|
||||
|
||||
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
|
||||
|
||||
XMLStreamReader reader = null;
|
||||
|
||||
try
|
||||
{
|
||||
Unmarshaller u = context.createUnmarshaller();
|
||||
|
||||
reader = xmlInputFactory.createXMLStreamReader(createReader());
|
||||
reader = XmlStreams.createReader(file);
|
||||
|
||||
// configuration
|
||||
reader.nextTag();
|
||||
@@ -390,7 +305,7 @@ public class JAXBConfigurationEntryStore<V> implements ConfigurationEntryStore<V
|
||||
}
|
||||
finally
|
||||
{
|
||||
close(reader);
|
||||
XmlStreams.close(reader);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,17 +317,8 @@ public class JAXBConfigurationEntryStore<V> implements ConfigurationEntryStore<V
|
||||
{
|
||||
logger.debug("store configuration to {}", file);
|
||||
|
||||
IndentXMLStreamWriter writer = null;
|
||||
|
||||
try
|
||||
try (IndentXMLStreamWriter writer = XmlStreams.createWriter(file))
|
||||
{
|
||||
//J-
|
||||
writer = new IndentXMLStreamWriter(
|
||||
XMLOutputFactory.newInstance().createXMLStreamWriter(
|
||||
createWriter()
|
||||
)
|
||||
);
|
||||
//J+
|
||||
writer.writeStartDocument();
|
||||
|
||||
// configuration start
|
||||
@@ -453,10 +359,6 @@ public class JAXBConfigurationEntryStore<V> implements ConfigurationEntryStore<V
|
||||
{
|
||||
throw new StoreException("could not store configuration", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
close(writer);
|
||||
}
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
71
scm-dao-xml/src/main/java/sonia/scm/xml/XmlStreams.java
Normal file
71
scm-dao-xml/src/main/java/sonia/scm/xml/XmlStreams.java
Normal file
@@ -0,0 +1,71 @@
|
||||
package sonia.scm.xml;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.xml.stream.XMLInputFactory;
|
||||
import javax.xml.stream.XMLOutputFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamReader;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public final class XmlStreams {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(XmlStreams.class);
|
||||
|
||||
private XmlStreams() {
|
||||
}
|
||||
|
||||
public static void close(XMLStreamWriter writer) {
|
||||
if (writer != null) {
|
||||
try {
|
||||
writer.close();
|
||||
} catch (XMLStreamException ex) {
|
||||
LOG.error("could not close writer", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void close(XMLStreamReader reader) {
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (XMLStreamException ex) {
|
||||
LOG.error("could not close reader", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static XMLStreamReader createReader(Path path) throws IOException, XMLStreamException {
|
||||
return createReader(Files.newBufferedReader(path, Charsets.UTF_8));
|
||||
}
|
||||
|
||||
public static XMLStreamReader createReader(File file) throws IOException, XMLStreamException {
|
||||
return createReader(file.toPath());
|
||||
}
|
||||
|
||||
private static XMLStreamReader createReader(Reader reader) throws XMLStreamException {
|
||||
return XMLInputFactory.newInstance().createXMLStreamReader(reader);
|
||||
}
|
||||
|
||||
|
||||
public static IndentXMLStreamWriter createWriter(Path path) throws IOException, XMLStreamException {
|
||||
return createWriter(Files.newBufferedWriter(path, Charsets.UTF_8));
|
||||
}
|
||||
|
||||
public static IndentXMLStreamWriter createWriter(File file) throws IOException, XMLStreamException {
|
||||
return createWriter(file.toPath());
|
||||
}
|
||||
|
||||
private static IndentXMLStreamWriter createWriter(Writer writer) throws XMLStreamException {
|
||||
return new IndentXMLStreamWriter(XMLOutputFactory.newFactory().createXMLStreamWriter(writer));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,109 +1,363 @@
|
||||
package sonia.scm.repository.xml;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junitpioneer.jupiter.TempDirectory;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
import sonia.scm.SCMContextProvider;
|
||||
import sonia.scm.io.DefaultFileSystem;
|
||||
import sonia.scm.io.FileSystem;
|
||||
import sonia.scm.repository.InitialRepositoryLocationResolver;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.store.ConfigurationStore;
|
||||
import sonia.scm.store.ConfigurationStoreFactory;
|
||||
import sonia.scm.repository.RepositoryTestData;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Clock;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.codehaus.groovy.runtime.InvokerHelper.asList;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static sonia.scm.repository.xml.XmlRepositoryDAO.STORE_NAME;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class XmlRepositoryDAOTest {
|
||||
@ExtendWith({MockitoExtension.class, TempDirectory.class})
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
class XmlRepositoryDAOTest {
|
||||
|
||||
@Mock
|
||||
private ConfigurationStoreFactory storeFactory;
|
||||
@Mock
|
||||
private ConfigurationStore<XmlRepositoryDatabase> store;
|
||||
@Mock
|
||||
private XmlRepositoryDatabase db;
|
||||
@Mock
|
||||
private SCMContextProvider context;
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
@Mock
|
||||
private InitialRepositoryLocationResolver locationResolver;
|
||||
|
||||
private final FileSystem fileSystem = new DefaultFileSystem();
|
||||
private FileSystem fileSystem = new DefaultFileSystem();
|
||||
|
||||
@Before
|
||||
public void init() throws IOException {
|
||||
when(storeFactory.getStore(XmlRepositoryDatabase.class, STORE_NAME)).thenReturn(store);
|
||||
when(store.get()).thenReturn(db);
|
||||
when(context.getBaseDirectory()).thenReturn(temporaryFolder.newFolder());
|
||||
private XmlRepositoryDAO dao;
|
||||
|
||||
private Path baseDirectory;
|
||||
|
||||
private AtomicLong atomicClock;
|
||||
|
||||
@BeforeEach
|
||||
void createDAO(@TempDirectory.TempDir Path baseDirectory) {
|
||||
this.baseDirectory = baseDirectory;
|
||||
this.atomicClock = new AtomicLong();
|
||||
|
||||
when(locationResolver.getPath("42")).thenReturn(Paths.get("repos", "42"));
|
||||
when(locationResolver.getPath("42+1")).thenReturn(Paths.get("repos", "puzzle"));
|
||||
|
||||
when(context.getBaseDirectory()).thenReturn(baseDirectory.toFile());
|
||||
when(context.resolve(any(Path.class))).then(ic -> {
|
||||
Path path = ic.getArgument(0);
|
||||
return baseDirectory.resolve(path);
|
||||
});
|
||||
|
||||
dao = createDAO();
|
||||
}
|
||||
|
||||
private XmlRepositoryDAO createDAO() {
|
||||
XmlRepositoryDAO dao = new XmlRepositoryDAO(context, locationResolver, fileSystem);
|
||||
|
||||
Clock clock = mock(Clock.class);
|
||||
when(clock.millis()).then(ic -> atomicClock.incrementAndGet());
|
||||
dao.clock = clock;
|
||||
|
||||
return dao;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addShouldCreateNewRepositoryPathWithRelativePath() {
|
||||
InitialRepositoryLocationResolver initialRepositoryLocationResolver = new InitialRepositoryLocationResolver(context);
|
||||
XmlRepositoryDAO dao = new XmlRepositoryDAO(storeFactory, initialRepositoryLocationResolver, fileSystem, context);
|
||||
|
||||
dao.add(new Repository("id", null, null, null));
|
||||
|
||||
verify(db).add(argThat(repositoryPath -> {
|
||||
assertThat(repositoryPath.getId()).isEqualTo("id");
|
||||
assertThat(repositoryPath.getPath()).isEqualTo(InitialRepositoryLocationResolver.DEFAULT_REPOSITORY_PATH + "/id");
|
||||
return true;
|
||||
}));
|
||||
verify(store).set(db);
|
||||
void shouldReturnXmlType() {
|
||||
assertThat(dao.getType()).isEqualTo("xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void modifyShouldStoreChangedRepository() {
|
||||
Repository oldRepository = new Repository("id", "old", null, null);
|
||||
RepositoryPath repositoryPath = new RepositoryPath("/path", "id", oldRepository);
|
||||
when(db.values()).thenReturn(asList(repositoryPath));
|
||||
|
||||
XmlRepositoryDAO dao = new XmlRepositoryDAO(storeFactory, new InitialRepositoryLocationResolver(context), fileSystem, context);
|
||||
|
||||
Repository newRepository = new Repository("id", "new", null, null);
|
||||
dao.modify(newRepository);
|
||||
|
||||
assertThat(repositoryPath.getRepository()).isSameAs(newRepository);
|
||||
verify(store).set(db);
|
||||
void shouldReturnCreationTimeAfterCreation() {
|
||||
long now = System.currentTimeMillis();
|
||||
assertThat(dao.getCreationTime()).isBetween(now - 200, now + 200);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetPathInBaseDirectoryForRelativePath() {
|
||||
Repository existingRepository = new Repository("id", "old", null, null);
|
||||
RepositoryPath repositoryPath = new RepositoryPath("path", "id", existingRepository);
|
||||
when(db.values()).thenReturn(asList(repositoryPath));
|
||||
|
||||
XmlRepositoryDAO dao = new XmlRepositoryDAO(storeFactory, new InitialRepositoryLocationResolver(context), fileSystem, context);
|
||||
|
||||
Path path = dao.getPath(existingRepository.getId());
|
||||
|
||||
assertThat(path.toString()).isEqualTo(context.getBaseDirectory().getPath() + "/path");
|
||||
void shouldNotReturnLastModifiedAfterCreation() {
|
||||
assertThat(dao.getLastModified()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetPathInBaseDirectoryForAbsolutePath() {
|
||||
Repository existingRepository = new Repository("id", "old", null, null);
|
||||
RepositoryPath repositoryPath = new RepositoryPath("/tmp/path", "id", existingRepository);
|
||||
when(db.values()).thenReturn(asList(repositoryPath));
|
||||
void shouldReturnTrueForEachContainsMethod() {
|
||||
Repository heartOfGold = createHeartOfGold();
|
||||
dao.add(heartOfGold);
|
||||
|
||||
XmlRepositoryDAO dao = new XmlRepositoryDAO(storeFactory, new InitialRepositoryLocationResolver(context), fileSystem, context);
|
||||
assertThat(dao.contains(heartOfGold)).isTrue();
|
||||
assertThat(dao.contains(heartOfGold.getId())).isTrue();
|
||||
assertThat(dao.contains(heartOfGold.getNamespaceAndName())).isTrue();
|
||||
}
|
||||
|
||||
Path path = dao.getPath(existingRepository.getId());
|
||||
private Repository createHeartOfGold() {
|
||||
Repository heartOfGold = RepositoryTestData.createHeartOfGold();
|
||||
heartOfGold.setId("42");
|
||||
return heartOfGold;
|
||||
}
|
||||
|
||||
assertThat(path.toString()).isEqualTo("/tmp/path");
|
||||
@Test
|
||||
void shouldReturnFalseForEachContainsMethod() {
|
||||
Repository heartOfGold = createHeartOfGold();
|
||||
|
||||
assertThat(dao.contains(heartOfGold)).isFalse();
|
||||
assertThat(dao.contains(heartOfGold.getId())).isFalse();
|
||||
assertThat(dao.contains(heartOfGold.getNamespaceAndName())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnNullForEachGetMethod() {
|
||||
assertThat(dao.get("42")).isNull();
|
||||
assertThat(dao.get(new NamespaceAndName("hitchhiker","HeartOfGold"))).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnRepository() {
|
||||
Repository heartOfGold = createHeartOfGold();
|
||||
dao.add(heartOfGold);
|
||||
|
||||
assertThat(dao.get("42")).isEqualTo(heartOfGold);
|
||||
assertThat(dao.get(new NamespaceAndName("hitchhiker","HeartOfGold"))).isEqualTo(heartOfGold);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotReturnTheSameInstance() {
|
||||
Repository heartOfGold = createHeartOfGold();
|
||||
dao.add(heartOfGold);
|
||||
|
||||
Repository repository = dao.get("42");
|
||||
assertThat(repository).isNotSameAs(heartOfGold);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnAllRepositories() {
|
||||
Repository heartOfGold = createHeartOfGold();
|
||||
dao.add(heartOfGold);
|
||||
|
||||
Repository puzzle = createPuzzle();
|
||||
dao.add(puzzle);
|
||||
|
||||
Collection<Repository> repositories = dao.getAll();
|
||||
assertThat(repositories).containsExactlyInAnyOrder(heartOfGold, puzzle);
|
||||
}
|
||||
|
||||
private Repository createPuzzle() {
|
||||
Repository puzzle = RepositoryTestData.create42Puzzle();
|
||||
puzzle.setId("42+1");
|
||||
return puzzle;
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldModifyRepository() {
|
||||
Repository heartOfGold = createHeartOfGold();
|
||||
heartOfGold.setDescription("HeartOfGold");
|
||||
dao.add(heartOfGold);
|
||||
assertThat(dao.get("42").getDescription()).isEqualTo("HeartOfGold");
|
||||
|
||||
heartOfGold = createHeartOfGold();
|
||||
heartOfGold.setDescription("Heart of Gold");
|
||||
dao.modify(heartOfGold);
|
||||
|
||||
assertThat(dao.get("42").getDescription()).isEqualTo("Heart of Gold");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRemoveRepository() {
|
||||
Repository heartOfGold = createHeartOfGold();
|
||||
dao.add(heartOfGold);
|
||||
assertThat(dao.contains("42")).isTrue();
|
||||
|
||||
dao.delete(heartOfGold);
|
||||
assertThat(dao.contains("42")).isFalse();
|
||||
assertThat(dao.contains(new NamespaceAndName("hitchhiker", "HeartOfGold"))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUpdateLastModifiedAfterEachWriteOperation() {
|
||||
Repository heartOfGold = createHeartOfGold();
|
||||
dao.add(heartOfGold);
|
||||
|
||||
Long firstLastModified = dao.getLastModified();
|
||||
assertThat(firstLastModified).isNotNull();
|
||||
|
||||
Repository puzzle = createPuzzle();
|
||||
dao.add(puzzle);
|
||||
|
||||
Long lastModifiedAdded = dao.getLastModified();
|
||||
assertThat(lastModifiedAdded).isGreaterThan(firstLastModified);
|
||||
|
||||
heartOfGold.setDescription("Heart of Gold");
|
||||
dao.modify(heartOfGold);
|
||||
|
||||
Long lastModifiedModified = dao.getLastModified();
|
||||
assertThat(lastModifiedModified).isGreaterThan(lastModifiedAdded);
|
||||
|
||||
dao.delete(puzzle);
|
||||
|
||||
Long lastModifiedRemoved = dao.getLastModified();
|
||||
assertThat(lastModifiedRemoved).isGreaterThan(lastModifiedModified);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRenameTheRepository() {
|
||||
Repository heartOfGold = createHeartOfGold();
|
||||
dao.add(heartOfGold);
|
||||
|
||||
heartOfGold.setNamespace("hg2tg");
|
||||
heartOfGold.setName("hog");
|
||||
|
||||
dao.modify(heartOfGold);
|
||||
|
||||
Repository repository = dao.get("42");
|
||||
assertThat(repository.getNamespace()).isEqualTo("hg2tg");
|
||||
assertThat(repository.getName()).isEqualTo("hog");
|
||||
|
||||
assertThat(dao.contains(new NamespaceAndName("hg2tg", "hog"))).isTrue();
|
||||
assertThat(dao.contains(new NamespaceAndName("hitchhiker", "HeartOfGold"))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDeleteRepositoryEvenWithChangedNamespace() {
|
||||
Repository heartOfGold = createHeartOfGold();
|
||||
dao.add(heartOfGold);
|
||||
|
||||
heartOfGold.setNamespace("hg2tg");
|
||||
heartOfGold.setName("hog");
|
||||
|
||||
dao.delete(heartOfGold);
|
||||
|
||||
assertThat(dao.contains(new NamespaceAndName("hitchhiker", "HeartOfGold"))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnThePathForTheRepository() {
|
||||
Path repositoryPath = Paths.get("r", "42");
|
||||
when(locationResolver.getPath("42")).thenReturn(repositoryPath);
|
||||
|
||||
Repository heartOfGold = createHeartOfGold();
|
||||
dao.add(heartOfGold);
|
||||
|
||||
Path path = dao.getPath("42");
|
||||
assertThat(path).isEqualTo(repositoryPath);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateTheDirectoryForTheRepository() {
|
||||
Path repositoryPath = Paths.get("r", "42");
|
||||
when(locationResolver.getPath("42")).thenReturn(repositoryPath);
|
||||
|
||||
Repository heartOfGold = createHeartOfGold();
|
||||
dao.add(heartOfGold);
|
||||
|
||||
Path path = getAbsolutePathFromDao("42");
|
||||
assertThat(path).isDirectory();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRemoveRepositoryDirectoryAfterDeletion() {
|
||||
Repository heartOfGold = createHeartOfGold();
|
||||
dao.add(heartOfGold);
|
||||
|
||||
Path path = getAbsolutePathFromDao(heartOfGold.getId());
|
||||
assertThat(path).isDirectory();
|
||||
|
||||
dao.delete(heartOfGold);
|
||||
assertThat(path).doesNotExist();
|
||||
}
|
||||
|
||||
private Path getAbsolutePathFromDao(String id) {
|
||||
return context.resolve(dao.getPath(id));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateRepositoryPathDatabase() throws IOException {
|
||||
Repository heartOfGold = createHeartOfGold();
|
||||
dao.add(heartOfGold);
|
||||
|
||||
Path storePath = dao.createStorePath();
|
||||
assertThat(storePath).isRegularFile();
|
||||
|
||||
String content = content(storePath);
|
||||
|
||||
assertThat(content).contains(heartOfGold.getId());
|
||||
assertThat(content).contains(dao.getPath(heartOfGold.getId()).toString());
|
||||
}
|
||||
|
||||
private String content(Path storePath) throws IOException {
|
||||
return new String(Files.readAllBytes(storePath), Charsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldStoreRepositoryMetadataAfterAdd() throws IOException {
|
||||
Repository heartOfGold = createHeartOfGold();
|
||||
dao.add(heartOfGold);
|
||||
|
||||
Path repositoryDirectory = getAbsolutePathFromDao(heartOfGold.getId());
|
||||
Path metadataPath = dao.createMetadataPath(repositoryDirectory);
|
||||
|
||||
assertThat(metadataPath).isRegularFile();
|
||||
|
||||
String content = content(metadataPath);
|
||||
assertThat(content).contains(heartOfGold.getName());
|
||||
assertThat(content).contains(heartOfGold.getNamespace());
|
||||
assertThat(content).contains(heartOfGold.getDescription());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUpdateRepositoryMetadataAfterModify() throws IOException {
|
||||
Repository heartOfGold = createHeartOfGold();
|
||||
dao.add(heartOfGold);
|
||||
|
||||
heartOfGold.setDescription("Awesome Spaceship");
|
||||
dao.modify(heartOfGold);
|
||||
|
||||
Path repositoryDirectory = getAbsolutePathFromDao(heartOfGold.getId());
|
||||
Path metadataPath = dao.createMetadataPath(repositoryDirectory);
|
||||
|
||||
String content = content(metadataPath);
|
||||
assertThat(content).contains("Awesome Spaceship");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReadPathDatabaseAndMetadataOfRepositories() {
|
||||
Repository heartOfGold = createHeartOfGold();
|
||||
dao.add(heartOfGold);
|
||||
|
||||
// reload data
|
||||
dao = createDAO();
|
||||
|
||||
heartOfGold = dao.get("42");
|
||||
assertThat(heartOfGold.getName()).isEqualTo("HeartOfGold");
|
||||
|
||||
Path path = getAbsolutePathFromDao(heartOfGold.getId());
|
||||
assertThat(path).isDirectory();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReadCreationTimeAndLastModifedDateFromDatabase() {
|
||||
Repository heartOfGold = createHeartOfGold();
|
||||
dao.add(heartOfGold);
|
||||
|
||||
Long creationTime = dao.getCreationTime();
|
||||
Long lastModified = dao.getLastModified();
|
||||
|
||||
// reload data
|
||||
dao = createDAO();
|
||||
|
||||
assertThat(dao.getCreationTime()).isEqualTo(creationTime);
|
||||
assertThat(dao.getLastModified()).isEqualTo(lastModified);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,8 +62,6 @@ public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
||||
@Mock
|
||||
private GitWorkdirFactory gitWorkdirFactory;
|
||||
|
||||
RepositoryLocationResolver repositoryLocationResolver;
|
||||
|
||||
|
||||
@Override
|
||||
protected void checkDirectory(File directory) {
|
||||
@@ -86,10 +84,10 @@ public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
||||
|
||||
@Override
|
||||
protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory,
|
||||
RepositoryLocationResolver locationResolver,
|
||||
File directory) {
|
||||
repositoryLocationResolver = new RepositoryLocationResolver(repoDao, new InitialRepositoryLocationResolver(contextProvider));
|
||||
GitRepositoryHandler repositoryHandler = new GitRepositoryHandler(factory,
|
||||
scheduler, repositoryLocationResolver, gitWorkdirFactory);
|
||||
scheduler, locationResolver, gitWorkdirFactory);
|
||||
repositoryHandler.init(contextProvider);
|
||||
|
||||
GitConfig config = new GitConfig();
|
||||
@@ -103,7 +101,7 @@ public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
||||
@Test
|
||||
public void getDirectory() {
|
||||
GitRepositoryHandler repositoryHandler = new GitRepositoryHandler(factory,
|
||||
scheduler, repositoryLocationResolver, gitWorkdirFactory);
|
||||
scheduler, locationResolver, gitWorkdirFactory);
|
||||
GitConfig config = new GitConfig();
|
||||
config.setDisabled(false);
|
||||
config.setGcExpression("gc exp");
|
||||
|
||||
@@ -50,7 +50,7 @@ import static org.junit.Assert.assertTrue;
|
||||
/**
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
@RunWith(MockitoJUnitRunner.Silent.class)
|
||||
public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
||||
|
||||
@Mock
|
||||
@@ -59,8 +59,6 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
||||
@Mock
|
||||
private com.google.inject.Provider<HgContext> provider;
|
||||
|
||||
private RepositoryLocationResolver repositoryLocationResolver;
|
||||
|
||||
@Override
|
||||
protected void checkDirectory(File directory) {
|
||||
File hgDirectory = new File(directory, ".hg");
|
||||
@@ -70,11 +68,8 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory,
|
||||
File directory) {
|
||||
repositoryLocationResolver = new RepositoryLocationResolver(repoDao, new InitialRepositoryLocationResolver(contextProvider));
|
||||
HgRepositoryHandler handler = new HgRepositoryHandler(factory,
|
||||
new HgContextProvider(), repositoryLocationResolver);
|
||||
protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory, RepositoryLocationResolver locationResolver, File directory) {
|
||||
HgRepositoryHandler handler = new HgRepositoryHandler(factory, new HgContextProvider(), locationResolver);
|
||||
|
||||
handler.init(contextProvider);
|
||||
HgTestUtil.checkForSkip(handler);
|
||||
@@ -84,8 +79,7 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
||||
|
||||
@Test
|
||||
public void getDirectory() {
|
||||
HgRepositoryHandler repositoryHandler = new HgRepositoryHandler(factory,
|
||||
provider, repositoryLocationResolver);
|
||||
HgRepositoryHandler repositoryHandler = new HgRepositoryHandler(factory, provider, locationResolver);
|
||||
|
||||
HgConfig hgConfig = new HgConfig();
|
||||
hgConfig.setHgBinary("hg");
|
||||
|
||||
@@ -103,7 +103,7 @@ public final class HgTestUtil
|
||||
|
||||
PathBasedRepositoryDAO repoDao = mock(PathBasedRepositoryDAO.class);
|
||||
|
||||
RepositoryLocationResolver repositoryLocationResolver = new RepositoryLocationResolver(repoDao, new InitialRepositoryLocationResolver(context));
|
||||
RepositoryLocationResolver repositoryLocationResolver = new RepositoryLocationResolver(context, repoDao, new InitialRepositoryLocationResolver());
|
||||
HgRepositoryHandler handler =
|
||||
new HgRepositoryHandler(new InMemoryConfigurationStoreFactory(), new HgContextProvider(), repositoryLocationResolver);
|
||||
Path repoDir = directory.toPath();
|
||||
|
||||
@@ -41,6 +41,7 @@ import sonia.scm.Stage;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -136,6 +137,11 @@ public class TempSCMContextProvider implements SCMContextProvider
|
||||
this.baseDirectory = baseDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path resolve(Path path) {
|
||||
return baseDirectory.toPath().resolve(path);
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
|
||||
@@ -67,15 +67,10 @@ public class SvnRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
||||
@Mock
|
||||
private com.google.inject.Provider<RepositoryManager> repositoryManagerProvider;
|
||||
|
||||
@Mock
|
||||
private RepositoryDAO repositoryDAO;
|
||||
|
||||
private HookContextFactory hookContextFactory = new HookContextFactory(mock(PreProcessorUtil.class));
|
||||
|
||||
private HookEventFacade facade = new HookEventFacade(repositoryManagerProvider, hookContextFactory);
|
||||
|
||||
private RepositoryLocationResolver repositoryLocationResolver;
|
||||
|
||||
@Override
|
||||
protected void checkDirectory(File directory) {
|
||||
File format = new File(directory, "format");
|
||||
@@ -91,9 +86,9 @@ public class SvnRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
||||
|
||||
@Override
|
||||
protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory,
|
||||
RepositoryLocationResolver locationResolver,
|
||||
File directory) {
|
||||
repositoryLocationResolver = new RepositoryLocationResolver(repoDao, new InitialRepositoryLocationResolver(contextProvider));
|
||||
SvnRepositoryHandler handler = new SvnRepositoryHandler(factory, null, repositoryLocationResolver);
|
||||
SvnRepositoryHandler handler = new SvnRepositoryHandler(factory, null, locationResolver);
|
||||
|
||||
handler.init(contextProvider);
|
||||
|
||||
@@ -109,7 +104,7 @@ public class SvnRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
||||
public void getDirectory() {
|
||||
when(factory.getStore(any(), any())).thenReturn(store);
|
||||
SvnRepositoryHandler repositoryHandler = new SvnRepositoryHandler(factory,
|
||||
facade, repositoryLocationResolver);
|
||||
facade, locationResolver);
|
||||
|
||||
SvnConfig svnConfig = new SvnConfig();
|
||||
repositoryHandler.setConfig(svnConfig);
|
||||
|
||||
@@ -45,6 +45,7 @@ public final class RepositoryTestData {
|
||||
.type(type)
|
||||
.contact("douglas.adams@hitchhiker.com")
|
||||
.name("42Puzzle")
|
||||
.namespace("hitchhiker")
|
||||
.description("The 42 Puzzle")
|
||||
.build();
|
||||
}
|
||||
@@ -59,6 +60,7 @@ public final class RepositoryTestData {
|
||||
.type(type)
|
||||
.contact("zaphod.beeblebrox@hitchhiker.com")
|
||||
.name("happyVerticalPeopleTransporter")
|
||||
.namespace("hitchhiker")
|
||||
.description("Happy Vertical People Transporter")
|
||||
.build();
|
||||
}
|
||||
@@ -72,6 +74,7 @@ public final class RepositoryTestData {
|
||||
.type(type)
|
||||
.contact("zaphod.beeblebrox@hitchhiker.com")
|
||||
.name("HeartOfGold")
|
||||
.namespace("hitchhiker")
|
||||
.description(
|
||||
"Heart of Gold is the first prototype ship to successfully utilise the revolutionary Infinite Improbability Drive")
|
||||
.build();
|
||||
@@ -87,6 +90,7 @@ public final class RepositoryTestData {
|
||||
.type(type)
|
||||
.contact("douglas.adams@hitchhiker.com")
|
||||
.name("RestaurantAtTheEndOfTheUniverse")
|
||||
.namespace("hitchhiker")
|
||||
.description("The Restaurant at the End of the Universe")
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -63,7 +64,7 @@ public abstract class SimpleRepositoryHandlerTestBase extends AbstractTestBase {
|
||||
protected abstract void checkDirectory(File directory);
|
||||
|
||||
protected abstract RepositoryHandler createRepositoryHandler(
|
||||
ConfigurationStoreFactory factory, File directory) throws IOException, RepositoryPathNotFoundException;
|
||||
ConfigurationStoreFactory factory, RepositoryLocationResolver locationResolver, File directory) throws IOException, RepositoryPathNotFoundException;
|
||||
|
||||
@Test
|
||||
public void testCreate() {
|
||||
@@ -75,7 +76,15 @@ public abstract class SimpleRepositoryHandlerTestBase extends AbstractTestBase {
|
||||
InMemoryConfigurationStoreFactory storeFactory = new InMemoryConfigurationStoreFactory();
|
||||
baseDirectory = new File(contextProvider.getBaseDirectory(), "repositories");
|
||||
IOUtil.mkdirs(baseDirectory);
|
||||
handler = createRepositoryHandler(storeFactory, baseDirectory);
|
||||
|
||||
locationResolver = mock(RepositoryLocationResolver.class);
|
||||
|
||||
when(locationResolver.getPath(anyString())).then(ic -> {
|
||||
String id = ic.getArgument(0);
|
||||
return baseDirectory.toPath().resolve(id);
|
||||
});
|
||||
|
||||
handler = createRepositoryHandler(storeFactory, locationResolver, baseDirectory);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,6 +114,7 @@ public abstract class SimpleRepositoryHandlerTestBase extends AbstractTestBase {
|
||||
}
|
||||
|
||||
protected File baseDirectory;
|
||||
protected RepositoryLocationResolver locationResolver;
|
||||
|
||||
private RepositoryHandler handler;
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ import static org.mockito.Mockito.*;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@@ -213,6 +214,10 @@ public final class MockUtil
|
||||
SCMContextProvider provider = mock(SCMContextProvider.class);
|
||||
|
||||
when(provider.getBaseDirectory()).thenReturn(directory);
|
||||
when(provider.resolve(any(Path.class))).then(ic -> {
|
||||
Path p = ic.getArgument(0);
|
||||
return directory.toPath().resolve(p);
|
||||
});
|
||||
|
||||
return provider;
|
||||
}
|
||||
|
||||
@@ -434,9 +434,9 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
|
||||
DefaultFileSystem fileSystem = new DefaultFileSystem();
|
||||
Set<RepositoryHandler> handlerSet = new HashSet<>();
|
||||
ConfigurationStoreFactory factory = new JAXBConfigurationStoreFactory(contextProvider);
|
||||
InitialRepositoryLocationResolver initialRepositoryLocationResolver = new InitialRepositoryLocationResolver(contextProvider);
|
||||
XmlRepositoryDAO repositoryDAO = new XmlRepositoryDAO(factory, initialRepositoryLocationResolver, fileSystem, contextProvider);
|
||||
RepositoryLocationResolver repositoryLocationResolver = new RepositoryLocationResolver(repositoryDAO, initialRepositoryLocationResolver);
|
||||
InitialRepositoryLocationResolver initialRepositoryLocationResolver = new InitialRepositoryLocationResolver();
|
||||
XmlRepositoryDAO repositoryDAO = new XmlRepositoryDAO(contextProvider, initialRepositoryLocationResolver, fileSystem);
|
||||
RepositoryLocationResolver repositoryLocationResolver = new RepositoryLocationResolver(contextProvider, repositoryDAO, initialRepositoryLocationResolver);
|
||||
handlerSet.add(new DummyRepositoryHandler(factory, repositoryLocationResolver));
|
||||
handlerSet.add(new DummyRepositoryHandler(factory, repositoryLocationResolver) {
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user