mirror of
				https://github.com/scm-manager/scm-manager.git
				synced 2025-11-03 20:15:52 +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 */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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,10 +1,10 @@
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * 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,
 | 
				
			||||||
@@ -13,7 +13,7 @@
 | 
				
			|||||||
 * 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);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  //~--- get methods ----------------------------------------------------------
 | 
					    pathDatabase.read(this::loadDates, this::loadRepository);
 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  public Repository get(NamespaceAndName namespaceAndName) {
 | 
					 | 
				
			||||||
    return db.get(namespaceAndName);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  //~--- methods --------------------------------------------------------------
 | 
					  private void loadDates(Long creationTime, Long lastModified) {
 | 
				
			||||||
 | 
					    this.creationTime = creationTime;
 | 
				
			||||||
 | 
					    this.lastModified = lastModified;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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
 | 
					 | 
				
			||||||
  protected XmlRepositoryDatabase createNewDatabase() {
 | 
					 | 
				
			||||||
    return new XmlRepositoryDatabase();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  public Path getPath(Repository repository) {
 | 
					  public Path getPath(String repositoryId) {
 | 
				
			||||||
    return context
 | 
					    return pathById.get(repositoryId);
 | 
				
			||||||
      .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