mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-02 11:35:57 +01:00
merge
This commit is contained in:
@@ -108,7 +108,7 @@ data:
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
|
|
||||||
<--
|
<!--
|
||||||
in a container environment we only need stdout
|
in a container environment we only need stdout
|
||||||
-->
|
-->
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
|||||||
@@ -29,6 +29,17 @@ spec:
|
|||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: data
|
- name: data
|
||||||
mountPath: /data
|
mountPath: /data
|
||||||
|
{{- if .Values.plugins }}
|
||||||
|
- name: install-plugins
|
||||||
|
image: alpine:3.8
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
command: ['sh', '/scripts/install-plugins.sh']
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /data
|
||||||
|
- name: scripts
|
||||||
|
mountPath: /scripts
|
||||||
|
{{- end }}
|
||||||
containers:
|
containers:
|
||||||
- name: {{ .Chart.Name }}
|
- name: {{ .Chart.Name }}
|
||||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||||
@@ -63,6 +74,11 @@ spec:
|
|||||||
- name: config
|
- name: config
|
||||||
configMap:
|
configMap:
|
||||||
name: {{ include "scm-manager.fullname" . }}
|
name: {{ include "scm-manager.fullname" . }}
|
||||||
|
{{- if .Values.plugins }}
|
||||||
|
- name: scripts
|
||||||
|
configMap:
|
||||||
|
name: {{ include "scm-manager.fullname" . }}-scripts
|
||||||
|
{{- end }}
|
||||||
{{- with .Values.nodeSelector }}
|
{{- with .Values.nodeSelector }}
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
{{ toYaml . | indent 8 }}
|
{{ toYaml . | indent 8 }}
|
||||||
|
|||||||
21
deployments/helm/templates/scripts.yaml
Normal file
21
deployments/helm/templates/scripts.yaml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{{- if .Values.plugins }}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: {{ include "scm-manager.fullname" . }}-scripts
|
||||||
|
labels:
|
||||||
|
app: {{ include "scm-manager.name" . }}
|
||||||
|
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||||
|
release: "{{ .Release.Name }}"
|
||||||
|
heritage: "{{ .Release.Service }}"
|
||||||
|
data:
|
||||||
|
install-plugins.sh: |
|
||||||
|
#!/bin/sh
|
||||||
|
mkdir -p /data/plugins
|
||||||
|
chown 1000:1000 /data/plugins
|
||||||
|
{{ range $i, $plugin := .Values.plugins }}
|
||||||
|
# install plugin {{ $plugin.name }}
|
||||||
|
wget -O /data/plugins/{{ $plugin.name }}.smp {{ $plugin.url }}
|
||||||
|
chown 1000:1000 /data/plugins/{{ $plugin.name }}.smp
|
||||||
|
{{ end }}
|
||||||
|
{{- end }}
|
||||||
@@ -10,6 +10,10 @@ image:
|
|||||||
tag: latest
|
tag: latest
|
||||||
pullPolicy: Always
|
pullPolicy: Always
|
||||||
|
|
||||||
|
# plugins:
|
||||||
|
# - name: scm-review-plugin
|
||||||
|
# url: https://oss.cloudogu.com/jenkins/job/scm-manager/job/scm-manager-bitbucket/job/scm-review-plugin/job/develop/lastSuccessfulBuild/artifact/target/scm-review-plugin-2.0.0-SNAPSHOT.smp
|
||||||
|
|
||||||
nameOverride: ""
|
nameOverride: ""
|
||||||
fullnameOverride: ""
|
fullnameOverride: ""
|
||||||
|
|
||||||
|
|||||||
24
pom.xml
24
pom.xml
@@ -142,6 +142,11 @@
|
|||||||
<artifactId>junit-vintage-engine</artifactId>
|
<artifactId>junit-vintage-engine</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit-pioneer</groupId>
|
||||||
|
<artifactId>junit-pioneer</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.hamcrest</groupId>
|
<groupId>org.hamcrest</groupId>
|
||||||
<artifactId>hamcrest-core</artifactId>
|
<artifactId>hamcrest-core</artifactId>
|
||||||
@@ -159,6 +164,11 @@
|
|||||||
<artifactId>mockito-core</artifactId>
|
<artifactId>mockito-core</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-junit-jupiter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.assertj</groupId>
|
<groupId>org.assertj</groupId>
|
||||||
<artifactId>assertj-core</artifactId>
|
<artifactId>assertj-core</artifactId>
|
||||||
@@ -325,6 +335,13 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit-pioneer</groupId>
|
||||||
|
<artifactId>junit-pioneer</artifactId>
|
||||||
|
<version>0.3.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.hamcrest</groupId>
|
<groupId>org.hamcrest</groupId>
|
||||||
<artifactId>hamcrest-core</artifactId>
|
<artifactId>hamcrest-core</artifactId>
|
||||||
@@ -346,6 +363,13 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-junit-jupiter</artifactId>
|
||||||
|
<version>${mockito.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.assertj</groupId>
|
<groupId>org.assertj</groupId>
|
||||||
<artifactId>assertj-core</artifactId>
|
<artifactId>assertj-core</artifactId>
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ package sonia.scm;
|
|||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import sonia.scm.util.Util;
|
import sonia.scm.util.Util;
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
//~--- JDK imports ------------------------------------------------------------
|
||||||
@@ -43,6 +44,7 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Properties;
|
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 --------------------------------------------------------------
|
//~--- methods --------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Path resolve(Path path) {
|
||||||
|
if (path.isAbsolute()) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseDirectory.toPath().resolve(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ package sonia.scm;
|
|||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main class for retrieving the home and the version of the SCM-Manager.
|
* 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();
|
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.
|
* Returns the current stage of SCM-Manager.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ public abstract class AbstractRepositoryHandler<C extends RepositoryConfig>
|
|||||||
* @throws NotSupportedFeatureException
|
* @throws NotSupportedFeatureException
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ImportHandler getImportHandler() throws NotSupportedFeatureException
|
public ImportHandler getImportHandler()
|
||||||
{
|
{
|
||||||
throw new NotSupportedFeatureException("import");
|
throw new NotSupportedFeatureException("import");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ public abstract class AbstractSimpleRepositoryHandler<C extends RepositoryConfig
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Repository create(Repository repository) {
|
public Repository create(Repository repository) {
|
||||||
File nativeDirectory = resolveNativeDirectory(repository);
|
File nativeDirectory = resolveNativeDirectory(repository.getId());
|
||||||
try {
|
try {
|
||||||
create(repository, nativeDirectory);
|
create(repository, nativeDirectory);
|
||||||
postCreate(repository, nativeDirectory);
|
postCreate(repository, nativeDirectory);
|
||||||
@@ -106,10 +106,10 @@ public abstract class AbstractSimpleRepositoryHandler<C extends RepositoryConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public File getDirectory(Repository repository) {
|
public File getDirectory(String repositoryId) {
|
||||||
File directory;
|
File directory;
|
||||||
if (isConfigured()) {
|
if (isConfigured()) {
|
||||||
directory = resolveNativeDirectory(repository);
|
directory = resolveNativeDirectory(repositoryId);
|
||||||
} else {
|
} else {
|
||||||
throw new ConfigurationException("RepositoryHandler is not configured");
|
throw new ConfigurationException("RepositoryHandler is not configured");
|
||||||
}
|
}
|
||||||
@@ -167,7 +167,7 @@ public abstract class AbstractSimpleRepositoryHandler<C extends RepositoryConfig
|
|||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
private File resolveNativeDirectory(Repository repository) {
|
private File resolveNativeDirectory(String repositoryId) {
|
||||||
return new File(repositoryLocationResolver.getRepositoryDirectory(repository), REPOSITORIES_NATIVE_DIRECTORY);
|
return repositoryLocationResolver.getPath(repositoryId).resolve(REPOSITORIES_NATIVE_DIRECTORY).toFile();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ public abstract class DirectoryHealthCheck implements HealthCheck
|
|||||||
else if (handler instanceof RepositoryDirectoryHandler)
|
else if (handler instanceof RepositoryDirectoryHandler)
|
||||||
{
|
{
|
||||||
File directory =
|
File directory =
|
||||||
((RepositoryDirectoryHandler) handler).getDirectory(repository);
|
((RepositoryDirectoryHandler) handler).getDirectory(repository.getId());
|
||||||
|
|
||||||
if (directory == null)
|
if (directory == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package sonia.scm.repository;
|
package sonia.scm.repository;
|
||||||
|
|
||||||
import sonia.scm.SCMContextProvider;
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import javax.inject.Inject;
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Location Resolver for File based Repository Storage.
|
* A Location Resolver for File based Repository Storage.
|
||||||
@@ -19,35 +17,17 @@ import java.io.File;
|
|||||||
*/
|
*/
|
||||||
public class InitialRepositoryLocationResolver {
|
public class InitialRepositoryLocationResolver {
|
||||||
|
|
||||||
public static final String DEFAULT_REPOSITORY_PATH = "repositories";
|
private static final String DEFAULT_REPOSITORY_PATH = "repositories";
|
||||||
|
|
||||||
private final SCMContextProvider context;
|
/**
|
||||||
|
* Returns the initial path to repository.
|
||||||
@Inject
|
*
|
||||||
public InitialRepositoryLocationResolver(SCMContextProvider context) {
|
* @param repositoryId id of the repository
|
||||||
this.context = context;
|
*
|
||||||
|
* @return initial path of repository
|
||||||
|
*/
|
||||||
|
public Path getPath(String repositoryId) {
|
||||||
|
return Paths.get(DEFAULT_REPOSITORY_PATH, repositoryId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public InitialRepositoryLocation getRelativeRepositoryPath(Repository repository) {
|
|
||||||
String relativePath = DEFAULT_REPOSITORY_PATH + File.separator + repository.getId();
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ import java.nio.file.Path;
|
|||||||
public interface PathBasedRepositoryDAO extends RepositoryDAO {
|
public interface PathBasedRepositoryDAO extends RepositoryDAO {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current path of the repository. This works for existing repositories only, not for repositories that should be created.
|
* Get the current path of the repository for the given id.
|
||||||
|
* This works for existing repositories only, not for repositories that should be created.
|
||||||
*/
|
*/
|
||||||
Path getPath(Repository repository) ;
|
Path getPath(String repositoryId) ;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,9 +43,8 @@ import java.io.File;
|
|||||||
public interface RepositoryDirectoryHandler extends RepositoryHandler {
|
public interface RepositoryDirectoryHandler extends RepositoryHandler {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current directory of the given repository
|
* Get the current directory of the repository for the given id.
|
||||||
* @param repository
|
|
||||||
* @return the current directory of the given repository
|
* @return the current directory of the given repository
|
||||||
*/
|
*/
|
||||||
File getDirectory(Repository repository);
|
File getDirectory(String repositoryId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
package sonia.scm.repository;
|
package sonia.scm.repository;
|
||||||
|
|
||||||
import groovy.lang.Singleton;
|
import groovy.lang.Singleton;
|
||||||
|
import sonia.scm.SCMContextProvider;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import java.io.File;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Location Resolver for File based Repository Storage.
|
* A Location Resolver for File based Repository Storage.
|
||||||
@@ -20,22 +21,33 @@ import java.io.File;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class RepositoryLocationResolver {
|
public class RepositoryLocationResolver {
|
||||||
|
|
||||||
private RepositoryDAO repositoryDAO;
|
private final SCMContextProvider contextProvider;
|
||||||
private InitialRepositoryLocationResolver initialRepositoryLocationResolver;
|
private final RepositoryDAO repositoryDAO;
|
||||||
|
private final InitialRepositoryLocationResolver initialRepositoryLocationResolver;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public RepositoryLocationResolver(RepositoryDAO repositoryDAO, InitialRepositoryLocationResolver initialRepositoryLocationResolver) {
|
public RepositoryLocationResolver(SCMContextProvider contextProvider, RepositoryDAO repositoryDAO, InitialRepositoryLocationResolver initialRepositoryLocationResolver) {
|
||||||
|
this.contextProvider = contextProvider;
|
||||||
this.repositoryDAO = repositoryDAO;
|
this.repositoryDAO = repositoryDAO;
|
||||||
this.initialRepositoryLocationResolver = initialRepositoryLocationResolver;
|
this.initialRepositoryLocationResolver = initialRepositoryLocationResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
public File getRepositoryDirectory(Repository repository){
|
/**
|
||||||
|
* 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) {
|
if (repositoryDAO instanceof PathBasedRepositoryDAO) {
|
||||||
PathBasedRepositoryDAO pathBasedRepositoryDAO = (PathBasedRepositoryDAO) repositoryDAO;
|
path = ((PathBasedRepositoryDAO) repositoryDAO).getPath(repositoryId);
|
||||||
return pathBasedRepositoryDAO.getPath(repository).toFile();
|
} else {
|
||||||
|
path = initialRepositoryLocationResolver.getPath(repositoryId);
|
||||||
}
|
}
|
||||||
return initialRepositoryLocationResolver.getRelativeRepositoryPath(repository).getAbsolutePath();
|
|
||||||
|
return contextProvider.resolve(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ import javax.xml.stream.XMLStreamWriter;
|
|||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
* @since 1.31
|
* @since 1.31
|
||||||
*/
|
*/
|
||||||
public final class IndentXMLStreamWriter implements XMLStreamWriter
|
public final class IndentXMLStreamWriter implements XMLStreamWriter, AutoCloseable
|
||||||
{
|
{
|
||||||
|
|
||||||
/** line separator */
|
/** line separator */
|
||||||
@@ -475,7 +475,7 @@ public final class IndentXMLStreamWriter implements XMLStreamWriter
|
|||||||
//~--- fields ---------------------------------------------------------------
|
//~--- fields ---------------------------------------------------------------
|
||||||
|
|
||||||
/** indent string */
|
/** indent string */
|
||||||
private String indent = " ";
|
private String indent = " ";
|
||||||
|
|
||||||
/** current level */
|
/** current level */
|
||||||
private int level = 0;
|
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,42 +1,23 @@
|
|||||||
package sonia.scm.repository;
|
package sonia.scm.repository;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.Rule;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.junit.Test;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
import org.junit.rules.TemporaryFolder;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
|
||||||
import sonia.scm.SCMContextProvider;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@ExtendWith({MockitoExtension.class})
|
||||||
public class InitialRepositoryLocationResolverTest {
|
class InitialRepositoryLocationResolverTest {
|
||||||
|
|
||||||
@Mock
|
|
||||||
private SCMContextProvider context;
|
|
||||||
|
|
||||||
@Rule
|
|
||||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void init() throws IOException {
|
|
||||||
when(context.getBaseDirectory()).thenReturn(temporaryFolder.newFolder());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldComputeInitialDirectory() {
|
void shouldComputeInitialPath() {
|
||||||
InitialRepositoryLocationResolver resolver = new InitialRepositoryLocationResolver(context);
|
InitialRepositoryLocationResolver resolver = new InitialRepositoryLocationResolver();
|
||||||
Repository repository = new Repository();
|
Path path = resolver.getPath("42");
|
||||||
repository.setId("ABC");
|
|
||||||
InitialRepositoryLocationResolver.InitialRepositoryLocation directory = resolver.getRelativeRepositoryPath(repository);
|
|
||||||
|
|
||||||
assertThat(directory.getAbsolutePath()).isEqualTo(new File(context.getBaseDirectory(), "repositories/ABC"));
|
assertThat(path).isRelative();
|
||||||
assertThat(directory.getRelativePath()).isEqualTo( "repositories/ABC");
|
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\" ?>");
|
StringBuilder buffer = new StringBuilder("<?xml version=\"1.0\" ?>");
|
||||||
buffer.append(IndentXMLStreamWriter.LINE_SEPARATOR);
|
buffer.append(IndentXMLStreamWriter.LINE_SEPARATOR);
|
||||||
buffer.append("<root>").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(IndentXMLStreamWriter.LINE_SEPARATOR);
|
||||||
buffer.append("</root>").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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
package sonia.scm.repository.xml;
|
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
import sonia.scm.ModelObject;
|
|
||||||
import sonia.scm.repository.Repository;
|
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
|
||||||
import javax.xml.bind.annotation.XmlRootElement;
|
|
||||||
import javax.xml.bind.annotation.XmlTransient;
|
|
||||||
|
|
||||||
@XmlRootElement(name = "repository-link")
|
|
||||||
@XmlAccessorType(XmlAccessType.FIELD)
|
|
||||||
public class RepositoryPath implements ModelObject {
|
|
||||||
|
|
||||||
private String path;
|
|
||||||
private String id;
|
|
||||||
private Long lastModified;
|
|
||||||
private Long creationDate;
|
|
||||||
|
|
||||||
@XmlTransient
|
|
||||||
private Repository repository;
|
|
||||||
|
|
||||||
@XmlTransient
|
|
||||||
private boolean toBeSynchronized;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Needed from JAXB
|
|
||||||
*/
|
|
||||||
public RepositoryPath() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public RepositoryPath(String path, String id, Repository repository) {
|
|
||||||
this.path = path;
|
|
||||||
this.id = id;
|
|
||||||
this.repository = repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Repository getRepository() {
|
|
||||||
return repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRepository(Repository repository) {
|
|
||||||
this.repository = repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPath() {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPath(String path) {
|
|
||||||
this.path = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setLastModified(Long lastModified) {
|
|
||||||
this.lastModified = lastModified;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Long getCreationDate() {
|
|
||||||
return creationDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setCreationDate(Long creationDate) {
|
|
||||||
this.creationDate = creationDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(String id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Long getLastModified() {
|
|
||||||
return lastModified;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getType() {
|
|
||||||
return getRepository()!= null? getRepository().getType():"";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isValid() {
|
|
||||||
return StringUtils.isNotEmpty(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean toBeSynchronized() {
|
|
||||||
return toBeSynchronized;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setToBeSynchronized(boolean toBeSynchronized) {
|
|
||||||
this.toBeSynchronized = toBeSynchronized;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (c) 2010, Sebastian Sdorra
|
* Copyright (c) 2010, Sebastian Sdorra
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
* <p>
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
* <p>
|
||||||
* 1. Redistributions of source code must retain the above copyright notice,
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer.
|
* this list of conditions and the following disclaimer.
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from this
|
* contributors may be used to endorse or promote products derived from this
|
||||||
* software without specific prior written permission.
|
* software without specific prior written permission.
|
||||||
*
|
* <p>
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
@@ -24,9 +24,8 @@
|
|||||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*
|
* <p>
|
||||||
* http://bitbucket.org/sdorra/scm-manager
|
* http://bitbucket.org/sdorra/scm-manager
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@@ -34,153 +33,229 @@ package sonia.scm.repository.xml;
|
|||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- 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 com.google.inject.Singleton;
|
||||||
import sonia.scm.SCMContextProvider;
|
import sonia.scm.SCMContextProvider;
|
||||||
import sonia.scm.io.FileSystem;
|
import sonia.scm.io.FileSystem;
|
||||||
import sonia.scm.repository.InitialRepositoryLocationResolver;
|
import sonia.scm.repository.InitialRepositoryLocationResolver;
|
||||||
import sonia.scm.repository.InitialRepositoryLocationResolver.InitialRepositoryLocation;
|
|
||||||
import sonia.scm.repository.InternalRepositoryException;
|
import sonia.scm.repository.InternalRepositoryException;
|
||||||
import sonia.scm.repository.NamespaceAndName;
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
import sonia.scm.repository.PathBasedRepositoryDAO;
|
import sonia.scm.repository.PathBasedRepositoryDAO;
|
||||||
import sonia.scm.repository.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
import sonia.scm.store.JAXBConfigurationStore;
|
|
||||||
import sonia.scm.store.Store;
|
|
||||||
import sonia.scm.store.StoreConstants;
|
import sonia.scm.store.StoreConstants;
|
||||||
import sonia.scm.xml.AbstractXmlDAO;
|
|
||||||
|
|
||||||
import java.io.File;
|
import javax.inject.Inject;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.time.Clock;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Optional;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
public class XmlRepositoryDAO
|
public class XmlRepositoryDAO implements PathBasedRepositoryDAO {
|
||||||
extends AbstractXmlDAO<Repository, XmlRepositoryDatabase>
|
|
||||||
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 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;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public XmlRepositoryDAO(InitialRepositoryLocationResolver initialRepositoryLocationResolver, FileSystem fileSystem, SCMContextProvider context) {
|
public XmlRepositoryDAO(SCMContextProvider context, InitialRepositoryLocationResolver locationResolver, FileSystem fileSystem) {
|
||||||
super(new JAXBConfigurationStore<>(XmlRepositoryDatabase.class,
|
|
||||||
new File(context.getBaseDirectory(), Store.CONFIG.getGlobalStoreDirectory()+File.separator+ STORE_NAME + StoreConstants.FILE_EXTENSION)));
|
|
||||||
this.initialRepositoryLocationResolver = initialRepositoryLocationResolver;
|
|
||||||
this.fileSystem = fileSystem;
|
|
||||||
this.context = context;
|
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
|
if (!Files.exists(storePath)) {
|
||||||
public boolean contains(NamespaceAndName namespaceAndName) {
|
return;
|
||||||
return db.contains(namespaceAndName);
|
}
|
||||||
|
|
||||||
|
pathDatabase.read(this::loadDates, this::loadRepository);
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- get methods ----------------------------------------------------------
|
private void loadDates(Long creationTime, Long lastModified) {
|
||||||
|
this.creationTime = creationTime;
|
||||||
@Override
|
this.lastModified = lastModified;
|
||||||
public Repository get(NamespaceAndName namespaceAndName) {
|
|
||||||
return db.get(namespaceAndName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- 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
|
@Override
|
||||||
public void modify(Repository repository) {
|
public String getType() {
|
||||||
RepositoryPath repositoryPath = findExistingRepositoryPath(repository).orElseThrow(() -> new InternalRepositoryException(repository, "path object for repository not found"));
|
return "xml";
|
||||||
repositoryPath.setRepository(repository);
|
}
|
||||||
repositoryPath.setToBeSynchronized(true);
|
|
||||||
storeDB();
|
@Override
|
||||||
|
public Long getCreationTime() {
|
||||||
|
return creationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getLastModified() {
|
||||||
|
return lastModified;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void add(Repository repository) {
|
public void add(Repository repository) {
|
||||||
InitialRepositoryLocation initialLocation = initialRepositoryLocationResolver.getRelativeRepositoryPath(repository);
|
Repository clone = repository.clone();
|
||||||
|
|
||||||
|
Path repositoryPath = locationResolver.getPath(repository.getId());
|
||||||
|
Path resolvedPath = context.resolve(repositoryPath);
|
||||||
|
|
||||||
try {
|
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) {
|
} catch (IOException e) {
|
||||||
throw new InternalRepositoryException(repository, "could not create directory for repository data: " + initialLocation.getAbsolutePath(), e);
|
throw new InternalRepositoryException(repository, "failed to create filesystem", e);
|
||||||
}
|
|
||||||
RepositoryPath repositoryPath = new RepositoryPath(initialLocation.getRelativePath(), repository.getId(), repository.clone());
|
|
||||||
repositoryPath.setToBeSynchronized(true);
|
|
||||||
synchronized (store) {
|
|
||||||
db.add(repositoryPath);
|
|
||||||
storeDB();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
@Override
|
||||||
public Repository get(String id) {
|
public Repository get(String id) {
|
||||||
RepositoryPath repositoryPath = db.get(id);
|
return byId.get(id);
|
||||||
if (repositoryPath != null) {
|
|
||||||
return repositoryPath.getRepository();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Repository> getAll() {
|
public Collection<Repository> getAll() {
|
||||||
return db.getRepositories();
|
return ImmutableList.copyOf(byNamespaceAndName.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
* @param repository
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
protected Repository clone(Repository repository) {
|
public void modify(Repository repository) {
|
||||||
return repository.clone();
|
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
|
@Override
|
||||||
public void delete(Repository repository) {
|
public void delete(Repository repository) {
|
||||||
Path directory = getPath(repository);
|
Path path;
|
||||||
super.delete(repository);
|
synchronized (this) {
|
||||||
|
Repository prev = byId.remove(repository.getId());
|
||||||
|
if (prev != null) {
|
||||||
|
byNamespaceAndName.remove(prev.getNamespaceAndName());
|
||||||
|
}
|
||||||
|
|
||||||
|
path = pathById.remove(repository.getId());
|
||||||
|
|
||||||
|
writePathDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
path = context.resolve(path);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fileSystem.destroy(directory.toFile());
|
fileSystem.destroy(path.toFile());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new InternalRepositoryException(repository, "could not delete repository directory", e);
|
throw new InternalRepositoryException(repository, "failed to destroy filesystem", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
protected XmlRepositoryDatabase createNewDatabase() {
|
public Path getPath(String repositoryId) {
|
||||||
return new XmlRepositoryDatabase();
|
return pathById.get(repositoryId);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Path getPath(Repository repository) {
|
|
||||||
return context
|
|
||||||
.getBaseDirectory()
|
|
||||||
.toPath()
|
|
||||||
.resolve(
|
|
||||||
findExistingRepositoryPath(repository)
|
|
||||||
.map(RepositoryPath::getPath)
|
|
||||||
.orElseThrow(() -> new InternalRepositoryException(repository, "could not find base directory for repository")));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<RepositoryPath> findExistingRepositoryPath(Repository repository) {
|
|
||||||
return db.values().stream()
|
|
||||||
.filter(repoPath -> repoPath.getId().equals(repository.getId()))
|
|
||||||
.findAny();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,188 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2010, Sebastian Sdorra
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* 1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
|
||||||
* and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from this
|
|
||||||
* software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
||||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* http://bitbucket.org/sdorra/scm-manager
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
package sonia.scm.repository.xml;
|
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import sonia.scm.repository.NamespaceAndName;
|
|
||||||
import sonia.scm.repository.Repository;
|
|
||||||
import sonia.scm.xml.XmlDatabase;
|
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
|
||||||
import javax.xml.bind.annotation.XmlElement;
|
|
||||||
import javax.xml.bind.annotation.XmlRootElement;
|
|
||||||
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
@XmlRootElement(name = "repository-db")
|
|
||||||
@XmlAccessorType(XmlAccessType.FIELD)
|
|
||||||
public class XmlRepositoryDatabase implements XmlDatabase<RepositoryPath> {
|
|
||||||
|
|
||||||
private Long creationTime;
|
|
||||||
|
|
||||||
private Long lastModified;
|
|
||||||
|
|
||||||
@XmlJavaTypeAdapter(XmlRepositoryMapAdapter.class)
|
|
||||||
@XmlElement(name = "repositories")
|
|
||||||
private Map<String, RepositoryPath> repositoryPathMap = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
|
|
||||||
public XmlRepositoryDatabase() {
|
|
||||||
long c = System.currentTimeMillis();
|
|
||||||
creationTime = c;
|
|
||||||
lastModified = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
static String createKey(NamespaceAndName namespaceAndName)
|
|
||||||
{
|
|
||||||
return namespaceAndName.getNamespace() + ":" + namespaceAndName.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
static String createKey(Repository repository)
|
|
||||||
{
|
|
||||||
return createKey(repository.getNamespaceAndName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void add(RepositoryPath repositoryPath)
|
|
||||||
{
|
|
||||||
repositoryPathMap.put(createKey(repositoryPath.getRepository()), repositoryPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean contains(NamespaceAndName namespaceAndName)
|
|
||||||
{
|
|
||||||
return repositoryPathMap.containsKey(createKey(namespaceAndName));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean contains(String id)
|
|
||||||
{
|
|
||||||
return get(id) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RepositoryPath remove(String id)
|
|
||||||
{
|
|
||||||
return repositoryPathMap.remove(createKey(get(id).getRepository()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<Repository> getRepositories() {
|
|
||||||
List<Repository> repositories = new ArrayList<>();
|
|
||||||
for (RepositoryPath repositoryPath : repositoryPathMap.values()) {
|
|
||||||
Repository repository = repositoryPath.getRepository();
|
|
||||||
repositories.add(repository);
|
|
||||||
}
|
|
||||||
return repositories;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<RepositoryPath> values()
|
|
||||||
{
|
|
||||||
return repositoryPathMap.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Repository get(NamespaceAndName namespaceAndName) {
|
|
||||||
RepositoryPath repositoryPath = repositoryPathMap.get(createKey(namespaceAndName));
|
|
||||||
if (repositoryPath != null) {
|
|
||||||
return repositoryPath.getRepository();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RepositoryPath get(String id) {
|
|
||||||
return values().stream()
|
|
||||||
.filter(repoPath -> repoPath.getId().equals(id))
|
|
||||||
.findFirst()
|
|
||||||
.orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public long getCreationTime()
|
|
||||||
{
|
|
||||||
return creationTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public long getLastModified()
|
|
||||||
{
|
|
||||||
return lastModified;
|
|
||||||
}
|
|
||||||
|
|
||||||
//~--- set methods ----------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param creationTime
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setCreationTime(long creationTime)
|
|
||||||
{
|
|
||||||
this.creationTime = creationTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param lastModified
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setLastModified(long lastModified)
|
|
||||||
{
|
|
||||||
this.lastModified = lastModified;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2010, Sebastian Sdorra
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* 1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
|
||||||
* and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from this
|
|
||||||
* software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
||||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* http://bitbucket.org/sdorra/scm-manager
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
package sonia.scm.repository.xml;
|
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import sonia.scm.repository.Repository;
|
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
|
||||||
import javax.xml.bind.annotation.XmlElement;
|
|
||||||
import javax.xml.bind.annotation.XmlRootElement;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Sebastian Sdorra
|
|
||||||
*/
|
|
||||||
@XmlRootElement(name = "repositories")
|
|
||||||
@XmlAccessorType(XmlAccessType.FIELD)
|
|
||||||
public class XmlRepositoryList implements Iterable<RepositoryPath>
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs ...
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public XmlRepositoryList() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs ...
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param repositoryMap
|
|
||||||
*/
|
|
||||||
public XmlRepositoryList(Map<String, RepositoryPath> repositoryMap)
|
|
||||||
{
|
|
||||||
this.repositories = new LinkedList<>(repositoryMap.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
//~--- methods --------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Iterator<RepositoryPath> iterator()
|
|
||||||
{
|
|
||||||
return repositories.iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
//~--- get methods ----------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public LinkedList<RepositoryPath> getRepositoryPaths()
|
|
||||||
{
|
|
||||||
return repositories;
|
|
||||||
}
|
|
||||||
|
|
||||||
//~--- set methods ----------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param repositories
|
|
||||||
*/
|
|
||||||
public void setRepositories(LinkedList<RepositoryPath> repositories)
|
|
||||||
{
|
|
||||||
this.repositories = repositories;
|
|
||||||
}
|
|
||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
|
||||||
|
|
||||||
/** Field description */
|
|
||||||
@XmlElement(name = "repository-path")
|
|
||||||
private LinkedList<RepositoryPath> repositories;
|
|
||||||
}
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2010, Sebastian Sdorra
|
|
||||||
* All rights reserved.
|
|
||||||
* <p>
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* <p>
|
|
||||||
* 1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
|
||||||
* and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from this
|
|
||||||
* software without specific prior written permission.
|
|
||||||
* <p>
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
||||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
* <p>
|
|
||||||
* http://bitbucket.org/sdorra/scm-manager
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
package sonia.scm.repository.xml;
|
|
||||||
|
|
||||||
import sonia.scm.SCMContext;
|
|
||||||
import sonia.scm.SCMContextProvider;
|
|
||||||
import sonia.scm.repository.InternalRepositoryException;
|
|
||||||
import sonia.scm.repository.Repository;
|
|
||||||
import sonia.scm.store.StoreConstants;
|
|
||||||
import sonia.scm.store.StoreException;
|
|
||||||
|
|
||||||
import javax.xml.bind.JAXBContext;
|
|
||||||
import javax.xml.bind.JAXBException;
|
|
||||||
import javax.xml.bind.Marshaller;
|
|
||||||
import javax.xml.bind.Unmarshaller;
|
|
||||||
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
|
||||||
import java.io.File;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Sebastian Sdorra
|
|
||||||
*/
|
|
||||||
public class XmlRepositoryMapAdapter extends XmlAdapter<XmlRepositoryList, Map<String, RepositoryPath>> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public XmlRepositoryList marshal(Map<String, RepositoryPath> repositoryMap) {
|
|
||||||
XmlRepositoryList repositoryPaths = new XmlRepositoryList(repositoryMap);
|
|
||||||
try {
|
|
||||||
JAXBContext context = JAXBContext.newInstance(Repository.class);
|
|
||||||
Marshaller marshaller = context.createMarshaller();
|
|
||||||
|
|
||||||
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
|
|
||||||
|
|
||||||
// marshall the repo_path/metadata.xml files
|
|
||||||
for (RepositoryPath repositoryPath : repositoryPaths.getRepositoryPaths()) {
|
|
||||||
if (repositoryPath.toBeSynchronized()) {
|
|
||||||
|
|
||||||
File baseDirectory = SCMContext.getContext().getBaseDirectory();
|
|
||||||
Path dir = baseDirectory.toPath().resolve(repositoryPath.getPath());
|
|
||||||
|
|
||||||
if (!Files.isDirectory(dir)) {
|
|
||||||
throw new InternalRepositoryException(repositoryPath.getRepository(), "repository path not found");
|
|
||||||
}
|
|
||||||
marshaller.marshal(repositoryPath.getRepository(), getRepositoryMetadataFile(dir.toFile()));
|
|
||||||
repositoryPath.setToBeSynchronized(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (JAXBException ex) {
|
|
||||||
throw new StoreException("failed to marshal repository database", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return repositoryPaths;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private File getRepositoryMetadataFile(File dir) {
|
|
||||||
return new File(dir, StoreConstants.REPOSITORY_METADATA.concat(StoreConstants.FILE_EXTENSION));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, RepositoryPath> unmarshal(XmlRepositoryList repositoryPaths) {
|
|
||||||
Map<String, RepositoryPath> repositoryPathMap = new LinkedHashMap<>();
|
|
||||||
try {
|
|
||||||
JAXBContext context = JAXBContext.newInstance(Repository.class);
|
|
||||||
Unmarshaller unmarshaller = context.createUnmarshaller();
|
|
||||||
for (RepositoryPath repositoryPath : repositoryPaths) {
|
|
||||||
SCMContextProvider contextProvider = SCMContext.getContext();
|
|
||||||
File baseDirectory = contextProvider.getBaseDirectory();
|
|
||||||
Repository repository = (Repository) unmarshaller.unmarshal(getRepositoryMetadataFile(baseDirectory.toPath().resolve(repositoryPath.getPath()).toFile()));
|
|
||||||
|
|
||||||
repositoryPath.setRepository(repository);
|
|
||||||
repositoryPathMap.put(XmlRepositoryDatabase.createKey(repository), repositoryPath);
|
|
||||||
}
|
|
||||||
} catch (JAXBException ex) {
|
|
||||||
throw new StoreException("failed to unmarshal object", ex);
|
|
||||||
}
|
|
||||||
return repositoryPathMap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -35,32 +35,14 @@ package sonia.scm.store;
|
|||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
import com.google.common.base.Charsets;
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.Collections2;
|
import com.google.common.collect.Collections2;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import sonia.scm.security.KeyGenerator;
|
import sonia.scm.security.KeyGenerator;
|
||||||
import sonia.scm.xml.IndentXMLStreamWriter;
|
import sonia.scm.xml.IndentXMLStreamWriter;
|
||||||
|
import sonia.scm.xml.XmlStreams;
|
||||||
//~--- 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 javax.xml.bind.JAXBContext;
|
import javax.xml.bind.JAXBContext;
|
||||||
import javax.xml.bind.JAXBElement;
|
import javax.xml.bind.JAXBElement;
|
||||||
@@ -68,11 +50,14 @@ import javax.xml.bind.JAXBException;
|
|||||||
import javax.xml.bind.Marshaller;
|
import javax.xml.bind.Marshaller;
|
||||||
import javax.xml.bind.Unmarshaller;
|
import javax.xml.bind.Unmarshaller;
|
||||||
import javax.xml.namespace.QName;
|
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.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 --------------------------------------------------------------
|
//~--- 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
|
* Method description
|
||||||
*
|
*
|
||||||
@@ -333,15 +250,13 @@ public class JAXBConfigurationEntryStore<V> implements ConfigurationEntryStore<V
|
|||||||
{
|
{
|
||||||
logger.debug("load configuration from {}", file);
|
logger.debug("load configuration from {}", file);
|
||||||
|
|
||||||
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
|
|
||||||
|
|
||||||
XMLStreamReader reader = null;
|
XMLStreamReader reader = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Unmarshaller u = context.createUnmarshaller();
|
Unmarshaller u = context.createUnmarshaller();
|
||||||
|
|
||||||
reader = xmlInputFactory.createXMLStreamReader(createReader());
|
reader = XmlStreams.createReader(file);
|
||||||
|
|
||||||
// configuration
|
// configuration
|
||||||
reader.nextTag();
|
reader.nextTag();
|
||||||
@@ -390,7 +305,7 @@ public class JAXBConfigurationEntryStore<V> implements ConfigurationEntryStore<V
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
close(reader);
|
XmlStreams.close(reader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,17 +317,8 @@ public class JAXBConfigurationEntryStore<V> implements ConfigurationEntryStore<V
|
|||||||
{
|
{
|
||||||
logger.debug("store configuration to {}", file);
|
logger.debug("store configuration to {}", file);
|
||||||
|
|
||||||
IndentXMLStreamWriter writer = null;
|
try (IndentXMLStreamWriter writer = XmlStreams.createWriter(file))
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
//J-
|
|
||||||
writer = new IndentXMLStreamWriter(
|
|
||||||
XMLOutputFactory.newInstance().createXMLStreamWriter(
|
|
||||||
createWriter()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
//J+
|
|
||||||
writer.writeStartDocument();
|
writer.writeStartDocument();
|
||||||
|
|
||||||
// configuration start
|
// configuration start
|
||||||
@@ -453,10 +359,6 @@ public class JAXBConfigurationEntryStore<V> implements ConfigurationEntryStore<V
|
|||||||
{
|
{
|
||||||
throw new StoreException("could not store configuration", ex);
|
throw new StoreException("could not store configuration", ex);
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
{
|
|
||||||
close(writer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
//~--- 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,111 +1,362 @@
|
|||||||
package sonia.scm.repository.xml;
|
package sonia.scm.repository.xml;
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Ignore;
|
import com.google.common.base.Charsets;
|
||||||
import org.junit.Rule;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.rules.TemporaryFolder;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.junit.runner.RunWith;
|
import org.junitpioneer.jupiter.TempDirectory;
|
||||||
import org.mockito.Mock;
|
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.SCMContextProvider;
|
||||||
import sonia.scm.io.DefaultFileSystem;
|
import sonia.scm.io.DefaultFileSystem;
|
||||||
import sonia.scm.io.FileSystem;
|
import sonia.scm.io.FileSystem;
|
||||||
import sonia.scm.repository.InitialRepositoryLocationResolver;
|
import sonia.scm.repository.InitialRepositoryLocationResolver;
|
||||||
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
import sonia.scm.repository.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
import sonia.scm.store.ConfigurationStore;
|
import sonia.scm.repository.RepositoryTestData;
|
||||||
import sonia.scm.store.ConfigurationStoreFactory;
|
|
||||||
import sonia.scm.store.StoreParameters;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
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.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.codehaus.groovy.runtime.InvokerHelper.asList;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.argThat;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static sonia.scm.repository.xml.XmlRepositoryDAO.STORE_NAME;
|
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@ExtendWith({MockitoExtension.class, TempDirectory.class})
|
||||||
@Ignore
|
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||||
public class XmlRepositoryDAOTest {
|
class XmlRepositoryDAOTest {
|
||||||
|
|
||||||
@Mock
|
|
||||||
private ConfigurationStoreFactory storeFactory;
|
|
||||||
@Mock
|
|
||||||
private ConfigurationStore<XmlRepositoryDatabase> store;
|
|
||||||
@Mock
|
|
||||||
private XmlRepositoryDatabase db;
|
|
||||||
@Mock
|
@Mock
|
||||||
private SCMContextProvider context;
|
private SCMContextProvider context;
|
||||||
|
|
||||||
@Rule
|
@Mock
|
||||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
private InitialRepositoryLocationResolver locationResolver;
|
||||||
|
|
||||||
private final FileSystem fileSystem = new DefaultFileSystem();
|
private FileSystem fileSystem = new DefaultFileSystem();
|
||||||
|
|
||||||
@Before
|
private XmlRepositoryDAO dao;
|
||||||
public void init() throws IOException {
|
|
||||||
StoreParameters storeParameters = new StoreParameters().withType(XmlRepositoryDatabase.class).withName(STORE_NAME).build();
|
private Path baseDirectory;
|
||||||
when(storeFactory.getStore(storeParameters)).thenReturn(store);
|
|
||||||
when(store.get()).thenReturn(db);
|
private AtomicLong atomicClock;
|
||||||
when(context.getBaseDirectory()).thenReturn(temporaryFolder.newFolder());
|
|
||||||
|
@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
|
@Test
|
||||||
public void addShouldCreateNewRepositoryPathWithRelativePath() {
|
void shouldReturnXmlType() {
|
||||||
InitialRepositoryLocationResolver initialRepositoryLocationResolver = new InitialRepositoryLocationResolver(context);
|
assertThat(dao.getType()).isEqualTo("xml");
|
||||||
XmlRepositoryDAO dao = new XmlRepositoryDAO(initialRepositoryLocationResolver, fileSystem, context);
|
|
||||||
|
|
||||||
dao.add(new Repository("id", "git", "namespace", "name"));
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void modifyShouldStoreChangedRepository() {
|
void shouldReturnCreationTimeAfterCreation() {
|
||||||
Repository oldRepository = new Repository("id", "old", null, null);
|
long now = System.currentTimeMillis();
|
||||||
RepositoryPath repositoryPath = new RepositoryPath("/path", "id", oldRepository);
|
assertThat(dao.getCreationTime()).isBetween(now - 200, now + 200);
|
||||||
when(db.values()).thenReturn(asList(repositoryPath));
|
|
||||||
|
|
||||||
XmlRepositoryDAO dao = new XmlRepositoryDAO(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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldGetPathInBaseDirectoryForRelativePath() {
|
void shouldNotReturnLastModifiedAfterCreation() {
|
||||||
Repository existingRepository = new Repository("id", "old", null, null);
|
assertThat(dao.getLastModified()).isNull();
|
||||||
RepositoryPath repositoryPath = new RepositoryPath("path", "id", existingRepository);
|
|
||||||
when(db.values()).thenReturn(asList(repositoryPath));
|
|
||||||
|
|
||||||
XmlRepositoryDAO dao = new XmlRepositoryDAO(new InitialRepositoryLocationResolver(context), fileSystem, context);
|
|
||||||
|
|
||||||
Path path = dao.getPath(existingRepository);
|
|
||||||
|
|
||||||
assertThat(path.toString()).isEqualTo(context.getBaseDirectory().getPath() + "/path");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldGetPathInBaseDirectoryForAbsolutePath() {
|
void shouldReturnTrueForEachContainsMethod() {
|
||||||
Repository existingRepository = new Repository("id", "old", null, null);
|
Repository heartOfGold = createHeartOfGold();
|
||||||
RepositoryPath repositoryPath = new RepositoryPath("/tmp/path", "id", existingRepository);
|
dao.add(heartOfGold);
|
||||||
when(db.values()).thenReturn(asList(repositoryPath));
|
|
||||||
|
|
||||||
XmlRepositoryDAO dao = new XmlRepositoryDAO(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);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ public class GitGcTask implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void gc(Repository repository){
|
private void gc(Repository repository){
|
||||||
File file = repositoryHandler.getDirectory(repository);
|
File file = repositoryHandler.getDirectory(repository.getId());
|
||||||
Git git = null;
|
Git git = null;
|
||||||
try {
|
try {
|
||||||
git = open(file);
|
git = open(file);
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ public abstract class AbstractGitIncomingOutgoingCommand
|
|||||||
|
|
||||||
Git git = Git.wrap(open());
|
Git git = Git.wrap(open());
|
||||||
|
|
||||||
GitUtil.fetch(git, handler.getDirectory(remoteRepository), remoteRepository);
|
GitUtil.fetch(git, handler.getDirectory(remoteRepository.getId()), remoteRepository);
|
||||||
|
|
||||||
ObjectId localId = getDefaultBranch(git.getRepository());
|
ObjectId localId = getDefaultBranch(git.getRepository());
|
||||||
ObjectId remoteId = null;
|
ObjectId remoteId = null;
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ public abstract class AbstractGitPushOrPullCommand extends AbstractGitCommand
|
|||||||
*/
|
*/
|
||||||
protected String getRemoteUrl(sonia.scm.repository.Repository repository)
|
protected String getRemoteUrl(sonia.scm.repository.Repository repository)
|
||||||
{
|
{
|
||||||
return getRemoteUrl(handler.getDirectory(repository));
|
return getRemoteUrl(handler.getDirectory(repository.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- methods --------------------------------------------------------------
|
//~--- methods --------------------------------------------------------------
|
||||||
|
|||||||
@@ -196,12 +196,12 @@ public class GitPullCommand extends AbstractGitPushOrPullCommand
|
|||||||
private PullResponse pullFromScmRepository(Repository sourceRepository)
|
private PullResponse pullFromScmRepository(Repository sourceRepository)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
File sourceDirectory = handler.getDirectory(sourceRepository);
|
File sourceDirectory = handler.getDirectory(sourceRepository.getId());
|
||||||
|
|
||||||
Preconditions.checkArgument(sourceDirectory.exists(),
|
Preconditions.checkArgument(sourceDirectory.exists(),
|
||||||
"source repository directory does not exists");
|
"source repository directory does not exists");
|
||||||
|
|
||||||
File targetDirectory = handler.getDirectory(repository);
|
File targetDirectory = handler.getDirectory(repository.getId());
|
||||||
|
|
||||||
Preconditions.checkArgument(sourceDirectory.exists(),
|
Preconditions.checkArgument(sourceDirectory.exists(),
|
||||||
"target repository directory does not exists");
|
"target repository directory does not exists");
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
|
|||||||
public GitRepositoryServiceProvider(GitRepositoryHandler handler, Repository repository) {
|
public GitRepositoryServiceProvider(GitRepositoryHandler handler, Repository repository) {
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
this.context = new GitContext(handler.getDirectory(repository), repository);
|
this.context = new GitContext(handler.getDirectory(repository.getId()), repository);
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- methods --------------------------------------------------------------
|
//~--- methods --------------------------------------------------------------
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ public class GitRepositoryResolver implements RepositoryResolver<HttpServletRequ
|
|||||||
|
|
||||||
if (config.isValid())
|
if (config.isValid())
|
||||||
{
|
{
|
||||||
File gitdir = handler.getDirectory(repo);
|
File gitdir = handler.getDirectory(repo.getId());
|
||||||
if (gitdir == null) {
|
if (gitdir == null) {
|
||||||
throw new RepositoryNotFoundException(repositoryName);
|
throw new RepositoryNotFoundException(repositoryName);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,8 +62,6 @@ public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
|||||||
@Mock
|
@Mock
|
||||||
private GitWorkdirFactory gitWorkdirFactory;
|
private GitWorkdirFactory gitWorkdirFactory;
|
||||||
|
|
||||||
RepositoryLocationResolver repositoryLocationResolver;
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void checkDirectory(File directory) {
|
protected void checkDirectory(File directory) {
|
||||||
@@ -86,10 +84,10 @@ public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory,
|
protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory,
|
||||||
|
RepositoryLocationResolver locationResolver,
|
||||||
File directory) {
|
File directory) {
|
||||||
repositoryLocationResolver = new RepositoryLocationResolver(repoDao, new InitialRepositoryLocationResolver(contextProvider));
|
|
||||||
GitRepositoryHandler repositoryHandler = new GitRepositoryHandler(factory,
|
GitRepositoryHandler repositoryHandler = new GitRepositoryHandler(factory,
|
||||||
scheduler, repositoryLocationResolver, gitWorkdirFactory);
|
scheduler, locationResolver, gitWorkdirFactory);
|
||||||
repositoryHandler.init(contextProvider);
|
repositoryHandler.init(contextProvider);
|
||||||
|
|
||||||
GitConfig config = new GitConfig();
|
GitConfig config = new GitConfig();
|
||||||
@@ -103,7 +101,7 @@ public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
|||||||
@Test
|
@Test
|
||||||
public void getDirectory() {
|
public void getDirectory() {
|
||||||
GitRepositoryHandler repositoryHandler = new GitRepositoryHandler(factory,
|
GitRepositoryHandler repositoryHandler = new GitRepositoryHandler(factory,
|
||||||
scheduler, repositoryLocationResolver, gitWorkdirFactory);
|
scheduler, locationResolver, gitWorkdirFactory);
|
||||||
GitConfig config = new GitConfig();
|
GitConfig config = new GitConfig();
|
||||||
config.setDisabled(false);
|
config.setDisabled(false);
|
||||||
config.setGcExpression("gc exp");
|
config.setGcExpression("gc exp");
|
||||||
@@ -111,7 +109,7 @@ public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
|||||||
repositoryHandler.setConfig(config);
|
repositoryHandler.setConfig(config);
|
||||||
|
|
||||||
initRepository();
|
initRepository();
|
||||||
File path = repositoryHandler.getDirectory(repository);
|
File path = repositoryHandler.getDirectory(repository.getId());
|
||||||
assertEquals(repoPath.toString() + File.separator + AbstractSimpleRepositoryHandler.REPOSITORIES_NATIVE_DIRECTORY, path.getAbsolutePath());
|
assertEquals(repoPath.toString() + File.separator + AbstractSimpleRepositoryHandler.REPOSITORIES_NATIVE_DIRECTORY, path.getAbsolutePath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,9 +92,9 @@ public class AbstractRemoteCommandTestBase
|
|||||||
outgoing = Git.init().setDirectory(outgoingDirectory).setBare(false).call();
|
outgoing = Git.init().setDirectory(outgoingDirectory).setBare(false).call();
|
||||||
|
|
||||||
handler = mock(GitRepositoryHandler.class);
|
handler = mock(GitRepositoryHandler.class);
|
||||||
when(handler.getDirectory(incomingRepository)).thenReturn(
|
when(handler.getDirectory(incomingRepository.getId())).thenReturn(
|
||||||
incomingDirectory);
|
incomingDirectory);
|
||||||
when(handler.getDirectory(outgoingRepository)).thenReturn(
|
when(handler.getDirectory(outgoingRepository.getId())).thenReturn(
|
||||||
outgoingDirectory);
|
outgoingDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ public class AbstractHgHandler
|
|||||||
protected AbstractHgHandler(HgRepositoryHandler handler, HgContext context,
|
protected AbstractHgHandler(HgRepositoryHandler handler, HgContext context,
|
||||||
Repository repository)
|
Repository repository)
|
||||||
{
|
{
|
||||||
this(handler, context, repository, handler.getDirectory(repository));
|
this(handler, context, repository, handler.getDirectory(repository.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public class AbstractHgPushOrPullCommand extends AbstractCommand
|
|||||||
if (repo != null)
|
if (repo != null)
|
||||||
{
|
{
|
||||||
url =
|
url =
|
||||||
handler.getDirectory(request.getRemoteRepository()).getAbsolutePath();
|
handler.getDirectory(request.getRemoteRepository().getId()).getAbsolutePath();
|
||||||
}
|
}
|
||||||
else if (request.getRemoteUrl() != null)
|
else if (request.getRemoteUrl() != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ public class HgIncomingCommand extends AbstractCommand
|
|||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public ChangesetPagingResult getIncomingChangesets(IncomingCommandRequest request) {
|
public ChangesetPagingResult getIncomingChangesets(IncomingCommandRequest request) {
|
||||||
File remoteRepository = handler.getDirectory(request.getRemoteRepository());
|
File remoteRepository = handler.getDirectory(request.getRemoteRepository().getId());
|
||||||
|
|
||||||
com.aragost.javahg.Repository repository = open();
|
com.aragost.javahg.Repository repository = open();
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public class HgOutgoingCommand extends AbstractCommand
|
|||||||
public ChangesetPagingResult getOutgoingChangesets(
|
public ChangesetPagingResult getOutgoingChangesets(
|
||||||
OutgoingCommandRequest request)
|
OutgoingCommandRequest request)
|
||||||
{
|
{
|
||||||
File remoteRepository = handler.getDirectory(request.getRemoteRepository());
|
File remoteRepository = handler.getDirectory(request.getRemoteRepository().getId());
|
||||||
|
|
||||||
com.aragost.javahg.Repository repository = open();
|
com.aragost.javahg.Repository repository = open();
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ public class HgRepositoryServiceProvider extends RepositoryServiceProvider
|
|||||||
{
|
{
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
this.repositoryDirectory = handler.getDirectory(repository);
|
this.repositoryDirectory = handler.getDirectory(repository.getId());
|
||||||
this.context = new HgCommandContext(hookManager, handler, repository,
|
this.context = new HgCommandContext(hookManager, handler, repository,
|
||||||
repositoryDirectory);
|
repositoryDirectory);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,9 @@ public class HgCGIServlet extends HttpServlet implements ScmProviderHttpServlet
|
|||||||
/** Field description */
|
/** Field description */
|
||||||
public static final String ENV_REPOSITORY_PATH = "SCM_REPOSITORY_PATH";
|
public static final String ENV_REPOSITORY_PATH = "SCM_REPOSITORY_PATH";
|
||||||
|
|
||||||
|
/** Field description */
|
||||||
|
public static final String ENV_REPOSITORY_ID = "SCM_REPOSITORY_ID";
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
public static final String ENV_SESSION_PREFIX = "SCM_";
|
public static final String ENV_SESSION_PREFIX = "SCM_";
|
||||||
|
|
||||||
@@ -250,8 +253,7 @@ public class HgCGIServlet extends HttpServlet implements ScmProviderHttpServlet
|
|||||||
HttpServletResponse response, Repository repository)
|
HttpServletResponse response, Repository repository)
|
||||||
throws IOException, ServletException
|
throws IOException, ServletException
|
||||||
{
|
{
|
||||||
String name = repository.getName();
|
File directory = handler.getDirectory(repository.getId());
|
||||||
File directory = handler.getDirectory(repository);
|
|
||||||
CGIExecutor executor = cgiExecutorFactory.createExecutor(configuration,
|
CGIExecutor executor = cgiExecutorFactory.createExecutor(configuration,
|
||||||
getServletContext(), request, response);
|
getServletContext(), request, response);
|
||||||
|
|
||||||
@@ -261,6 +263,7 @@ public class HgCGIServlet extends HttpServlet implements ScmProviderHttpServlet
|
|||||||
executor.setStatusCodeHandler(exceptionHandler);
|
executor.setStatusCodeHandler(exceptionHandler);
|
||||||
executor.setContentLengthWorkaround(true);
|
executor.setContentLengthWorkaround(true);
|
||||||
executor.getEnvironment().set(ENV_REPOSITORY_NAME, repository.getNamespace() + "/" + repository.getName());
|
executor.getEnvironment().set(ENV_REPOSITORY_NAME, repository.getNamespace() + "/" + repository.getName());
|
||||||
|
executor.getEnvironment().set(ENV_REPOSITORY_ID, repository.getId());
|
||||||
executor.getEnvironment().set(ENV_REPOSITORY_PATH,
|
executor.getEnvironment().set(ENV_REPOSITORY_PATH,
|
||||||
directory.getAbsolutePath());
|
directory.getAbsolutePath());
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ package sonia.scm.web;
|
|||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.io.Closeables;
|
import com.google.common.io.Closeables;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
@@ -44,12 +45,10 @@ import org.apache.shiro.SecurityUtils;
|
|||||||
import org.apache.shiro.subject.Subject;
|
import org.apache.shiro.subject.Subject;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import sonia.scm.ContextEntry;
|
|
||||||
import sonia.scm.NotFoundException;
|
import sonia.scm.NotFoundException;
|
||||||
import sonia.scm.repository.HgContext;
|
import sonia.scm.repository.HgContext;
|
||||||
import sonia.scm.repository.HgHookManager;
|
import sonia.scm.repository.HgHookManager;
|
||||||
import sonia.scm.repository.HgRepositoryHandler;
|
import sonia.scm.repository.HgRepositoryHandler;
|
||||||
import sonia.scm.repository.InternalRepositoryException;
|
|
||||||
import sonia.scm.repository.RepositoryHookType;
|
import sonia.scm.repository.RepositoryHookType;
|
||||||
import sonia.scm.repository.api.HgHookMessage;
|
import sonia.scm.repository.api.HgHookMessage;
|
||||||
import sonia.scm.repository.api.HgHookMessage.Severity;
|
import sonia.scm.repository.api.HgHookMessage.Severity;
|
||||||
@@ -88,7 +87,7 @@ public class HgHookCallbackServlet extends HttpServlet
|
|||||||
public static final String HGHOOK_PRE_RECEIVE = "pretxnchangegroup";
|
public static final String HGHOOK_PRE_RECEIVE = "pretxnchangegroup";
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
public static final String PARAM_REPOSITORYPATH = "repositoryPath";
|
public static final String PARAM_REPOSITORYID = "repositoryId";
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
private static final String PARAM_CHALLENGE = "challenge";
|
private static final String PARAM_CHALLENGE = "challenge";
|
||||||
@@ -170,7 +169,7 @@ public class HgHookCallbackServlet extends HttpServlet
|
|||||||
|
|
||||||
if (m.matches())
|
if (m.matches())
|
||||||
{
|
{
|
||||||
File repositoryPath = getRepositoryPath(request);
|
String repositoryId = getRepositoryId(request);
|
||||||
String type = m.group(1);
|
String type = m.group(1);
|
||||||
String challenge = request.getParameter(PARAM_CHALLENGE);
|
String challenge = request.getParameter(PARAM_CHALLENGE);
|
||||||
|
|
||||||
@@ -187,7 +186,7 @@ public class HgHookCallbackServlet extends HttpServlet
|
|||||||
authenticate(request, credentials);
|
authenticate(request, credentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
hookCallback(response, repositoryPath, type, challenge, node);
|
hookCallback(response, type, repositoryId, challenge, node);
|
||||||
}
|
}
|
||||||
else if (logger.isDebugEnabled())
|
else if (logger.isDebugEnabled())
|
||||||
{
|
{
|
||||||
@@ -246,7 +245,7 @@ public class HgHookCallbackServlet extends HttpServlet
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fireHook(HttpServletResponse response, File repositoryDirectory, String node, RepositoryHookType type)
|
private void fireHook(HttpServletResponse response, String repositoryId, String node, RepositoryHookType type)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
HgHookContextProvider context = null;
|
HgHookContextProvider context = null;
|
||||||
@@ -258,10 +257,10 @@ public class HgHookCallbackServlet extends HttpServlet
|
|||||||
contextProvider.get().setPending(true);
|
contextProvider.get().setPending(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File repositoryDirectory = handler.getDirectory(repositoryId);
|
||||||
context = new HgHookContextProvider(handler, repositoryDirectory, hookManager,
|
context = new HgHookContextProvider(handler, repositoryDirectory, hookManager,
|
||||||
node, type);
|
node, type);
|
||||||
|
|
||||||
String repositoryId = getRepositoryId(repositoryDirectory);
|
|
||||||
hookEventFacade.handle(repositoryId).fireHookEvent(type, context);
|
hookEventFacade.handle(repositoryId).fireHookEvent(type, context);
|
||||||
|
|
||||||
printMessages(response, context);
|
printMessages(response, context);
|
||||||
@@ -280,7 +279,7 @@ public class HgHookCallbackServlet extends HttpServlet
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void hookCallback(HttpServletResponse response, File repositoryDirectory, String typeName, String challenge, String node) throws IOException {
|
private void hookCallback(HttpServletResponse response, String typeName, String repositoryId, String challenge, String node) throws IOException {
|
||||||
if (hookManager.isAcceptAble(challenge))
|
if (hookManager.isAcceptAble(challenge))
|
||||||
{
|
{
|
||||||
RepositoryHookType type = null;
|
RepositoryHookType type = null;
|
||||||
@@ -296,7 +295,7 @@ public class HgHookCallbackServlet extends HttpServlet
|
|||||||
|
|
||||||
if (type != null)
|
if (type != null)
|
||||||
{
|
{
|
||||||
fireHook(response, repositoryDirectory, node, type);
|
fireHook(response, repositoryId, node, type);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -441,21 +440,11 @@ public class HgHookCallbackServlet extends HttpServlet
|
|||||||
|
|
||||||
//~--- get methods ----------------------------------------------------------
|
//~--- get methods ----------------------------------------------------------
|
||||||
|
|
||||||
@SuppressWarnings("squid:S2083") // we do nothing with the path given, so this should be no issue
|
private String getRepositoryId(HttpServletRequest request)
|
||||||
private String getRepositoryId(File repositoryPath)
|
|
||||||
{
|
{
|
||||||
return handler.getRepositoryId(repositoryPath);
|
String id = request.getParameter(PARAM_REPOSITORYID);
|
||||||
}
|
Preconditions.checkArgument(!Strings.isNullOrEmpty(id), "repository id not found in request");
|
||||||
|
return id;
|
||||||
private File getRepositoryPath(HttpServletRequest request) {
|
|
||||||
String path = request.getParameter(PARAM_REPOSITORYPATH);
|
|
||||||
if (Util.isNotEmpty(path)) {
|
|
||||||
return new File(path);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InternalRepositoryException(ContextEntry.ContextBuilder.entity("directory", path), "could not find hgrc in directory");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
//~--- fields ---------------------------------------------------------------
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import os, urllib, urllib2
|
|||||||
baseUrl = os.environ['SCM_URL']
|
baseUrl = os.environ['SCM_URL']
|
||||||
challenge = os.environ['SCM_CHALLENGE']
|
challenge = os.environ['SCM_CHALLENGE']
|
||||||
credentials = os.environ['SCM_CREDENTIALS']
|
credentials = os.environ['SCM_CREDENTIALS']
|
||||||
|
repositoryId = os.environ['SCM_REPOSITORY_ID']
|
||||||
|
|
||||||
def printMessages(ui, msgs):
|
def printMessages(ui, msgs):
|
||||||
for line in msgs:
|
for line in msgs:
|
||||||
@@ -53,7 +54,7 @@ def callHookUrl(ui, repo, hooktype, node):
|
|||||||
try:
|
try:
|
||||||
url = baseUrl + hooktype
|
url = baseUrl + hooktype
|
||||||
ui.debug( "send scm-hook to " + url + " and " + node + "\n" )
|
ui.debug( "send scm-hook to " + url + " and " + node + "\n" )
|
||||||
data = urllib.urlencode({'node': node, 'challenge': challenge, 'credentials': credentials, 'repositoryPath': repo.root})
|
data = urllib.urlencode({'node': node, 'challenge': challenge, 'credentials': credentials, 'repositoryPath': repo.root, 'repositoryId': repositoryId})
|
||||||
# open url but ignore proxy settings
|
# open url but ignore proxy settings
|
||||||
proxy_handler = urllib2.ProxyHandler({})
|
proxy_handler = urllib2.ProxyHandler({})
|
||||||
opener = urllib2.build_opener(proxy_handler)
|
opener = urllib2.build_opener(proxy_handler)
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ import static org.junit.Assert.assertTrue;
|
|||||||
/**
|
/**
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
*/
|
*/
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.Silent.class)
|
||||||
public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
@@ -59,8 +59,6 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
|||||||
@Mock
|
@Mock
|
||||||
private com.google.inject.Provider<HgContext> provider;
|
private com.google.inject.Provider<HgContext> provider;
|
||||||
|
|
||||||
private RepositoryLocationResolver repositoryLocationResolver;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void checkDirectory(File directory) {
|
protected void checkDirectory(File directory) {
|
||||||
File hgDirectory = new File(directory, ".hg");
|
File hgDirectory = new File(directory, ".hg");
|
||||||
@@ -70,11 +68,8 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory,
|
protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory, RepositoryLocationResolver locationResolver, File directory) {
|
||||||
File directory) {
|
HgRepositoryHandler handler = new HgRepositoryHandler(factory, new HgContextProvider(), locationResolver);
|
||||||
repositoryLocationResolver = new RepositoryLocationResolver(repoDao, new InitialRepositoryLocationResolver(contextProvider));
|
|
||||||
HgRepositoryHandler handler = new HgRepositoryHandler(factory,
|
|
||||||
new HgContextProvider(), repositoryLocationResolver);
|
|
||||||
|
|
||||||
handler.init(contextProvider);
|
handler.init(contextProvider);
|
||||||
HgTestUtil.checkForSkip(handler);
|
HgTestUtil.checkForSkip(handler);
|
||||||
@@ -84,8 +79,7 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getDirectory() {
|
public void getDirectory() {
|
||||||
HgRepositoryHandler repositoryHandler = new HgRepositoryHandler(factory,
|
HgRepositoryHandler repositoryHandler = new HgRepositoryHandler(factory, provider, locationResolver);
|
||||||
provider, repositoryLocationResolver);
|
|
||||||
|
|
||||||
HgConfig hgConfig = new HgConfig();
|
HgConfig hgConfig = new HgConfig();
|
||||||
hgConfig.setHgBinary("hg");
|
hgConfig.setHgBinary("hg");
|
||||||
@@ -93,7 +87,7 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
|||||||
repositoryHandler.setConfig(hgConfig);
|
repositoryHandler.setConfig(hgConfig);
|
||||||
|
|
||||||
initRepository();
|
initRepository();
|
||||||
File path = repositoryHandler.getDirectory(repository);
|
File path = repositoryHandler.getDirectory(repository.getId());
|
||||||
assertEquals(repoPath.toString() + File.separator + AbstractSimpleRepositoryHandler.REPOSITORIES_NATIVE_DIRECTORY, path.getAbsolutePath());
|
assertEquals(repoPath.toString() + File.separator + AbstractSimpleRepositoryHandler.REPOSITORIES_NATIVE_DIRECTORY, path.getAbsolutePath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ public final class HgTestUtil
|
|||||||
|
|
||||||
PathBasedRepositoryDAO repoDao = mock(PathBasedRepositoryDAO.class);
|
PathBasedRepositoryDAO repoDao = mock(PathBasedRepositoryDAO.class);
|
||||||
|
|
||||||
RepositoryLocationResolver repositoryLocationResolver = new RepositoryLocationResolver(repoDao, new InitialRepositoryLocationResolver(context));
|
RepositoryLocationResolver repositoryLocationResolver = new RepositoryLocationResolver(context, repoDao, new InitialRepositoryLocationResolver());
|
||||||
HgRepositoryHandler handler =
|
HgRepositoryHandler handler =
|
||||||
new HgRepositoryHandler(new InMemoryConfigurationStoreFactory(), new HgContextProvider(), repositoryLocationResolver);
|
new HgRepositoryHandler(new InMemoryConfigurationStoreFactory(), new HgContextProvider(), repositoryLocationResolver);
|
||||||
Path repoDir = directory.toPath();
|
Path repoDir = directory.toPath();
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import sonia.scm.Stage;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -136,6 +137,11 @@ public class TempSCMContextProvider implements SCMContextProvider
|
|||||||
this.baseDirectory = baseDirectory;
|
this.baseDirectory = baseDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Path resolve(Path path) {
|
||||||
|
return baseDirectory.toPath().resolve(path);
|
||||||
|
}
|
||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
//~--- fields ---------------------------------------------------------------
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
|
|||||||
@@ -94,9 +94,9 @@ public abstract class IncomingOutgoingTestBase extends AbstractTestBase
|
|||||||
outgoing = Repository.create(createConfig(temp), outgoingDirectory);
|
outgoing = Repository.create(createConfig(temp), outgoingDirectory);
|
||||||
|
|
||||||
handler = mock(HgRepositoryHandler.class);
|
handler = mock(HgRepositoryHandler.class);
|
||||||
when(handler.getDirectory(incomingRepository)).thenReturn(
|
when(handler.getDirectory(incomingRepository.getId())).thenReturn(
|
||||||
incomingDirectory);
|
incomingDirectory);
|
||||||
when(handler.getDirectory(outgoingRepository)).thenReturn(
|
when(handler.getDirectory(outgoingRepository.getId())).thenReturn(
|
||||||
outgoingDirectory);
|
outgoingDirectory);
|
||||||
when(handler.getConfig()).thenReturn(temp.getConfig());
|
when(handler.getConfig()).thenReturn(temp.getConfig());
|
||||||
when(handler.getHgContext()).thenReturn(new HgContext());
|
when(handler.getHgContext()).thenReturn(new HgContext());
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ public class SvnRepositoryServiceProvider extends RepositoryServiceProvider
|
|||||||
Repository repository)
|
Repository repository)
|
||||||
{
|
{
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
this.context = new SvnContext(handler.getDirectory(repository));
|
this.context = new SvnContext(handler.getDirectory(repository.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- methods --------------------------------------------------------------
|
//~--- methods --------------------------------------------------------------
|
||||||
|
|||||||
@@ -292,7 +292,7 @@ public class SvnDAVConfig extends DAVConfig
|
|||||||
|
|
||||||
if (repository != null)
|
if (repository != null)
|
||||||
{
|
{
|
||||||
directory = handler.getDirectory(repository);
|
directory = handler.getDirectory(repository.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
return directory;
|
return directory;
|
||||||
|
|||||||
@@ -67,15 +67,10 @@ public class SvnRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
|||||||
@Mock
|
@Mock
|
||||||
private com.google.inject.Provider<RepositoryManager> repositoryManagerProvider;
|
private com.google.inject.Provider<RepositoryManager> repositoryManagerProvider;
|
||||||
|
|
||||||
@Mock
|
|
||||||
private RepositoryDAO repositoryDAO;
|
|
||||||
|
|
||||||
private HookContextFactory hookContextFactory = new HookContextFactory(mock(PreProcessorUtil.class));
|
private HookContextFactory hookContextFactory = new HookContextFactory(mock(PreProcessorUtil.class));
|
||||||
|
|
||||||
private HookEventFacade facade = new HookEventFacade(repositoryManagerProvider, hookContextFactory);
|
private HookEventFacade facade = new HookEventFacade(repositoryManagerProvider, hookContextFactory);
|
||||||
|
|
||||||
private RepositoryLocationResolver repositoryLocationResolver;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void checkDirectory(File directory) {
|
protected void checkDirectory(File directory) {
|
||||||
File format = new File(directory, "format");
|
File format = new File(directory, "format");
|
||||||
@@ -91,9 +86,9 @@ public class SvnRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory,
|
protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory,
|
||||||
|
RepositoryLocationResolver locationResolver,
|
||||||
File directory) {
|
File directory) {
|
||||||
repositoryLocationResolver = new RepositoryLocationResolver(repoDao, new InitialRepositoryLocationResolver(contextProvider));
|
SvnRepositoryHandler handler = new SvnRepositoryHandler(factory, null, locationResolver);
|
||||||
SvnRepositoryHandler handler = new SvnRepositoryHandler(factory, null, repositoryLocationResolver);
|
|
||||||
|
|
||||||
handler.init(contextProvider);
|
handler.init(contextProvider);
|
||||||
|
|
||||||
@@ -109,13 +104,13 @@ public class SvnRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
|||||||
public void getDirectory() {
|
public void getDirectory() {
|
||||||
when(factory.getStore(any())).thenReturn(store);
|
when(factory.getStore(any())).thenReturn(store);
|
||||||
SvnRepositoryHandler repositoryHandler = new SvnRepositoryHandler(factory,
|
SvnRepositoryHandler repositoryHandler = new SvnRepositoryHandler(factory,
|
||||||
facade, repositoryLocationResolver);
|
facade, locationResolver);
|
||||||
|
|
||||||
SvnConfig svnConfig = new SvnConfig();
|
SvnConfig svnConfig = new SvnConfig();
|
||||||
repositoryHandler.setConfig(svnConfig);
|
repositoryHandler.setConfig(svnConfig);
|
||||||
|
|
||||||
initRepository();
|
initRepository();
|
||||||
File path = repositoryHandler.getDirectory(repository);
|
File path = repositoryHandler.getDirectory(repository.getId());
|
||||||
assertEquals(repoPath.toString()+File.separator+ AbstractSimpleRepositoryHandler.REPOSITORIES_NATIVE_DIRECTORY, path.getAbsolutePath());
|
assertEquals(repoPath.toString()+File.separator+ AbstractSimpleRepositoryHandler.REPOSITORIES_NATIVE_DIRECTORY, path.getAbsolutePath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ public final class RepositoryTestData {
|
|||||||
.type(type)
|
.type(type)
|
||||||
.contact("douglas.adams@hitchhiker.com")
|
.contact("douglas.adams@hitchhiker.com")
|
||||||
.name("42Puzzle")
|
.name("42Puzzle")
|
||||||
|
.namespace("hitchhiker")
|
||||||
.description("The 42 Puzzle")
|
.description("The 42 Puzzle")
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
@@ -59,6 +60,7 @@ public final class RepositoryTestData {
|
|||||||
.type(type)
|
.type(type)
|
||||||
.contact("zaphod.beeblebrox@hitchhiker.com")
|
.contact("zaphod.beeblebrox@hitchhiker.com")
|
||||||
.name("happyVerticalPeopleTransporter")
|
.name("happyVerticalPeopleTransporter")
|
||||||
|
.namespace("hitchhiker")
|
||||||
.description("Happy Vertical People Transporter")
|
.description("Happy Vertical People Transporter")
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
@@ -72,6 +74,7 @@ public final class RepositoryTestData {
|
|||||||
.type(type)
|
.type(type)
|
||||||
.contact("zaphod.beeblebrox@hitchhiker.com")
|
.contact("zaphod.beeblebrox@hitchhiker.com")
|
||||||
.name("HeartOfGold")
|
.name("HeartOfGold")
|
||||||
|
.namespace("hitchhiker")
|
||||||
.description(
|
.description(
|
||||||
"Heart of Gold is the first prototype ship to successfully utilise the revolutionary Infinite Improbability Drive")
|
"Heart of Gold is the first prototype ship to successfully utilise the revolutionary Infinite Improbability Drive")
|
||||||
.build();
|
.build();
|
||||||
@@ -87,6 +90,7 @@ public final class RepositoryTestData {
|
|||||||
.type(type)
|
.type(type)
|
||||||
.contact("douglas.adams@hitchhiker.com")
|
.contact("douglas.adams@hitchhiker.com")
|
||||||
.name("RestaurantAtTheEndOfTheUniverse")
|
.name("RestaurantAtTheEndOfTheUniverse")
|
||||||
|
.namespace("hitchhiker")
|
||||||
.description("The Restaurant at the End of the Universe")
|
.description("The Restaurant at the End of the Universe")
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import java.io.IOException;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@@ -62,7 +63,7 @@ public abstract class SimpleRepositoryHandlerTestBase extends AbstractTestBase {
|
|||||||
protected abstract void checkDirectory(File directory);
|
protected abstract void checkDirectory(File directory);
|
||||||
|
|
||||||
protected abstract RepositoryHandler createRepositoryHandler(
|
protected abstract RepositoryHandler createRepositoryHandler(
|
||||||
ConfigurationStoreFactory factory, File directory) throws IOException, RepositoryPathNotFoundException;
|
ConfigurationStoreFactory factory, RepositoryLocationResolver locationResolver, File directory) throws IOException, RepositoryPathNotFoundException;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreate() {
|
public void testCreate() {
|
||||||
@@ -74,7 +75,15 @@ public abstract class SimpleRepositoryHandlerTestBase extends AbstractTestBase {
|
|||||||
InMemoryConfigurationStoreFactory storeFactory = new InMemoryConfigurationStoreFactory();
|
InMemoryConfigurationStoreFactory storeFactory = new InMemoryConfigurationStoreFactory();
|
||||||
baseDirectory = new File(contextProvider.getBaseDirectory(), "repositories");
|
baseDirectory = new File(contextProvider.getBaseDirectory(), "repositories");
|
||||||
IOUtil.mkdirs(baseDirectory);
|
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
|
@Override
|
||||||
@@ -98,11 +107,12 @@ public abstract class SimpleRepositoryHandlerTestBase extends AbstractTestBase {
|
|||||||
repository = RepositoryTestData.createHeartOfGold();
|
repository = RepositoryTestData.createHeartOfGold();
|
||||||
File repoDirectory = new File(baseDirectory, repository.getId());
|
File repoDirectory = new File(baseDirectory, repository.getId());
|
||||||
repoPath = repoDirectory.toPath();
|
repoPath = repoDirectory.toPath();
|
||||||
when(repoDao.getPath(repository)).thenReturn(repoPath);
|
when(repoDao.getPath(repository.getId())).thenReturn(repoPath);
|
||||||
return new File(repoDirectory, AbstractSimpleRepositoryHandler.REPOSITORIES_NATIVE_DIRECTORY);
|
return new File(repoDirectory, AbstractSimpleRepositoryHandler.REPOSITORIES_NATIVE_DIRECTORY);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected File baseDirectory;
|
protected File baseDirectory;
|
||||||
|
protected RepositoryLocationResolver locationResolver;
|
||||||
|
|
||||||
private RepositoryHandler handler;
|
private RepositoryHandler handler;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ import static org.mockito.Mockito.*;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -213,6 +214,10 @@ public final class MockUtil
|
|||||||
SCMContextProvider provider = mock(SCMContextProvider.class);
|
SCMContextProvider provider = mock(SCMContextProvider.class);
|
||||||
|
|
||||||
when(provider.getBaseDirectory()).thenReturn(directory);
|
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;
|
return provider;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@babel/polyfill": "^7.0.0",
|
||||||
"@fortawesome/fontawesome-free": "^5.3.1",
|
"@fortawesome/fontawesome-free": "^5.3.1",
|
||||||
"@scm-manager/ui-extensions": "^0.1.1",
|
"@scm-manager/ui-extensions": "^0.1.1",
|
||||||
"bulma": "^0.7.1",
|
"bulma": "^0.7.1",
|
||||||
@@ -31,17 +32,19 @@
|
|||||||
"redux": "^4.0.0",
|
"redux": "^4.0.0",
|
||||||
"redux-devtools-extension": "^2.13.5",
|
"redux-devtools-extension": "^2.13.5",
|
||||||
"redux-logger": "^3.0.6",
|
"redux-logger": "^3.0.6",
|
||||||
"redux-thunk": "^2.3.0"
|
"redux-thunk": "^2.3.0",
|
||||||
|
"whatwg-fetch": "^3.0.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"polyfills": "concat node_modules/@babel/polyfill/dist/polyfill.min.js node_modules/whatwg-fetch/dist/fetch.umd.js -o target/scm-ui/polyfills.bundle.js",
|
||||||
"webfonts": "copyfiles -f node_modules/@fortawesome/fontawesome-free/webfonts/* target/scm-ui/styles/webfonts",
|
"webfonts": "copyfiles -f node_modules/@fortawesome/fontawesome-free/webfonts/* target/scm-ui/styles/webfonts",
|
||||||
"build-css": "node-sass-chokidar --include-path ./styles --include-path ./node_modules styles/ -o target/scm-ui/styles",
|
"build-css": "node-sass-chokidar --include-path ./styles --include-path ./node_modules styles/ -o target/scm-ui/styles",
|
||||||
"watch-css": "npm run build-css && node-sass-chokidar --include-path ./styles --include-path ./node_modules styles/ -o target/scm-ui/styles --watch --recursive",
|
"watch-css": "npm run build-css && node-sass-chokidar --include-path ./styles --include-path ./node_modules styles/ -o target/scm-ui/styles --watch --recursive",
|
||||||
"start-js": "ui-bundler serve --target target/scm-ui --vendor vendor.bundle.js",
|
"start-js": "ui-bundler serve --target target/scm-ui --vendor vendor.bundle.js",
|
||||||
"start": "npm-run-all -p webfonts watch-css start-js",
|
"start": "npm-run-all -p webfonts watch-css polyfills start-js",
|
||||||
"build-js": "ui-bundler bundle --mode=production target/scm-ui/scm-ui.bundle.js",
|
"build-js": "ui-bundler bundle --mode=production target/scm-ui/scm-ui.bundle.js",
|
||||||
"build-vendor": "ui-bundler vendor --mode=production target/scm-ui/vendor.bundle.js",
|
"build-vendor": "ui-bundler vendor --mode=production target/scm-ui/vendor.bundle.js",
|
||||||
"build": "npm-run-all -s webfonts build-css build-vendor build-js",
|
"build": "npm-run-all -s webfonts build-css polyfills build-vendor build-js",
|
||||||
"test": "ui-bundler test",
|
"test": "ui-bundler test",
|
||||||
"test-ci": "ui-bundler test --ci",
|
"test-ci": "ui-bundler test --ci",
|
||||||
"flow": "flow",
|
"flow": "flow",
|
||||||
@@ -49,6 +52,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@scm-manager/ui-bundler": "^0.0.21",
|
"@scm-manager/ui-bundler": "^0.0.21",
|
||||||
|
"concat": "^1.0.3",
|
||||||
"copyfiles": "^2.0.0",
|
"copyfiles": "^2.0.0",
|
||||||
"enzyme": "^3.3.0",
|
"enzyme": "^3.3.0",
|
||||||
"enzyme-adapter-react-16": "^1.1.1",
|
"enzyme-adapter-react-16": "^1.1.1",
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
<script>
|
<script>
|
||||||
window.ctxPath = "{{ contextPath }}";
|
window.ctxPath = "{{ contextPath }}";
|
||||||
</script>
|
</script>
|
||||||
|
<script src="{{ contextPath }}/polyfills.bundle.js"></script>
|
||||||
<script src="{{ contextPath }}/vendor.bundle.js"></script>
|
<script src="{{ contextPath }}/vendor.bundle.js"></script>
|
||||||
<script src="{{ contextPath }}/scm-ui.bundle.js"></script>
|
<script src="{{ contextPath }}/scm-ui.bundle.js"></script>
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { withRouter } from "react-router-dom";
|
import { withRouter } from "react-router-dom";
|
||||||
import type { Repository, Branch } from "@scm-manager/ui-types";
|
import type { Branch, Repository } from "@scm-manager/ui-types";
|
||||||
import FileTree from "../components/FileTree";
|
import FileTree from "../components/FileTree";
|
||||||
import { ErrorNotification, Loading } from "@scm-manager/ui-components";
|
import { ErrorNotification, Loading } from "@scm-manager/ui-components";
|
||||||
import BranchSelector from "../../containers/BranchSelector";
|
import BranchSelector from "../../containers/BranchSelector";
|
||||||
@@ -109,9 +109,9 @@ class Sources extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderBranchSelector = () => {
|
renderBranchSelector = () => {
|
||||||
const { repository, branches, revision } = this.props;
|
const { branches, revision } = this.props;
|
||||||
|
|
||||||
if (repository._links.branches) {
|
if (branches) {
|
||||||
return (
|
return (
|
||||||
<BranchSelector
|
<BranchSelector
|
||||||
branches={branches}
|
branches={branches}
|
||||||
|
|||||||
@@ -513,6 +513,13 @@
|
|||||||
"@babel/helper-regex" "^7.0.0"
|
"@babel/helper-regex" "^7.0.0"
|
||||||
regexpu-core "^4.1.3"
|
regexpu-core "^4.1.3"
|
||||||
|
|
||||||
|
"@babel/polyfill@^7.0.0":
|
||||||
|
version "7.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.0.0.tgz#c8ff65c9ec3be6a1ba10113ebd40e8750fb90bff"
|
||||||
|
dependencies:
|
||||||
|
core-js "^2.5.7"
|
||||||
|
regenerator-runtime "^0.11.1"
|
||||||
|
|
||||||
"@babel/preset-env@^7.0.0":
|
"@babel/preset-env@^7.0.0":
|
||||||
version "7.1.0"
|
version "7.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.1.0.tgz#e67ea5b0441cfeab1d6f41e9b5c79798800e8d11"
|
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.1.0.tgz#e67ea5b0441cfeab1d6f41e9b5c79798800e8d11"
|
||||||
@@ -2005,6 +2012,12 @@ concat-stream@^1.6.0, concat-stream@^1.6.1, concat-stream@~1.6.0:
|
|||||||
readable-stream "^2.2.2"
|
readable-stream "^2.2.2"
|
||||||
typedarray "^0.0.6"
|
typedarray "^0.0.6"
|
||||||
|
|
||||||
|
concat@^1.0.3:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/concat/-/concat-1.0.3.tgz#40f3353089d65467695cb1886b45edd637d8cca8"
|
||||||
|
dependencies:
|
||||||
|
commander "^2.9.0"
|
||||||
|
|
||||||
connect-history-api-fallback@^1:
|
connect-history-api-fallback@^1:
|
||||||
version "1.5.0"
|
version "1.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz#b06873934bc5e344fef611a196a6faae0aee015a"
|
resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz#b06873934bc5e344fef611a196a6faae0aee015a"
|
||||||
@@ -2065,7 +2078,7 @@ copyfiles@^2.0.0:
|
|||||||
through2 "^2.0.1"
|
through2 "^2.0.1"
|
||||||
yargs "^11.0.0"
|
yargs "^11.0.0"
|
||||||
|
|
||||||
core-js@^2.4.0, core-js@^2.5.0:
|
core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.7:
|
||||||
version "2.5.7"
|
version "2.5.7"
|
||||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e"
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e"
|
||||||
|
|
||||||
@@ -2929,11 +2942,10 @@ event-emitter@^0.3.5:
|
|||||||
es5-ext "~0.10.14"
|
es5-ext "~0.10.14"
|
||||||
|
|
||||||
event-stream@~3.3.0:
|
event-stream@~3.3.0:
|
||||||
version "3.3.6"
|
version "3.3.5"
|
||||||
resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.6.tgz#cac1230890e07e73ec9cacd038f60a5b66173eef"
|
resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.5.tgz#e5dd8989543630d94c6cf4d657120341fa31636b"
|
||||||
dependencies:
|
dependencies:
|
||||||
duplexer "^0.1.1"
|
duplexer "^0.1.1"
|
||||||
flatmap-stream "^0.1.0"
|
|
||||||
from "^0.1.7"
|
from "^0.1.7"
|
||||||
map-stream "0.0.7"
|
map-stream "0.0.7"
|
||||||
pause-stream "^0.0.11"
|
pause-stream "^0.0.11"
|
||||||
@@ -3251,10 +3263,6 @@ flat-cache@^1.2.1:
|
|||||||
graceful-fs "^4.1.2"
|
graceful-fs "^4.1.2"
|
||||||
write "^0.2.1"
|
write "^0.2.1"
|
||||||
|
|
||||||
flatmap-stream@^0.1.0:
|
|
||||||
version "0.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/flatmap-stream/-/flatmap-stream-0.1.1.tgz#d34f39ef3b9aa5a2fc225016bd3adf28ac5ae6ea"
|
|
||||||
|
|
||||||
flow-bin@^0.79.1:
|
flow-bin@^0.79.1:
|
||||||
version "0.79.1"
|
version "0.79.1"
|
||||||
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.79.1.tgz#01c9f427baa6556753fa878c192d42e1ecb764b6"
|
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.79.1.tgz#01c9f427baa6556753fa878c192d42e1ecb764b6"
|
||||||
@@ -7056,7 +7064,7 @@ regenerator-runtime@^0.10.5:
|
|||||||
version "0.10.5"
|
version "0.10.5"
|
||||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658"
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658"
|
||||||
|
|
||||||
regenerator-runtime@^0.11.0:
|
regenerator-runtime@^0.11.0, regenerator-runtime@^0.11.1:
|
||||||
version "0.11.1"
|
version "0.11.1"
|
||||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
|
||||||
|
|
||||||
@@ -8530,6 +8538,10 @@ whatwg-fetch@^2.0.4:
|
|||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f"
|
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f"
|
||||||
|
|
||||||
|
whatwg-fetch@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb"
|
||||||
|
|
||||||
whatwg-mimetype@^2.1.0:
|
whatwg-mimetype@^2.1.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.2.0.tgz#a3d58ef10b76009b042d03e25591ece89b88d171"
|
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.2.0.tgz#a3d58ef10b76009b042d03e25591ece89b88d171"
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package sonia.scm.api.v2;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.ws.rs.container.ContainerRequestContext;
|
||||||
|
import javax.ws.rs.container.ContainerResponseContext;
|
||||||
|
import javax.ws.rs.container.ContainerResponseFilter;
|
||||||
|
import javax.ws.rs.ext.Provider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the Cache-Control: no-cache header to every api call. But only if non caching headers are set to the response.
|
||||||
|
* The Cache-Control header should fix stale resources on ie.
|
||||||
|
*/
|
||||||
|
@Provider
|
||||||
|
public class CacheControlResponseFilter implements ContainerResponseFilter {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(CacheControlResponseFilter.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) {
|
||||||
|
if (!isCacheable(responseContext)) {
|
||||||
|
LOG.trace("add no-cache header to response");
|
||||||
|
responseContext.getHeaders().add("Cache-Control", "no-cache");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCacheable(ContainerResponseContext responseContext) {
|
||||||
|
return hasLastModifiedDate(responseContext) || hasEntityTag(responseContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasEntityTag(ContainerResponseContext responseContext) {
|
||||||
|
return responseContext.getEntityTag() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasLastModifiedDate(ContainerResponseContext responseContext) {
|
||||||
|
return responseContext.getLastModified() != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package sonia.scm.api.v2;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import javax.ws.rs.container.ContainerRequestContext;
|
||||||
|
import javax.ws.rs.container.ContainerResponseContext;
|
||||||
|
import javax.ws.rs.core.EntityTag;
|
||||||
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class CacheControlResponseFilterTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ContainerRequestContext requestContext;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ContainerResponseContext responseContext;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private MultivaluedMap<String, Object> headers;
|
||||||
|
|
||||||
|
private CacheControlResponseFilter filter = new CacheControlResponseFilter();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUpMocks() {
|
||||||
|
when(responseContext.getHeaders()).thenReturn(headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void filterShouldAddCacheControlHeader() {
|
||||||
|
filter.filter(requestContext, responseContext);
|
||||||
|
|
||||||
|
verify(headers).add("Cache-Control", "no-cache");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void filterShouldNotSetHeaderIfLastModifiedIsNotNull() {
|
||||||
|
when(responseContext.getLastModified()).thenReturn(new Date());
|
||||||
|
|
||||||
|
filter.filter(requestContext, responseContext);
|
||||||
|
|
||||||
|
verify(headers, never()).add("Cache-Control", "no-cache");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void filterShouldNotSetHeaderIfEtagIsNotNull() {
|
||||||
|
when(responseContext.getEntityTag()).thenReturn(new EntityTag("42"));
|
||||||
|
|
||||||
|
filter.filter(requestContext, responseContext);
|
||||||
|
|
||||||
|
verify(headers, never()).add("Cache-Control", "no-cache");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -65,7 +65,6 @@ import sonia.scm.security.KeyGenerator;
|
|||||||
import sonia.scm.store.ConfigurationStoreFactory;
|
import sonia.scm.store.ConfigurationStoreFactory;
|
||||||
import sonia.scm.store.JAXBConfigurationStoreFactory;
|
import sonia.scm.store.JAXBConfigurationStoreFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -434,10 +433,10 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase<Repository> {
|
|||||||
private DefaultRepositoryManager createRepositoryManager(boolean archiveEnabled, KeyGenerator keyGenerator) {
|
private DefaultRepositoryManager createRepositoryManager(boolean archiveEnabled, KeyGenerator keyGenerator) {
|
||||||
DefaultFileSystem fileSystem = new DefaultFileSystem();
|
DefaultFileSystem fileSystem = new DefaultFileSystem();
|
||||||
Set<RepositoryHandler> handlerSet = new HashSet<>();
|
Set<RepositoryHandler> handlerSet = new HashSet<>();
|
||||||
InitialRepositoryLocationResolver initialRepositoryLocationResolver = new InitialRepositoryLocationResolver(contextProvider);
|
ConfigurationStoreFactory factory = new JAXBConfigurationStoreFactory(contextProvider);
|
||||||
XmlRepositoryDAO repositoryDAO = new XmlRepositoryDAO(initialRepositoryLocationResolver, fileSystem, contextProvider);
|
InitialRepositoryLocationResolver initialRepositoryLocationResolver = new InitialRepositoryLocationResolver();
|
||||||
RepositoryLocationResolver repositoryLocationResolver = new RepositoryLocationResolver(repositoryDAO, initialRepositoryLocationResolver);
|
XmlRepositoryDAO repositoryDAO = new XmlRepositoryDAO(contextProvider, initialRepositoryLocationResolver, fileSystem);
|
||||||
ConfigurationStoreFactory factory = new JAXBConfigurationStoreFactory(contextProvider, repositoryLocationResolver);
|
RepositoryLocationResolver repositoryLocationResolver = new RepositoryLocationResolver(contextProvider, repositoryDAO, initialRepositoryLocationResolver);
|
||||||
handlerSet.add(new DummyRepositoryHandler(factory, repositoryLocationResolver));
|
handlerSet.add(new DummyRepositoryHandler(factory, repositoryLocationResolver));
|
||||||
handlerSet.add(new DummyRepositoryHandler(factory, repositoryLocationResolver) {
|
handlerSet.add(new DummyRepositoryHandler(factory, repositoryLocationResolver) {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user