mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-03-04 19:30:51 +01:00
merge with develop
This commit is contained in:
@@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## Unreleased
|
||||
### Added
|
||||
- Detect renamed files in git and hg diffs ([#1157](https://github.com/scm-manager/scm-manager/pull/1157))
|
||||
- ClassLoader and Adapter parameters to typed store apis ([#1111](https://github.com/scm-manager/scm-manager/pull/1111))
|
||||
- Native packaging for Debian, Red Hat, Windows, Unix, Docker and Kubernetes ([#1165](https://github.com/scm-manager/scm-manager/pull/1165))
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>sonia.scm</groupId>
|
||||
<artifactId>scm-core</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<name>scm-core</name>
|
||||
|
||||
@@ -21,10 +21,8 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.store;
|
||||
|
||||
import sonia.scm.repository.Repository;
|
||||
package sonia.scm.store;
|
||||
|
||||
/**
|
||||
* The BlobStoreFactory can be used to create a new or get an existing {@link BlobStore}s.
|
||||
@@ -46,7 +44,7 @@ import sonia.scm.repository.Repository;
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.23
|
||||
*
|
||||
*
|
||||
* @apiviz.landmark
|
||||
* @apiviz.uses sonia.scm.store.BlobStore
|
||||
*/
|
||||
@@ -66,66 +64,7 @@ public interface BlobStoreFactory {
|
||||
* @param name The name for the {@link BlobStore}.
|
||||
* @return Floating API to either specify a repository or directly build a global {@link BlobStore}.
|
||||
*/
|
||||
default FloatingStoreParameters.Builder withName(String name) {
|
||||
return new FloatingStoreParameters(this).new Builder(name);
|
||||
}
|
||||
}
|
||||
|
||||
final class FloatingStoreParameters implements StoreParameters {
|
||||
|
||||
private String name;
|
||||
private String repositoryId;
|
||||
|
||||
private final BlobStoreFactory factory;
|
||||
|
||||
FloatingStoreParameters(BlobStoreFactory factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRepositoryId() {
|
||||
return repositoryId;
|
||||
}
|
||||
|
||||
public class Builder {
|
||||
|
||||
Builder(String name) {
|
||||
FloatingStoreParameters.this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to create or get a {@link BlobStore} for a specific repository. This step is optional. If you want to
|
||||
* have a global {@link BlobStore}, omit this.
|
||||
* @param repository The optional repository for the {@link BlobStore}.
|
||||
* @return Floating API to finish the call.
|
||||
*/
|
||||
public FloatingStoreParameters.Builder forRepository(Repository repository) {
|
||||
FloatingStoreParameters.this.repositoryId = repository.getId();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to create or get a {@link BlobStore} for a specific repository. This step is optional. If you want to
|
||||
* have a global {@link BlobStore}, omit this.
|
||||
* @param repositoryId The id of the optional repository for the {@link BlobStore}.
|
||||
* @return Floating API to finish the call.
|
||||
*/
|
||||
public FloatingStoreParameters.Builder forRepository(String repositoryId) {
|
||||
FloatingStoreParameters.this.repositoryId = repositoryId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or gets the {@link BlobStore} with the given name and (if specified) the given repository. If no
|
||||
* repository is given, the {@link BlobStore} will be global.
|
||||
*/
|
||||
public BlobStore build(){
|
||||
return factory.getStore(FloatingStoreParameters.this);
|
||||
}
|
||||
default StoreParametersBuilder<BlobStore> withName(String name) {
|
||||
return new StoreParametersBuilder<>(name, this::getStore);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,20 +21,15 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.store;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* A ConfigurationEntryStore can be used to store multiple entries of structured
|
||||
* configuration data. <b>Note:</b> the default implementation use JAXB to
|
||||
* A ConfigurationEntryStore can be used to store multiple entries of structured
|
||||
* configuration data. <b>Note:</b> the default implementation use JAXB to
|
||||
* marshall the items.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
@@ -52,5 +47,5 @@ public interface ConfigurationEntryStore<V> extends DataStore<V> {
|
||||
*
|
||||
* @return filtered collection of values
|
||||
*/
|
||||
public Collection<V> getMatchingValues(Predicate<V> predicate);
|
||||
Collection<V> getMatchingValues(Predicate<V> predicate);
|
||||
}
|
||||
|
||||
@@ -21,10 +21,8 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.store;
|
||||
|
||||
import sonia.scm.repository.Repository;
|
||||
package sonia.scm.store;
|
||||
|
||||
/**
|
||||
* The ConfigurationEntryStoreFactory can be used to create new or get existing {@link ConfigurationEntryStore}s.
|
||||
@@ -51,7 +49,7 @@ import sonia.scm.repository.Repository;
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.31
|
||||
*
|
||||
*
|
||||
* @apiviz.landmark
|
||||
* @apiviz.uses sonia.scm.store.ConfigurationEntryStore
|
||||
*/
|
||||
@@ -72,67 +70,7 @@ public interface ConfigurationEntryStoreFactory {
|
||||
* @return Floating API to set the name and either specify a repository or directly build a global
|
||||
* {@link ConfigurationEntryStore}.
|
||||
*/
|
||||
default <T> TypedFloatingConfigurationEntryStoreParameters<T>.Builder withType(Class<T> type) {
|
||||
return new TypedFloatingConfigurationEntryStoreParameters<T>(this).new Builder(type);
|
||||
}
|
||||
}
|
||||
|
||||
final class TypedFloatingConfigurationEntryStoreParameters<T> {
|
||||
|
||||
private final TypedStoreParametersImpl<T> parameters = new TypedStoreParametersImpl<>();
|
||||
private final ConfigurationEntryStoreFactory factory;
|
||||
|
||||
TypedFloatingConfigurationEntryStoreParameters(ConfigurationEntryStoreFactory factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public class Builder {
|
||||
|
||||
Builder(Class<T> type) {
|
||||
parameters.setType(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to set the name for the {@link ConfigurationEntryStore}.
|
||||
* @param name The name for the {@link ConfigurationEntryStore}.
|
||||
* @return Floating API to either specify a repository or directly build a global {@link ConfigurationEntryStore}.
|
||||
*/
|
||||
public OptionalRepositoryBuilder withName(String name) {
|
||||
parameters.setName(name);
|
||||
return new OptionalRepositoryBuilder();
|
||||
}
|
||||
}
|
||||
|
||||
public class OptionalRepositoryBuilder {
|
||||
|
||||
/**
|
||||
* Use this to create or get a {@link ConfigurationEntryStore} for a specific repository. This step is optional. If
|
||||
* you want to have a global {@link ConfigurationEntryStore}, omit this.
|
||||
* @param repository The optional repository for the {@link ConfigurationEntryStore}.
|
||||
* @return Floating API to finish the call.
|
||||
*/
|
||||
public OptionalRepositoryBuilder forRepository(Repository repository) {
|
||||
parameters.setRepositoryId(repository.getId());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to create or get a {@link ConfigurationEntryStore} for a specific repository. This step is optional. If
|
||||
* you want to have a global {@link ConfigurationEntryStore}, omit this.
|
||||
* @param repositoryId The id of the optional repository for the {@link ConfigurationEntryStore}.
|
||||
* @return Floating API to finish the call.
|
||||
*/
|
||||
public OptionalRepositoryBuilder forRepository(String repositoryId) {
|
||||
parameters.setRepositoryId(repositoryId);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or gets the {@link ConfigurationEntryStore} with the given name and (if specified) the given repository.
|
||||
* If no repository is given, the {@link ConfigurationEntryStore} will be global.
|
||||
*/
|
||||
public ConfigurationEntryStore<T> build(){
|
||||
return factory.getStore(parameters);
|
||||
}
|
||||
default <T> TypedStoreParametersBuilder<T, ConfigurationEntryStore<T>> withType(Class<T> type) {
|
||||
return new TypedStoreParametersBuilder<>(type, this::getStore);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,12 +21,10 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.store;
|
||||
|
||||
|
||||
import sonia.scm.repository.Repository;
|
||||
|
||||
/**
|
||||
* The ConfigurationStoreFactory can be used to create new or get existing {@link ConfigurationStore} objects.
|
||||
* <br>
|
||||
@@ -51,7 +49,7 @@ import sonia.scm.repository.Repository;
|
||||
* </pre></code>
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*
|
||||
*
|
||||
* @apiviz.landmark
|
||||
* @apiviz.uses sonia.scm.store.ConfigurationStore
|
||||
*/
|
||||
@@ -72,67 +70,7 @@ public interface ConfigurationStoreFactory {
|
||||
* @return Floating API to set the name and either specify a repository or directly build a global
|
||||
* {@link ConfigurationStore}.
|
||||
*/
|
||||
default <T> TypedFloatingConfigurationStoreParameters<T>.Builder withType(Class<T> type) {
|
||||
return new TypedFloatingConfigurationStoreParameters<T>(this).new Builder(type);
|
||||
}
|
||||
}
|
||||
|
||||
final class TypedFloatingConfigurationStoreParameters<T> {
|
||||
|
||||
private final TypedStoreParametersImpl<T> parameters = new TypedStoreParametersImpl<>();
|
||||
private final ConfigurationStoreFactory factory;
|
||||
|
||||
TypedFloatingConfigurationStoreParameters(ConfigurationStoreFactory factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public class Builder {
|
||||
|
||||
Builder(Class<T> type) {
|
||||
parameters.setType(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to set the name for the {@link ConfigurationStore}.
|
||||
* @param name The name for the {@link ConfigurationStore}.
|
||||
* @return Floating API to either specify a repository or directly build a global {@link ConfigurationStore}.
|
||||
*/
|
||||
public OptionalRepositoryBuilder withName(String name) {
|
||||
parameters.setName(name);
|
||||
return new OptionalRepositoryBuilder();
|
||||
}
|
||||
}
|
||||
|
||||
public class OptionalRepositoryBuilder {
|
||||
|
||||
/**
|
||||
* Use this to create or get a {@link ConfigurationStore} for a specific repository. This step is optional. If you
|
||||
* want to have a global {@link ConfigurationStore}, omit this.
|
||||
* @param repository The optional repository for the {@link ConfigurationStore}.
|
||||
* @return Floating API to finish the call.
|
||||
*/
|
||||
public OptionalRepositoryBuilder forRepository(Repository repository) {
|
||||
parameters.setRepositoryId(repository.getId());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to create or get a {@link ConfigurationStore} for a specific repository. This step is optional. If you
|
||||
* want to have a global {@link ConfigurationStore}, omit this.
|
||||
* @param repositoryId The id of the optional repository for the {@link ConfigurationStore}.
|
||||
* @return Floating API to finish the call.
|
||||
*/
|
||||
public OptionalRepositoryBuilder forRepository(String repositoryId) {
|
||||
parameters.setRepositoryId(repositoryId);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or gets the {@link ConfigurationStore} with the given name and (if specified) the given repository. If no
|
||||
* repository is given, the {@link ConfigurationStore} will be global.
|
||||
*/
|
||||
public ConfigurationStore<T> build(){
|
||||
return factory.getStore(parameters);
|
||||
}
|
||||
default <T> TypedStoreParametersBuilder<T,ConfigurationStore<T>> withType(Class<T> type) {
|
||||
return new TypedStoreParametersBuilder<>(type, this::getStore);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,8 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.store;
|
||||
|
||||
import sonia.scm.repository.Repository;
|
||||
package sonia.scm.store;
|
||||
|
||||
/**
|
||||
* The DataStoreFactory can be used to create new or get existing {@link DataStore}s.
|
||||
@@ -48,7 +46,7 @@ import sonia.scm.repository.Repository;
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.23
|
||||
*
|
||||
*
|
||||
* @apiviz.landmark
|
||||
* @apiviz.uses sonia.scm.store.DataStore
|
||||
*/
|
||||
@@ -69,67 +67,7 @@ public interface DataStoreFactory {
|
||||
* @return Floating API to set the name and either specify a repository or directly build a global
|
||||
* {@link DataStore}.
|
||||
*/
|
||||
default <T> TypedFloatingDataStoreParameters<T>.Builder withType(Class<T> type) {
|
||||
return new TypedFloatingDataStoreParameters<T>(this).new Builder(type);
|
||||
}
|
||||
}
|
||||
|
||||
final class TypedFloatingDataStoreParameters<T> {
|
||||
|
||||
private final TypedStoreParametersImpl<T> parameters = new TypedStoreParametersImpl<>();
|
||||
private final DataStoreFactory factory;
|
||||
|
||||
TypedFloatingDataStoreParameters(DataStoreFactory factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public class Builder {
|
||||
|
||||
Builder(Class<T> type) {
|
||||
parameters.setType(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to set the name for the {@link DataStore}.
|
||||
* @param name The name for the {@link DataStore}.
|
||||
* @return Floating API to either specify a repository or directly build a global {@link DataStore}.
|
||||
*/
|
||||
public OptionalRepositoryBuilder withName(String name) {
|
||||
parameters.setName(name);
|
||||
return new OptionalRepositoryBuilder();
|
||||
}
|
||||
}
|
||||
|
||||
public class OptionalRepositoryBuilder {
|
||||
|
||||
/**
|
||||
* Use this to create or get a {@link DataStore} for a specific repository. This step is optional. If you
|
||||
* want to have a global {@link DataStore}, omit this.
|
||||
* @param repository The optional repository for the {@link DataStore}.
|
||||
* @return Floating API to finish the call.
|
||||
*/
|
||||
public OptionalRepositoryBuilder forRepository(Repository repository) {
|
||||
parameters.setRepositoryId(repository.getId());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to create or get a {@link DataStore} for a specific repository. This step is optional. If you
|
||||
* want to have a global {@link DataStore}, omit this.
|
||||
* @param repositoryId The id of the optional repository for the {@link DataStore}.
|
||||
* @return Floating API to finish the call.
|
||||
*/
|
||||
public OptionalRepositoryBuilder forRepository(String repositoryId) {
|
||||
parameters.setRepositoryId(repositoryId);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or gets the {@link DataStore} with the given name and (if specified) the given repository. If no
|
||||
* repository is given, the {@link DataStore} will be global.
|
||||
*/
|
||||
public DataStore<T> build(){
|
||||
return factory.getStore(parameters);
|
||||
}
|
||||
default <T> TypedStoreParametersBuilder<T, DataStore<T>> withType(Class<T> type) {
|
||||
return new TypedStoreParametersBuilder<>(type, this::getStore);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,8 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.store;
|
||||
|
||||
import sonia.scm.repository.Repository;
|
||||
package sonia.scm.store;
|
||||
|
||||
/**
|
||||
* The fields of the {@link StoreParameters} are used from the {@link BlobStoreFactory} to create a store.
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.store;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import sonia.scm.repository.Repository;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Builder for {@link StoreParameters}.
|
||||
*
|
||||
* @param <S> type of store
|
||||
*/
|
||||
public final class StoreParametersBuilder<S> {
|
||||
|
||||
private final StoreParametersImpl parameters;
|
||||
private final Function<StoreParameters, S> factory;
|
||||
|
||||
StoreParametersBuilder(String name, Function<StoreParameters, S> factory) {
|
||||
this.parameters = new StoreParametersImpl(name);
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter(AccessLevel.PRIVATE)
|
||||
@RequiredArgsConstructor
|
||||
private static class StoreParametersImpl implements StoreParameters {
|
||||
private final String name;
|
||||
private String repositoryId;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Use this to create or get a store for a specific repository. This step is optional. If you want to
|
||||
* have a global store, omit this.
|
||||
*
|
||||
* @param repository The optional repository for the store.
|
||||
* @return Floating API to finish the call.
|
||||
*/
|
||||
public StoreParametersBuilder<S> forRepository(Repository repository) {
|
||||
parameters.repositoryId = repository.getId();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to create or get a store for a specific repository. This step is optional. If you want to
|
||||
* have a global store, omit this.
|
||||
*
|
||||
* @param repositoryId The id of the optional repository for the store.
|
||||
* @return Floating API to finish the call.
|
||||
*/
|
||||
public StoreParametersBuilder<S> forRepository(String repositoryId) {
|
||||
parameters.repositoryId = repositoryId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or gets the store with the given name and (if specified) the given repository. If no
|
||||
* repository is given, the store will be global.
|
||||
*/
|
||||
public S build() {
|
||||
return factory.apply(parameters);
|
||||
}
|
||||
}
|
||||
@@ -21,10 +21,12 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.store;
|
||||
|
||||
import sonia.scm.repository.Repository;
|
||||
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The fields of the {@link TypedStoreParameters} are used from the {@link ConfigurationStoreFactory},
|
||||
@@ -33,11 +35,13 @@ import sonia.scm.repository.Repository;
|
||||
* @author Mohamed Karray
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public interface TypedStoreParameters<T> {
|
||||
public interface TypedStoreParameters<T> extends StoreParameters {
|
||||
|
||||
Class<T> getType();
|
||||
|
||||
String getName();
|
||||
Optional<ClassLoader> getClassLoader();
|
||||
|
||||
@SuppressWarnings("java:S1452") // we could not provide generic type, because we don't know it here
|
||||
Set<XmlAdapter<?,?>> getAdapters();
|
||||
|
||||
String getRepositoryId();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.store;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import sonia.scm.plugin.PluginLoader;
|
||||
import sonia.scm.repository.Repository;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Builder for {@link TypedStoreParameters}.
|
||||
*
|
||||
* @param <T> type of data
|
||||
* @param <S> type of store
|
||||
*/
|
||||
public final class TypedStoreParametersBuilder<T,S> {
|
||||
|
||||
private final TypedStoreParametersImpl<T> parameters;
|
||||
private final Function<TypedStoreParameters<T>, S> factory;
|
||||
|
||||
TypedStoreParametersBuilder(Class<T> type, Function<TypedStoreParameters<T>, S> factory) {
|
||||
this.factory = factory;
|
||||
this.parameters = new TypedStoreParametersImpl<>(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to set the name for the store.
|
||||
* @param name The name for the store.
|
||||
* @return Floating API to either specify a repository or directly build a global store.
|
||||
*/
|
||||
public OptionalRepositoryBuilder withName(String name) {
|
||||
parameters.setName(name);
|
||||
return new OptionalRepositoryBuilder();
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter(AccessLevel.PRIVATE)
|
||||
@RequiredArgsConstructor
|
||||
private static class TypedStoreParametersImpl<T> implements TypedStoreParameters<T> {
|
||||
private final Class<T> type;
|
||||
private String name;
|
||||
private String repositoryId;
|
||||
private ClassLoader classLoader;
|
||||
private Set<XmlAdapter<?, ?>> adapters;
|
||||
|
||||
public Optional<ClassLoader> getClassLoader() {
|
||||
return Optional.ofNullable(classLoader);
|
||||
}
|
||||
}
|
||||
|
||||
public class OptionalRepositoryBuilder {
|
||||
|
||||
private final Set<XmlAdapter<?,?>> adapters = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Use this to create or get a store for a specific repository. This step is optional. If you
|
||||
* want to have a global store, omit this.
|
||||
* @param repository The optional repository for the store.
|
||||
* @return Floating API to finish the call.
|
||||
*/
|
||||
public OptionalRepositoryBuilder forRepository(Repository repository) {
|
||||
parameters.setRepositoryId(repository.getId());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to create or get a store for a specific repository. This step is optional. If you
|
||||
* want to have a global store, omit this.
|
||||
* @param repositoryId The id of the optional repository for the store.
|
||||
* @return Floating API to finish the call.
|
||||
*/
|
||||
public OptionalRepositoryBuilder forRepository(String repositoryId) {
|
||||
parameters.setRepositoryId(repositoryId);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link ClassLoader} which is used as context class loader during marshaling and unmarshalling.
|
||||
* This is especially useful for storing class objects which come from an unknown source, in this case the
|
||||
* UberClassLoader ({@link PluginLoader#getUberClassLoader()} could be used for the store.
|
||||
*
|
||||
* @param classLoader classLoader for the context
|
||||
*
|
||||
* @return {@code this}
|
||||
*/
|
||||
public OptionalRepositoryBuilder withClassLoader(ClassLoader classLoader) {
|
||||
parameters.setClassLoader(classLoader);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an instance of an {@link XmlAdapter}.
|
||||
*
|
||||
* @param adapter adapter
|
||||
* @return {@code this}
|
||||
*/
|
||||
public OptionalRepositoryBuilder withAdapter(XmlAdapter<?, ?> adapter) {
|
||||
adapters.add(adapter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or gets the store with the given name and (if specified) the given repository. If no
|
||||
* repository is given, the store will be global.
|
||||
*/
|
||||
public S build(){
|
||||
parameters.setAdapters(ImmutableSet.copyOf(adapters));
|
||||
return factory.apply(parameters);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.store;
|
||||
|
||||
class TypedStoreParametersImpl<T> implements TypedStoreParameters<T> {
|
||||
private Class<T> type;
|
||||
private String name;
|
||||
private String repositoryId;
|
||||
|
||||
@Override
|
||||
public Class<T> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
void setType(Class<T> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRepositoryId() {
|
||||
return repositoryId;
|
||||
}
|
||||
|
||||
void setRepositoryId(String repositoryId) {
|
||||
this.repositoryId = repositoryId;
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,6 @@
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>sonia.scm</groupId>
|
||||
<artifactId>scm-dao-xml</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<name>scm-dao-xml</name>
|
||||
|
||||
@@ -21,12 +21,9 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.store;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.slf4j.Logger;
|
||||
@@ -35,11 +32,8 @@ import sonia.scm.security.KeyGenerator;
|
||||
import sonia.scm.xml.IndentXMLStreamWriter;
|
||||
import sonia.scm.xml.XmlStreams;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBElement;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.stream.XMLStreamReader;
|
||||
import java.io.File;
|
||||
@@ -47,98 +41,52 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
import static javax.xml.stream.XMLStreamConstants.END_ELEMENT;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*
|
||||
* @param <V>
|
||||
*/
|
||||
public class JAXBConfigurationEntryStore<V> implements ConfigurationEntryStore<V>
|
||||
{
|
||||
public class JAXBConfigurationEntryStore<V> implements ConfigurationEntryStore<V> {
|
||||
|
||||
/** Field description */
|
||||
private static final String TAG_CONFIGURATION = "configuration";
|
||||
|
||||
/** Field description */
|
||||
private static final String TAG_ENTRY = "entry";
|
||||
|
||||
/** Field description */
|
||||
private static final String TAG_KEY = "key";
|
||||
|
||||
/** Field description */
|
||||
private static final String TAG_VALUE = "value";
|
||||
|
||||
/**
|
||||
* the logger for JAXBConfigurationEntryStore
|
||||
*/
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(JAXBConfigurationEntryStore.class);
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JAXBConfigurationEntryStore.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param keyGenerator
|
||||
* @param file
|
||||
* @param type
|
||||
*/
|
||||
JAXBConfigurationEntryStore(File file, KeyGenerator keyGenerator,
|
||||
Class<V> type)
|
||||
{
|
||||
private final File file;
|
||||
private final KeyGenerator keyGenerator;
|
||||
private final Class<V> type;
|
||||
private final TypedStoreContext<V> context;
|
||||
private final Map<String, V> entries = Maps.newHashMap();
|
||||
|
||||
JAXBConfigurationEntryStore(File file, KeyGenerator keyGenerator, Class<V> type, TypedStoreContext<V> context) {
|
||||
this.file = file;
|
||||
this.keyGenerator = keyGenerator;
|
||||
this.type = type;
|
||||
|
||||
try
|
||||
{
|
||||
this.context = JAXBContext.newInstance(type);
|
||||
|
||||
if (file.exists())
|
||||
{
|
||||
load();
|
||||
}
|
||||
}
|
||||
catch (JAXBException ex)
|
||||
{
|
||||
throw new StoreException("could not create jaxb context", ex);
|
||||
this.context = context;
|
||||
// initial load
|
||||
if (file.exists()) {
|
||||
load();
|
||||
}
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void clear()
|
||||
{
|
||||
logger.debug("clear configuration store");
|
||||
public void clear() {
|
||||
LOG.debug("clear configuration store");
|
||||
|
||||
synchronized (file)
|
||||
{
|
||||
synchronized (file) {
|
||||
entries.clear();
|
||||
store();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param item
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String put(V item)
|
||||
{
|
||||
public String put(V item) {
|
||||
String id = keyGenerator.createKey();
|
||||
|
||||
put(id, item);
|
||||
@@ -146,225 +94,138 @@ public class JAXBConfigurationEntryStore<V> implements ConfigurationEntryStore<V
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param id
|
||||
* @param item
|
||||
*/
|
||||
@Override
|
||||
public void put(String id, V item)
|
||||
{
|
||||
logger.debug("put item {} to configuration store", id);
|
||||
public void put(String id, V item) {
|
||||
LOG.debug("put item {} to configuration store", id);
|
||||
|
||||
synchronized (file)
|
||||
{
|
||||
synchronized (file) {
|
||||
entries.put(id, item);
|
||||
store();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param id
|
||||
*/
|
||||
@Override
|
||||
public void remove(String id)
|
||||
{
|
||||
logger.debug("remove item {} from configuration store", id);
|
||||
public void remove(String id) {
|
||||
LOG.debug("remove item {} from configuration store", id);
|
||||
|
||||
synchronized (file)
|
||||
{
|
||||
synchronized (file) {
|
||||
entries.remove(id);
|
||||
store();
|
||||
}
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param id
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public V get(String id)
|
||||
{
|
||||
logger.trace("get item {} from configuration store", id);
|
||||
public V get(String id) {
|
||||
LOG.trace("get item {} from configuration store", id);
|
||||
|
||||
return entries.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Map<String, V> getAll()
|
||||
{
|
||||
logger.trace("get all items from configuration store");
|
||||
public Map<String, V> getAll() {
|
||||
LOG.trace("get all items from configuration store");
|
||||
|
||||
return Collections.unmodifiableMap(entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param predicate
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Collection<V> getMatchingValues(Predicate<V> predicate)
|
||||
{
|
||||
return Collections2.filter(entries.values(), predicate);
|
||||
public Collection<V> getMatchingValues(Predicate<V> predicate) {
|
||||
return Collections2.filter(entries.values(), predicate::test);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
private void load() {
|
||||
LOG.debug("load configuration from {}", file);
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private void load()
|
||||
{
|
||||
logger.debug("load configuration from {}", file);
|
||||
context.withUnmarshaller(u -> {
|
||||
XMLStreamReader reader = null;
|
||||
try {
|
||||
reader = XmlStreams.createReader(file);
|
||||
|
||||
XMLStreamReader reader = null;
|
||||
|
||||
try
|
||||
{
|
||||
Unmarshaller u = context.createUnmarshaller();
|
||||
|
||||
reader = XmlStreams.createReader(file);
|
||||
|
||||
// configuration
|
||||
reader.nextTag();
|
||||
|
||||
// entry start
|
||||
reader.nextTag();
|
||||
|
||||
while (reader.isStartElement() && reader.getLocalName().equals(TAG_ENTRY))
|
||||
{
|
||||
|
||||
// read key
|
||||
// configuration
|
||||
reader.nextTag();
|
||||
|
||||
String key = reader.getElementText();
|
||||
|
||||
// read value
|
||||
// entry start
|
||||
reader.nextTag();
|
||||
|
||||
JAXBElement<V> element = u.unmarshal(reader, type);
|
||||
while (reader.isStartElement() && reader.getLocalName().equals(TAG_ENTRY)) {
|
||||
|
||||
if (!element.isNil())
|
||||
{
|
||||
V v = element.getValue();
|
||||
|
||||
logger.trace("add element {} to configuration entry store", v);
|
||||
|
||||
entries.put(key, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.warn("could not unmarshall object of entry store");
|
||||
}
|
||||
|
||||
// closed or new entry tag
|
||||
if (reader.nextTag() == XMLStreamReader.END_ELEMENT)
|
||||
{
|
||||
|
||||
// fixed format, start new entry
|
||||
// read key
|
||||
reader.nextTag();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new StoreException("could not load configuration", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
XmlStreams.close(reader);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
private void store()
|
||||
{
|
||||
logger.debug("store configuration to {}", file);
|
||||
String key = reader.getElementText();
|
||||
|
||||
CopyOnWrite.withTemporaryFile(
|
||||
temp -> {
|
||||
try (IndentXMLStreamWriter writer = XmlStreams.createWriter(temp)) {
|
||||
writer.writeStartDocument();
|
||||
// read value
|
||||
reader.nextTag();
|
||||
|
||||
// configuration start
|
||||
writer.writeStartElement(TAG_CONFIGURATION);
|
||||
JAXBElement<V> element = u.unmarshal(reader, type);
|
||||
|
||||
Marshaller m = context.createMarshaller();
|
||||
if (!element.isNil()) {
|
||||
V v = element.getValue();
|
||||
|
||||
m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
|
||||
LOG.trace("add element {} to configuration entry store", v);
|
||||
|
||||
for (Entry<String, V> e : entries.entrySet()) {
|
||||
|
||||
// entry start
|
||||
writer.writeStartElement(TAG_ENTRY);
|
||||
|
||||
// key start
|
||||
writer.writeStartElement(TAG_KEY);
|
||||
writer.writeCharacters(e.getKey());
|
||||
|
||||
// key end
|
||||
writer.writeEndElement();
|
||||
|
||||
// value
|
||||
JAXBElement<V> je = new JAXBElement<>(QName.valueOf(TAG_VALUE), type,
|
||||
e.getValue());
|
||||
|
||||
m.marshal(je, writer);
|
||||
|
||||
// entry end
|
||||
writer.writeEndElement();
|
||||
entries.put(key, v);
|
||||
} else {
|
||||
LOG.warn("could not unmarshall object of entry store");
|
||||
}
|
||||
|
||||
// configuration end
|
||||
writer.writeEndElement();
|
||||
writer.writeEndDocument();
|
||||
// closed or new entry tag
|
||||
if (reader.nextTag() == END_ELEMENT) {
|
||||
|
||||
// fixed format, start new entry
|
||||
reader.nextTag();
|
||||
}
|
||||
}
|
||||
},
|
||||
file.toPath()
|
||||
);
|
||||
} finally {
|
||||
XmlStreams.close(reader);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
private void store() {
|
||||
LOG.debug("store configuration to {}", file);
|
||||
|
||||
/** Field description */
|
||||
private final File file;
|
||||
context.withMarshaller(m -> {
|
||||
m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
|
||||
|
||||
/** Field description */
|
||||
private JAXBContext context;
|
||||
CopyOnWrite.withTemporaryFile(
|
||||
temp -> {
|
||||
try (IndentXMLStreamWriter writer = XmlStreams.createWriter(temp)) {
|
||||
writer.writeStartDocument();
|
||||
|
||||
/** Field description */
|
||||
private Map<String, V> entries = Maps.newHashMap();
|
||||
// configuration start
|
||||
writer.writeStartElement(TAG_CONFIGURATION);
|
||||
|
||||
/** Field description */
|
||||
private KeyGenerator keyGenerator;
|
||||
|
||||
/** Field description */
|
||||
private Class<V> type;
|
||||
for (Entry<String, V> e : entries.entrySet()) {
|
||||
|
||||
// entry start
|
||||
writer.writeStartElement(TAG_ENTRY);
|
||||
|
||||
// key start
|
||||
writer.writeStartElement(TAG_KEY);
|
||||
writer.writeCharacters(e.getKey());
|
||||
|
||||
// key end
|
||||
writer.writeEndElement();
|
||||
|
||||
// value
|
||||
JAXBElement<V> je = new JAXBElement<>(QName.valueOf(TAG_VALUE), type,
|
||||
e.getValue());
|
||||
|
||||
m.marshal(je, writer);
|
||||
|
||||
// entry end
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
||||
// configuration end
|
||||
writer.writeEndElement();
|
||||
writer.writeEndDocument();
|
||||
}
|
||||
},
|
||||
file.toPath()
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.store;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
@@ -55,6 +55,8 @@ public class JAXBConfigurationEntryStoreFactory extends FileBasedStoreFactory
|
||||
return new JAXBConfigurationEntryStore<>(
|
||||
getStoreLocation(storeParameters.getName().concat(StoreConstants.FILE_EXTENSION), storeParameters.getType(), storeParameters.getRepositoryId()),
|
||||
keyGenerator,
|
||||
storeParameters.getType());
|
||||
storeParameters.getType(),
|
||||
TypedStoreContext.of(storeParameters)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,26 +21,19 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.store;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
import java.io.File;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
|
||||
/**
|
||||
* JAXB implementation of {@link ConfigurationStore}.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*
|
||||
* @param <T>
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class JAXBConfigurationStore<T> extends AbstractStore<T> {
|
||||
|
||||
@@ -49,28 +42,19 @@ public class JAXBConfigurationStore<T> extends AbstractStore<T> {
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JAXBConfigurationStore.class);
|
||||
|
||||
private Class<T> type;
|
||||
|
||||
private File configFile;
|
||||
|
||||
private JAXBContext context;
|
||||
|
||||
public JAXBConfigurationStore(Class<T> type, File configFile) {
|
||||
this.type = type;
|
||||
private final TypedStoreContext<T> context;
|
||||
private final Class<T> type;
|
||||
private final File configFile;
|
||||
|
||||
try {
|
||||
context = JAXBContext.newInstance(type);
|
||||
this.configFile = configFile;
|
||||
}
|
||||
catch (JAXBException ex) {
|
||||
throw new StoreException("failed to create jaxb context", ex);
|
||||
}
|
||||
public JAXBConfigurationStore(TypedStoreContext<T> context, Class<T> type, File configFile) {
|
||||
this.context = context;
|
||||
this.type = type;
|
||||
this.configFile = configFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns type of stored object.
|
||||
*
|
||||
*
|
||||
* @return type
|
||||
*/
|
||||
public Class<T> getType() {
|
||||
@@ -82,38 +66,18 @@ public class JAXBConfigurationStore<T> extends AbstractStore<T> {
|
||||
protected T readObject() {
|
||||
LOG.debug("load {} from store {}", type, configFile);
|
||||
|
||||
T result = null;
|
||||
|
||||
if (configFile.exists()) {
|
||||
try {
|
||||
result = (T) context.createUnmarshaller().unmarshal(configFile);
|
||||
}
|
||||
catch (JAXBException ex) {
|
||||
throw new StoreException("failed to unmarshall object", ex);
|
||||
}
|
||||
return context.unmarshall(configFile);
|
||||
}
|
||||
|
||||
return result;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeObject(T object) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("store {} to {}", object.getClass().getName(),
|
||||
configFile.getPath());
|
||||
}
|
||||
|
||||
try {
|
||||
Marshaller marshaller = context.createMarshaller();
|
||||
|
||||
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
|
||||
CopyOnWrite.withTemporaryFile(
|
||||
temp -> marshaller.marshal(object, temp.toFile()),
|
||||
configFile.toPath()
|
||||
);
|
||||
}
|
||||
catch (JAXBException ex) {
|
||||
throw new StoreException("failed to marshall object", ex);
|
||||
}
|
||||
LOG.debug("store {} to {}", object.getClass().getName(), configFile.getPath());
|
||||
CopyOnWrite.withTemporaryFile(
|
||||
temp -> context.marshal(object, temp.toFile()),
|
||||
configFile.toPath()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.store;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
@@ -49,10 +49,13 @@ public class JAXBConfigurationStoreFactory extends FileBasedStoreFactory impleme
|
||||
|
||||
@Override
|
||||
public <T> JAXBConfigurationStore<T> getStore(TypedStoreParameters<T> storeParameters) {
|
||||
TypedStoreContext<T> context = TypedStoreContext.of(storeParameters);
|
||||
return new JAXBConfigurationStore<>(
|
||||
context,
|
||||
storeParameters.getType(),
|
||||
getStoreLocation(storeParameters.getName().concat(StoreConstants.FILE_EXTENSION),
|
||||
storeParameters.getType(),
|
||||
storeParameters.getRepositoryId()));
|
||||
storeParameters.getRepositoryId())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,23 +24,16 @@
|
||||
|
||||
package sonia.scm.store;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMap.Builder;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.security.KeyGenerator;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
import java.io.File;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Jaxb implementation of {@link DataStore}.
|
||||
@@ -54,24 +47,16 @@ public class JAXBDataStore<T> extends FileBasedStore<T> implements DataStore<T>
|
||||
/**
|
||||
* the logger for JAXBDataStore
|
||||
*/
|
||||
private static final Logger LOG
|
||||
= LoggerFactory.getLogger(JAXBDataStore.class);
|
||||
|
||||
private final JAXBContext context;
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JAXBDataStore.class);
|
||||
|
||||
private final KeyGenerator keyGenerator;
|
||||
private final TypedStoreContext<T> context;
|
||||
|
||||
JAXBDataStore(KeyGenerator keyGenerator, Class<T> type, File directory) {
|
||||
JAXBDataStore(KeyGenerator keyGenerator, TypedStoreContext<T> context, File directory) {
|
||||
super(directory, StoreConstants.FILE_EXTENSION);
|
||||
this.keyGenerator = keyGenerator;
|
||||
|
||||
try {
|
||||
context = JAXBContext.newInstance(type);
|
||||
this.directory = directory;
|
||||
}
|
||||
catch (JAXBException ex) {
|
||||
throw new StoreException("failed to create jaxb context", ex);
|
||||
}
|
||||
this.directory = directory;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -118,22 +103,11 @@ public class JAXBDataStore<T> extends FileBasedStore<T> implements DataStore<T>
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected T read(File file) {
|
||||
T item = null;
|
||||
|
||||
if (file.exists()) {
|
||||
LOG.trace("try to read {}", file);
|
||||
|
||||
try {
|
||||
item = (T) context.createUnmarshaller().unmarshal(file);
|
||||
}
|
||||
catch (JAXBException ex) {
|
||||
throw new StoreException(
|
||||
"could not read object ".concat(file.getPath()), ex);
|
||||
}
|
||||
return context.unmarshall(file);
|
||||
}
|
||||
|
||||
return item;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.store;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
@@ -56,6 +56,6 @@ public class JAXBDataStoreFactory extends FileBasedStoreFactory
|
||||
public <T> DataStore<T> getStore(TypedStoreParameters<T> storeParameters) {
|
||||
File storeLocation = getStoreLocation(storeParameters);
|
||||
IOUtil.mkdirs(storeLocation);
|
||||
return new JAXBDataStore<>(keyGenerator, storeParameters.getType(), storeLocation);
|
||||
return new JAXBDataStore<>(keyGenerator, TypedStoreContext.of(storeParameters), storeLocation);
|
||||
}
|
||||
}
|
||||
|
||||
127
scm-dao-xml/src/main/java/sonia/scm/store/TypedStoreContext.java
Normal file
127
scm-dao-xml/src/main/java/sonia/scm/store/TypedStoreContext.java
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.store;
|
||||
|
||||
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.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
final class TypedStoreContext<T> {
|
||||
|
||||
private final JAXBContext jaxbContext;
|
||||
private final TypedStoreParameters<T> parameters;
|
||||
|
||||
private TypedStoreContext(JAXBContext jaxbContext, TypedStoreParameters<T> parameters) {
|
||||
this.jaxbContext = jaxbContext;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
static <T> TypedStoreContext<T> of(TypedStoreParameters<T> parameters) {
|
||||
try {
|
||||
JAXBContext jaxbContext = JAXBContext.newInstance(parameters.getType());
|
||||
return new TypedStoreContext<>(jaxbContext, parameters);
|
||||
} catch (JAXBException e) {
|
||||
throw new StoreException("failed to create context for store");
|
||||
}
|
||||
}
|
||||
|
||||
T unmarshall(File file) {
|
||||
AtomicReference<T> ref = new AtomicReference<>();
|
||||
withUnmarshaller(unmarshaller -> {
|
||||
T value = parameters.getType().cast(unmarshaller.unmarshal(file));
|
||||
ref.set(value);
|
||||
});
|
||||
return ref.get();
|
||||
}
|
||||
|
||||
void marshal(Object object, File file) {
|
||||
withMarshaller(marshaller -> marshaller.marshal(object, file));
|
||||
}
|
||||
|
||||
void withMarshaller(ThrowingConsumer<Marshaller> consumer) {
|
||||
Marshaller marshaller = createMarshaller();
|
||||
withClassLoader(consumer, marshaller);
|
||||
}
|
||||
|
||||
void withUnmarshaller(ThrowingConsumer<Unmarshaller> consumer) {
|
||||
Unmarshaller unmarshaller = createUnmarshaller();
|
||||
withClassLoader(consumer, unmarshaller);
|
||||
}
|
||||
|
||||
private <C> void withClassLoader(ThrowingConsumer<C> consumer, C consume) {
|
||||
ClassLoader contextClassLoader = null;
|
||||
Optional<ClassLoader> classLoader = parameters.getClassLoader();
|
||||
if (classLoader.isPresent()) {
|
||||
contextClassLoader = Thread.currentThread().getContextClassLoader();
|
||||
Thread.currentThread().setContextClassLoader(classLoader.get());
|
||||
}
|
||||
try {
|
||||
consumer.consume(consume);
|
||||
} catch (Exception e) {
|
||||
throw new StoreException("failure during marshalling/unmarshalling", e);
|
||||
} finally {
|
||||
if (contextClassLoader != null) {
|
||||
Thread.currentThread().setContextClassLoader(contextClassLoader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Marshaller createMarshaller() {
|
||||
try {
|
||||
Marshaller marshaller = jaxbContext.createMarshaller();
|
||||
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
|
||||
for (XmlAdapter<?, ?> adapter : parameters.getAdapters()) {
|
||||
marshaller.setAdapter(adapter);
|
||||
}
|
||||
return marshaller;
|
||||
} catch (JAXBException e) {
|
||||
throw new StoreException("could not create marshaller", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Unmarshaller createUnmarshaller() {
|
||||
try {
|
||||
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
|
||||
for (XmlAdapter<?, ?> adapter : parameters.getAdapters()) {
|
||||
unmarshaller.setAdapter(adapter);
|
||||
}
|
||||
return unmarshaller;
|
||||
} catch (JAXBException e) {
|
||||
throw new StoreException("could not create unmarshaller", e);
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface ThrowingConsumer<T> {
|
||||
@SuppressWarnings("java:S112") // we need to throw Exception here
|
||||
void consume(T item) throws Exception;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.store;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
@@ -52,7 +52,7 @@ public class JAXBDataStoreTest extends DataStoreTestBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataStore getDataStore(Class type, Repository repository) {
|
||||
protected <T> DataStore<T> getDataStore(Class<T> type, Repository repository) {
|
||||
return createDataStoreFactory()
|
||||
.withType(type)
|
||||
.withName("test")
|
||||
@@ -61,7 +61,7 @@ public class JAXBDataStoreTest extends DataStoreTestBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataStore getDataStore(Class type) {
|
||||
protected <T> DataStore<T> getDataStore(Class<T> type) {
|
||||
return createDataStoreFactory()
|
||||
.withType(type)
|
||||
.withName("test")
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.store;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class TypedStoreContextTest {
|
||||
|
||||
@Test
|
||||
void shouldMarshallAndUnmarshall(@TempDir Path tempDir) {
|
||||
TypedStoreContext<Sample> context = context(Sample.class);
|
||||
|
||||
File file = tempDir.resolve("test.xml").toFile();
|
||||
context.marshal(new Sample("awesome"), file);
|
||||
Sample sample = context.unmarshall(file);
|
||||
|
||||
assertThat(sample.value).isEqualTo("awesome");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithMarshallerAndUnmarshaller(@TempDir Path tempDir) {
|
||||
TypedStoreContext<Sample> context = context(Sample.class);
|
||||
|
||||
File file = tempDir.resolve("test.xml").toFile();
|
||||
|
||||
context.withMarshaller(marshaller -> {
|
||||
marshaller.marshal(new Sample("wow"), file);
|
||||
});
|
||||
|
||||
AtomicReference<Sample> ref = new AtomicReference<>();
|
||||
|
||||
context.withUnmarshaller(unmarshaller -> {
|
||||
Sample sample = (Sample) unmarshaller.unmarshal(file);
|
||||
ref.set(sample);
|
||||
});
|
||||
|
||||
assertThat(ref.get().value).isEqualTo("wow");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSetContextClassLoader() {
|
||||
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
|
||||
|
||||
ClassLoader classLoader = new URLClassLoader(new URL[0], contextClassLoader);
|
||||
|
||||
TypedStoreParameters<Sample> params = params(Sample.class);
|
||||
when(params.getClassLoader()).thenReturn(Optional.of(classLoader));
|
||||
|
||||
TypedStoreContext<Sample> context = TypedStoreContext.of(params);
|
||||
|
||||
AtomicReference<ClassLoader> ref = new AtomicReference<>();
|
||||
context.withMarshaller(marshaller -> {
|
||||
ref.set(Thread.currentThread().getContextClassLoader());
|
||||
});
|
||||
|
||||
assertThat(ref.get()).isSameAs(classLoader);
|
||||
assertThat(Thread.currentThread().getContextClassLoader()).isSameAs(contextClassLoader);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldConfigureAdapter(@TempDir Path tempDir) {
|
||||
TypedStoreParameters<SampleWithAdapter> params = params(SampleWithAdapter.class);
|
||||
when(params.getAdapters()).thenReturn(Collections.singleton(new AppendingAdapter("!")));
|
||||
|
||||
TypedStoreContext<SampleWithAdapter> context = TypedStoreContext.of(params);
|
||||
|
||||
File file = tempDir.resolve("test.xml").toFile();
|
||||
context.marshal(new SampleWithAdapter("awesome"), file);
|
||||
SampleWithAdapter sample = context.unmarshall(file);
|
||||
|
||||
// one ! should be added for marshal and one for unmarshal
|
||||
assertThat(sample.value).isEqualTo("awesome!!");
|
||||
}
|
||||
|
||||
private <T> TypedStoreContext<T> context(Class<T> type) {
|
||||
return TypedStoreContext.of(params(type));
|
||||
}
|
||||
|
||||
private <T> TypedStoreParameters<T> params(Class<T> type) {
|
||||
TypedStoreParameters<T> params = mock(TypedStoreParameters.class);
|
||||
when(params.getType()).thenReturn(type);
|
||||
return params;
|
||||
}
|
||||
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public static class Sample {
|
||||
private String value;
|
||||
|
||||
public Sample() {
|
||||
}
|
||||
|
||||
public Sample(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public static class SampleWithAdapter {
|
||||
@XmlJavaTypeAdapter(AppendingAdapter.class)
|
||||
private String value;
|
||||
|
||||
public SampleWithAdapter() {
|
||||
}
|
||||
|
||||
public SampleWithAdapter(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static class AppendingAdapter extends XmlAdapter<String, String> {
|
||||
|
||||
private final String suffix;
|
||||
|
||||
public AppendingAdapter(String suffix) {
|
||||
this.suffix = suffix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String unmarshal(String v) {
|
||||
return v + suffix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String marshal(String v) {
|
||||
return v + suffix;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,13 +21,12 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.store;
|
||||
|
||||
import sonia.scm.repository.Repository;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public abstract class ConfigurationEntryStoreTestBase extends KeyValueStoreTestBase {
|
||||
@@ -35,26 +34,25 @@ public abstract class ConfigurationEntryStoreTestBase extends KeyValueStoreTestB
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract ConfigurationEntryStoreFactory createConfigurationStoreFactory();
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
@Override
|
||||
protected ConfigurationEntryStore getDataStore(Class type) {
|
||||
protected <T> ConfigurationEntryStore<T> getDataStore(Class<T> type) {
|
||||
return this.createConfigurationStoreFactory()
|
||||
.withType(type)
|
||||
.withName(storeName)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigurationEntryStore getDataStore(Class type, Repository repository) {
|
||||
return this.createConfigurationStoreFactory()
|
||||
.withType(type)
|
||||
.withName(repoStoreName)
|
||||
.forRepository(repository)
|
||||
.build();
|
||||
@Override
|
||||
protected <T> ConfigurationEntryStore<T> getDataStore(Class<T> type, Repository repository) {
|
||||
return this.createConfigurationStoreFactory()
|
||||
.withType(type)
|
||||
.withName(repoStoreName)
|
||||
.forRepository(repository)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,16 +21,16 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.store;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class InMemoryConfigurationEntryStore<V> implements ConfigurationEntryStore<V> {
|
||||
|
||||
Reference in New Issue
Block a user