From dbc97d8993455ce5c2bd6a3c0e8f1cd24c9ba6de Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 28 Apr 2020 09:47:16 +0200 Subject: [PATCH 1/9] simplified structure of store builders --- .../sonia/scm/store/BlobStoreFactory.java | 69 +----------- .../store/ConfigurationEntryStoreFactory.java | 70 +----------- .../scm/store/ConfigurationStoreFactory.java | 70 +----------- .../sonia/scm/store/DataStoreFactory.java | 70 +----------- .../java/sonia/scm/store/StoreParameters.java | 4 +- .../scm/store/StoreParametersBuilder.java | 90 ++++++++++++++++ .../sonia/scm/store/TypedStoreParameters.java | 9 +- .../store/TypedStoreParametersBuilder.java | 102 ++++++++++++++++++ .../scm/store/TypedStoreParametersImpl.java | 58 ---------- .../sonia/scm/store/JAXBDataStoreTest.java | 6 +- .../ConfigurationEntryStoreTestBase.java | 20 ++-- 11 files changed, 223 insertions(+), 345 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/store/StoreParametersBuilder.java create mode 100644 scm-core/src/main/java/sonia/scm/store/TypedStoreParametersBuilder.java delete mode 100644 scm-core/src/main/java/sonia/scm/store/TypedStoreParametersImpl.java diff --git a/scm-core/src/main/java/sonia/scm/store/BlobStoreFactory.java b/scm-core/src/main/java/sonia/scm/store/BlobStoreFactory.java index 05e7fd5033..e2477f1752 100644 --- a/scm-core/src/main/java/sonia/scm/store/BlobStoreFactory.java +++ b/scm-core/src/main/java/sonia/scm/store/BlobStoreFactory.java @@ -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 withName(String name) { + return new StoreParametersBuilder<>(name, this::getStore); } } diff --git a/scm-core/src/main/java/sonia/scm/store/ConfigurationEntryStoreFactory.java b/scm-core/src/main/java/sonia/scm/store/ConfigurationEntryStoreFactory.java index 96cca1da3d..09f1217815 100644 --- a/scm-core/src/main/java/sonia/scm/store/ConfigurationEntryStoreFactory.java +++ b/scm-core/src/main/java/sonia/scm/store/ConfigurationEntryStoreFactory.java @@ -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 TypedFloatingConfigurationEntryStoreParameters.Builder withType(Class type) { - return new TypedFloatingConfigurationEntryStoreParameters(this).new Builder(type); - } -} - -final class TypedFloatingConfigurationEntryStoreParameters { - - private final TypedStoreParametersImpl parameters = new TypedStoreParametersImpl<>(); - private final ConfigurationEntryStoreFactory factory; - - TypedFloatingConfigurationEntryStoreParameters(ConfigurationEntryStoreFactory factory) { - this.factory = factory; - } - - public class Builder { - - Builder(Class 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 build(){ - return factory.getStore(parameters); - } + default TypedStoreParametersBuilder> withType(Class type) { + return new TypedStoreParametersBuilder<>(type, this::getStore); } } diff --git a/scm-core/src/main/java/sonia/scm/store/ConfigurationStoreFactory.java b/scm-core/src/main/java/sonia/scm/store/ConfigurationStoreFactory.java index 6e8d2b18ff..8202f8425b 100644 --- a/scm-core/src/main/java/sonia/scm/store/ConfigurationStoreFactory.java +++ b/scm-core/src/main/java/sonia/scm/store/ConfigurationStoreFactory.java @@ -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. *
@@ -51,7 +49,7 @@ import sonia.scm.repository.Repository; * * * @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 TypedFloatingConfigurationStoreParameters.Builder withType(Class type) { - return new TypedFloatingConfigurationStoreParameters(this).new Builder(type); - } -} - -final class TypedFloatingConfigurationStoreParameters { - - private final TypedStoreParametersImpl parameters = new TypedStoreParametersImpl<>(); - private final ConfigurationStoreFactory factory; - - TypedFloatingConfigurationStoreParameters(ConfigurationStoreFactory factory) { - this.factory = factory; - } - - public class Builder { - - Builder(Class 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 build(){ - return factory.getStore(parameters); - } + default TypedStoreParametersBuilder> withType(Class type) { + return new TypedStoreParametersBuilder<>(type, this::getStore); } } diff --git a/scm-core/src/main/java/sonia/scm/store/DataStoreFactory.java b/scm-core/src/main/java/sonia/scm/store/DataStoreFactory.java index c7ebbbb67f..45d9a93993 100644 --- a/scm-core/src/main/java/sonia/scm/store/DataStoreFactory.java +++ b/scm-core/src/main/java/sonia/scm/store/DataStoreFactory.java @@ -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 TypedFloatingDataStoreParameters.Builder withType(Class type) { - return new TypedFloatingDataStoreParameters(this).new Builder(type); - } -} - -final class TypedFloatingDataStoreParameters { - - private final TypedStoreParametersImpl parameters = new TypedStoreParametersImpl<>(); - private final DataStoreFactory factory; - - TypedFloatingDataStoreParameters(DataStoreFactory factory) { - this.factory = factory; - } - - public class Builder { - - Builder(Class 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 build(){ - return factory.getStore(parameters); - } + default TypedStoreParametersBuilder> withType(Class type) { + return new TypedStoreParametersBuilder<>(type, this::getStore); } } diff --git a/scm-core/src/main/java/sonia/scm/store/StoreParameters.java b/scm-core/src/main/java/sonia/scm/store/StoreParameters.java index 379013388a..2326a68803 100644 --- a/scm-core/src/main/java/sonia/scm/store/StoreParameters.java +++ b/scm-core/src/main/java/sonia/scm/store/StoreParameters.java @@ -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. diff --git a/scm-core/src/main/java/sonia/scm/store/StoreParametersBuilder.java b/scm-core/src/main/java/sonia/scm/store/StoreParametersBuilder.java new file mode 100644 index 0000000000..38cd208a7d --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/store/StoreParametersBuilder.java @@ -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 type of store + */ +public final class StoreParametersBuilder { + + private final StoreParametersImpl parameters; + private final Function factory; + + StoreParametersBuilder(String name, Function 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 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 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); + } +} diff --git a/scm-core/src/main/java/sonia/scm/store/TypedStoreParameters.java b/scm-core/src/main/java/sonia/scm/store/TypedStoreParameters.java index c07df2b4a0..0a28b0d455 100644 --- a/scm-core/src/main/java/sonia/scm/store/TypedStoreParameters.java +++ b/scm-core/src/main/java/sonia/scm/store/TypedStoreParameters.java @@ -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 TypedStoreParameters} are used from the {@link ConfigurationStoreFactory}, @@ -33,11 +31,8 @@ import sonia.scm.repository.Repository; * @author Mohamed Karray * @since 2.0.0 */ -public interface TypedStoreParameters { +public interface TypedStoreParameters extends StoreParameters { Class getType(); - String getName(); - - String getRepositoryId(); } diff --git a/scm-core/src/main/java/sonia/scm/store/TypedStoreParametersBuilder.java b/scm-core/src/main/java/sonia/scm/store/TypedStoreParametersBuilder.java new file mode 100644 index 0000000000..e590ce40a0 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/store/TypedStoreParametersBuilder.java @@ -0,0 +1,102 @@ +/* + * 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 TypedStoreParameters}. + * + * @param type of data + * @param type of store + */ +public final class TypedStoreParametersBuilder { + + private final TypedStoreParametersImpl parameters; + private final Function, S> factory; + + TypedStoreParametersBuilder(Class type, Function, 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 implements TypedStoreParameters { + private final Class type; + private String name; + private String repositoryId; + } + + public class OptionalRepositoryBuilder { + + /** + * 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; + } + + /** + * 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); + } + } + +} diff --git a/scm-core/src/main/java/sonia/scm/store/TypedStoreParametersImpl.java b/scm-core/src/main/java/sonia/scm/store/TypedStoreParametersImpl.java deleted file mode 100644 index 6d99185302..0000000000 --- a/scm-core/src/main/java/sonia/scm/store/TypedStoreParametersImpl.java +++ /dev/null @@ -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 implements TypedStoreParameters { - private Class type; - private String name; - private String repositoryId; - - @Override - public Class getType() { - return type; - } - - void setType(Class 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; - } -} diff --git a/scm-dao-xml/src/test/java/sonia/scm/store/JAXBDataStoreTest.java b/scm-dao-xml/src/test/java/sonia/scm/store/JAXBDataStoreTest.java index 54057cfad6..13b9ad29b8 100644 --- a/scm-dao-xml/src/test/java/sonia/scm/store/JAXBDataStoreTest.java +++ b/scm-dao-xml/src/test/java/sonia/scm/store/JAXBDataStoreTest.java @@ -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 DataStore getDataStore(Class 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 DataStore getDataStore(Class type) { return createDataStoreFactory() .withType(type) .withName("test") diff --git a/scm-test/src/main/java/sonia/scm/store/ConfigurationEntryStoreTestBase.java b/scm-test/src/main/java/sonia/scm/store/ConfigurationEntryStoreTestBase.java index b90af5c970..ff44f6cbd2 100644 --- a/scm-test/src/main/java/sonia/scm/store/ConfigurationEntryStoreTestBase.java +++ b/scm-test/src/main/java/sonia/scm/store/ConfigurationEntryStoreTestBase.java @@ -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 ConfigurationEntryStore getDataStore(Class 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 ConfigurationEntryStore getDataStore(Class type, Repository repository) { + return this.createConfigurationStoreFactory() + .withType(type) + .withName(repoStoreName) + .forRepository(repository) + .build(); } } From cf4c1092b92dc8e298ac4804c4a16ebc6943ad96 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 28 Apr 2020 13:46:11 +0200 Subject: [PATCH 2/9] create context class in order to configure Marshaller and Unmarshaller central --- .../store/JAXBConfigurationEntryStore.java | 342 ++++++------------ .../JAXBConfigurationEntryStoreFactory.java | 6 +- .../scm/store/JAXBConfigurationStore.java | 63 +--- .../store/JAXBConfigurationStoreFactory.java | 7 +- .../java/sonia/scm/store/JAXBDataStore.java | 49 +-- .../sonia/scm/store/JAXBDataStoreFactory.java | 4 +- .../sonia/scm/store/TypedStoreContext.java | 112 ++++++ .../scm/store/TypedStoreContextTest.java | 101 ++++++ 8 files changed, 364 insertions(+), 320 deletions(-) create mode 100644 scm-dao-xml/src/main/java/sonia/scm/store/TypedStoreContext.java create mode 100644 scm-dao-xml/src/test/java/sonia/scm/store/TypedStoreContextTest.java diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java index b25c1c9e9a..c2047486b4 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java @@ -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 -------------------------------------------------------- @@ -35,9 +35,7 @@ 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; @@ -51,94 +49,66 @@ import java.util.Map.Entry; //~--- JDK imports ------------------------------------------------------------ /** - * - * @author Sebastian Sdorra - * * @param + * @author Sebastian Sdorra */ -public class JAXBConfigurationEntryStore implements ConfigurationEntryStore -{ +public class JAXBConfigurationEntryStore implements ConfigurationEntryStore { - /** Field description */ + /** + * Field description + */ private static final String TAG_CONFIGURATION = "configuration"; - /** Field description */ + /** + * Field description + */ private static final String TAG_ENTRY = "entry"; - /** Field description */ + /** + * Field description + */ private static final String TAG_KEY = "key"; - /** Field description */ + /** + * 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 type) - { + private final File file; + private final KeyGenerator keyGenerator; + private final Class type; + private final TypedStoreContext context; + private final Map entries = Maps.newHashMap(); + + JAXBConfigurationEntryStore(File file, KeyGenerator keyGenerator, Class type, TypedStoreContext 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 +116,141 @@ public class JAXBConfigurationEntryStore implements ConfigurationEntryStore getAll() - { - logger.trace("get all items from configuration store"); + public Map getAll() { + LOG.trace("get all items from configuration store"); return Collections.unmodifiableMap(entries); } - /** - * Method description - * - * - * @param predicate - * - * @return - */ @Override - public Collection getMatchingValues(Predicate predicate) - { + public Collection getMatchingValues(Predicate predicate) { return Collections2.filter(entries.values(), predicate); } - //~--- 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 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(); + + String key = reader.getElementText(); + + // read value + reader.nextTag(); + + JAXBElement element = u.unmarshal(reader, type); + + if (!element.isNil()) { + V v = element.getValue(); + + LOG.trace("add element {} to configuration entry store", v); + + entries.put(key, v); + } else { + LOG.warn("could not unmarshall object of entry store"); + } + + // closed or new entry tag + if (reader.nextTag() == XMLStreamReader.END_ELEMENT) { + + // fixed format, start new entry + reader.nextTag(); + } } + } finally { + XmlStreams.close(reader); } - } - 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); + private void store() { + LOG.debug("store configuration to {}", file); - CopyOnWrite.withTemporaryFile( - temp -> { - try (IndentXMLStreamWriter writer = XmlStreams.createWriter(temp)) { - writer.writeStartDocument(); + context.withMarshaller(m -> { + m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); - // configuration start - writer.writeStartElement(TAG_CONFIGURATION); + CopyOnWrite.withTemporaryFile( + temp -> { + try (IndentXMLStreamWriter writer = XmlStreams.createWriter(temp)) { + writer.writeStartDocument(); - Marshaller m = context.createMarshaller(); + // configuration start + writer.writeStartElement(TAG_CONFIGURATION); - m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); - for (Entry e : entries.entrySet()) { + for (Entry e : entries.entrySet()) { - // entry start - writer.writeStartElement(TAG_ENTRY); + // entry start + writer.writeStartElement(TAG_ENTRY); - // key start - writer.writeStartElement(TAG_KEY); - writer.writeCharacters(e.getKey()); + // key start + writer.writeStartElement(TAG_KEY); + writer.writeCharacters(e.getKey()); - // key end - writer.writeEndElement(); - - // value - JAXBElement je = new JAXBElement<>(QName.valueOf(TAG_VALUE), type, - e.getValue()); - - m.marshal(je, writer); - - // entry end + // key end + writer.writeEndElement(); + + // value + JAXBElement je = new JAXBElement<>(QName.valueOf(TAG_VALUE), type, + e.getValue()); + + m.marshal(je, writer); + + // entry end + writer.writeEndElement(); + } + + // configuration end writer.writeEndElement(); + writer.writeEndDocument(); } - - // configuration end - writer.writeEndElement(); - writer.writeEndDocument(); - } - }, - file.toPath() - ); + }, + file.toPath() + ); + }); } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private final File file; - - /** Field description */ - private JAXBContext context; - - /** Field description */ - private Map entries = Maps.newHashMap(); - - /** Field description */ - private KeyGenerator keyGenerator; - - /** Field description */ - private Class type; } diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStoreFactory.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStoreFactory.java index 83294038d6..7feae4e0df 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStoreFactory.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStoreFactory.java @@ -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) + ); } } diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationStore.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationStore.java index 5a641dcd29..b5b4ff4034 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationStore.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationStore.java @@ -21,10 +21,11 @@ * 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; @@ -38,9 +39,8 @@ import javax.xml.bind.Marshaller; /** * JAXB implementation of {@link ConfigurationStore}. * - * @author Sebastian Sdorra - * * @param + * @author Sebastian Sdorra */ public class JAXBConfigurationStore extends AbstractStore { @@ -49,28 +49,19 @@ public class JAXBConfigurationStore extends AbstractStore { */ private static final Logger LOG = LoggerFactory.getLogger(JAXBConfigurationStore.class); - private Class type; - - private File configFile; - - private JAXBContext context; - - public JAXBConfigurationStore(Class type, File configFile) { - this.type = type; + private final TypedStoreContext context; + private final Class 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 context, Class type, File configFile) { + this.context = context; + this.type = type; + this.configFile = configFile; } /** * Returns type of stored object. * - * * @return type */ public Class getType() { @@ -82,38 +73,18 @@ public class JAXBConfigurationStore extends AbstractStore { 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() + ); } } diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationStoreFactory.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationStoreFactory.java index 65a81c8b7b..29cdf471a3 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationStoreFactory.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationStoreFactory.java @@ -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 JAXBConfigurationStore getStore(TypedStoreParameters storeParameters) { + TypedStoreContext context = TypedStoreContext.of(storeParameters); return new JAXBConfigurationStore<>( + context, storeParameters.getType(), getStoreLocation(storeParameters.getName().concat(StoreConstants.FILE_EXTENSION), storeParameters.getType(), - storeParameters.getRepositoryId())); + storeParameters.getRepositoryId()) + ); } } diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStore.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStore.java index a4181c5c8d..4ab83ca961 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStore.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStore.java @@ -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 -------------------------------------------------------- @@ -54,42 +54,22 @@ public class JAXBDataStore extends FileBasedStore implements DataStore /** * 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 context; - JAXBDataStore(KeyGenerator keyGenerator, Class type, File directory) { + JAXBDataStore(KeyGenerator keyGenerator, TypedStoreContext 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 public void put(String id, T item) { LOG.debug("put item {} to store", id); - - File file = getFile(id); - - try { - Marshaller marshaller = context.createMarshaller(); - - marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); - marshaller.marshal(item, file); - } - catch (JAXBException ex) { - throw new StoreException("could not write object with id ".concat(id), - ex); - } + context.marshal(item, getFile(id)); } @Override @@ -115,22 +95,11 @@ public class JAXBDataStore extends FileBasedStore implements DataStore } @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; } } diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStoreFactory.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStoreFactory.java index d525b5c7a6..80e2d260d2 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStoreFactory.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStoreFactory.java @@ -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 DataStore getStore(TypedStoreParameters storeParameters) { File storeLocation = getStoreLocation(storeParameters); IOUtil.mkdirs(storeLocation); - return new JAXBDataStore<>(keyGenerator, storeParameters.getType(), storeLocation); + return new JAXBDataStore<>(keyGenerator, TypedStoreContext.of(storeParameters), storeLocation); } } diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/TypedStoreContext.java b/scm-dao-xml/src/main/java/sonia/scm/store/TypedStoreContext.java new file mode 100644 index 0000000000..5963804931 --- /dev/null +++ b/scm-dao-xml/src/main/java/sonia/scm/store/TypedStoreContext.java @@ -0,0 +1,112 @@ +/* + * 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 java.io.File; + +final class TypedStoreContext { + + private final JAXBContext jaxbContext; + private final TypedStoreParameters parameters; + + private TypedStoreContext(JAXBContext jaxbContext, TypedStoreParameters parameters) { + this.jaxbContext = jaxbContext; + this.parameters = parameters; + } + + static TypedStoreContext of(TypedStoreParameters 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) { + Unmarshaller unmarshaller = createUnmarshaller(); + try { + return parameters.getType().cast(unmarshaller.unmarshal(file)); + } catch (JAXBException e) { + throw new StoreException("failed to unmarshall " + file); + } + } + + void marshal(Object object, File file) { + Marshaller marshaller = createMarshaller(); + try { + marshaller.marshal(object, file); + } catch (JAXBException e) { + throw new StoreException("failed to marshall " + object + " to " + file); + } + } + + void withMarshaller(ThrowingConsumer consumer) { + Marshaller marshaller = createMarshaller(); + try { + consumer.consume(marshaller); + } catch (Exception e) { + throw new StoreException("failure during work with marshaller"); + } + } + + void withUnmarshaller(ThrowingConsumer consumer) { + Unmarshaller unmarshaller = createUnmarshaller(); + try { + consumer.consume(unmarshaller); + } catch (Exception e) { + throw new StoreException("failure during work with unmarshaller", e); + } + } + + private Marshaller createMarshaller() { + try { + Marshaller marshaller = jaxbContext.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + return marshaller; + } catch (JAXBException e) { + throw new StoreException("could not create marshaller", e); + } + } + + private Unmarshaller createUnmarshaller() { + try { + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + return unmarshaller; + } catch (JAXBException e) { + throw new StoreException("could not create unmarshaller", e); + } + } + + @FunctionalInterface + interface ThrowingConsumer { + void consume(T item) throws Exception; + } + +} diff --git a/scm-dao-xml/src/test/java/sonia/scm/store/TypedStoreContextTest.java b/scm-dao-xml/src/test/java/sonia/scm/store/TypedStoreContextTest.java new file mode 100644 index 0000000000..a790bd51d9 --- /dev/null +++ b/scm-dao-xml/src/test/java/sonia/scm/store/TypedStoreContextTest.java @@ -0,0 +1,101 @@ +/* + * 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.junitpioneer.jupiter.TempDirectory; +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 java.io.File; +import java.nio.file.Path; +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, TempDirectory.class}) +class TypedStoreContextTest { + + @Test + void shouldMarshallAndUnmarshall(@TempDirectory.TempDir Path tempDir) { + TypedStoreContext 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(@TempDirectory.TempDir Path tempDir) { + TypedStoreContext context = context(Sample.class); + + File file = tempDir.resolve("test.xml").toFile(); + + context.withMarshaller(marshaller -> { + marshaller.marshal(new Sample("wow"), file); + }); + + AtomicReference ref = new AtomicReference<>(); + + context.withUnmarshaller(unmarshaller -> { + Sample sample = (Sample) unmarshaller.unmarshal(file); + ref.set(sample); + }); + + assertThat(ref.get().value).isEqualTo("wow"); + } + + private TypedStoreContext context(Class type) { + return TypedStoreContext.of(params(type)); + } + + private TypedStoreParameters params(Class type) { + TypedStoreParameters 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; + } + } + +} From 38eea06312d1f67c9efe37f054744c964c9ee39e Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 28 Apr 2020 14:10:11 +0200 Subject: [PATCH 3/9] added optional store parameter for ClassLoader and adapter --- .../sonia/scm/store/TypedStoreParameters.java | 9 +++ .../store/TypedStoreParametersBuilder.java | 41 ++++++++++ .../sonia/scm/store/TypedStoreContext.java | 51 +++++++++---- .../scm/store/TypedStoreContextTest.java | 75 ++++++++++++++++++- 4 files changed, 162 insertions(+), 14 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/store/TypedStoreParameters.java b/scm-core/src/main/java/sonia/scm/store/TypedStoreParameters.java index 0a28b0d455..4f80078743 100644 --- a/scm-core/src/main/java/sonia/scm/store/TypedStoreParameters.java +++ b/scm-core/src/main/java/sonia/scm/store/TypedStoreParameters.java @@ -24,6 +24,10 @@ package sonia.scm.store; +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}, * {@link ConfigurationEntryStoreFactory} and {@link DataStoreFactory} to create a type safe store. @@ -35,4 +39,9 @@ public interface TypedStoreParameters extends StoreParameters { Class getType(); + Optional getClassLoader(); + + @SuppressWarnings("java:S1452") // we could not provide generic type, because we don't know it here + Set> getAdapters(); + } diff --git a/scm-core/src/main/java/sonia/scm/store/TypedStoreParametersBuilder.java b/scm-core/src/main/java/sonia/scm/store/TypedStoreParametersBuilder.java index e590ce40a0..efda2187a6 100644 --- a/scm-core/src/main/java/sonia/scm/store/TypedStoreParametersBuilder.java +++ b/scm-core/src/main/java/sonia/scm/store/TypedStoreParametersBuilder.java @@ -24,11 +24,18 @@ 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; /** @@ -64,10 +71,18 @@ public final class TypedStoreParametersBuilder { private final Class type; private String name; private String repositoryId; + private ClassLoader classLoader; + private Set> adapters; + + public Optional getClassLoader() { + return Optional.ofNullable(classLoader); + } } public class OptionalRepositoryBuilder { + private final Set> 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. @@ -90,11 +105,37 @@ public final class TypedStoreParametersBuilder { 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); } } diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/TypedStoreContext.java b/scm-dao-xml/src/main/java/sonia/scm/store/TypedStoreContext.java index 5963804931..2664444e55 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/TypedStoreContext.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/TypedStoreContext.java @@ -28,7 +28,10 @@ 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 { @@ -50,38 +53,53 @@ final class TypedStoreContext { } T unmarshall(File file) { - Unmarshaller unmarshaller = createUnmarshaller(); - try { - return parameters.getType().cast(unmarshaller.unmarshal(file)); - } catch (JAXBException e) { - throw new StoreException("failed to unmarshall " + file); - } + AtomicReference 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) { - Marshaller marshaller = createMarshaller(); - try { - marshaller.marshal(object, file); - } catch (JAXBException e) { - throw new StoreException("failed to marshall " + object + " to " + file); - } + withMarshaller(marshaller -> marshaller.marshal(object, file)); } void withMarshaller(ThrowingConsumer consumer) { Marshaller marshaller = createMarshaller(); + ClassLoader contextClassLoader = null; + Optional classLoader = parameters.getClassLoader(); + if (classLoader.isPresent()) { + contextClassLoader = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(classLoader.get()); + } try { consumer.consume(marshaller); } catch (Exception e) { - throw new StoreException("failure during work with marshaller"); + throw new StoreException("failure during work with marshaller", e); + } finally { + if (contextClassLoader != null) { + Thread.currentThread().setContextClassLoader(contextClassLoader); + } } } void withUnmarshaller(ThrowingConsumer consumer) { Unmarshaller unmarshaller = createUnmarshaller(); + ClassLoader contextClassLoader = null; + Optional classLoader = parameters.getClassLoader(); + if (classLoader.isPresent()) { + contextClassLoader = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(classLoader.get()); + } try { consumer.consume(unmarshaller); } catch (Exception e) { throw new StoreException("failure during work with unmarshaller", e); + } finally { + if (contextClassLoader != null) { + Thread.currentThread().setContextClassLoader(contextClassLoader); + } } } @@ -89,6 +107,9 @@ final class TypedStoreContext { 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); @@ -98,6 +119,9 @@ final class TypedStoreContext { 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); @@ -106,6 +130,7 @@ final class TypedStoreContext { @FunctionalInterface interface ThrowingConsumer { + @SuppressWarnings("java:S112") // we need to throw Exception here void consume(T item) throws Exception; } diff --git a/scm-dao-xml/src/test/java/sonia/scm/store/TypedStoreContextTest.java b/scm-dao-xml/src/test/java/sonia/scm/store/TypedStoreContextTest.java index a790bd51d9..d29fd44298 100644 --- a/scm-dao-xml/src/test/java/sonia/scm/store/TypedStoreContextTest.java +++ b/scm-dao-xml/src/test/java/sonia/scm/store/TypedStoreContextTest.java @@ -32,9 +32,14 @@ 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; @@ -75,6 +80,41 @@ class TypedStoreContextTest { assertThat(ref.get().value).isEqualTo("wow"); } + @Test + void shouldSetContextClassLoader() { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + + ClassLoader classLoader = new URLClassLoader(new URL[0], contextClassLoader); + + TypedStoreParameters params = params(Sample.class); + when(params.getClassLoader()).thenReturn(Optional.of(classLoader)); + + TypedStoreContext context = TypedStoreContext.of(params); + + AtomicReference 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(@TempDirectory.TempDir Path tempDir) { + TypedStoreParameters params = params(SampleWithAdapter.class); + when(params.getAdapters()).thenReturn(Collections.singleton(new AppendingAdapter("!"))); + + TypedStoreContext 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 TypedStoreContext context(Class type) { return TypedStoreContext.of(params(type)); } @@ -98,4 +138,37 @@ class TypedStoreContextTest { } } + @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 { + + 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; + } + } + } From aa23f6f7abd96dcdf73fde9fad3164fd353864bf Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 28 Apr 2020 14:16:25 +0200 Subject: [PATCH 4/9] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4beb9f22a3..992f2416b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add iconStyle + onClick option and story shot for icon component ([#1100](https://github.com/scm-manager/scm-manager/pull/1100)) - Making WebElements (Servlet or Filter) optional by using the `@Requires` annotation ([#1101](https://github.com/scm-manager/scm-manager/pull/1101)) +- ClassLoader and Adapter parameters to typed store apis ([#1111](https://github.com/scm-manager/scm-manager/pull/1111)) ### Changed - Removed the `requires` attribute on the `@Extension` annotation and instead create a new `@Requires` annotation ([#1097](https://github.com/scm-manager/scm-manager/pull/1097)) From 1a367b2657a8597912f386cfc6b89e1a5f0d4b69 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 28 Apr 2020 14:41:31 +0200 Subject: [PATCH 5/9] fix duplicate ClassLoader logic --- .../sonia/scm/store/TypedStoreContext.java | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/TypedStoreContext.java b/scm-dao-xml/src/main/java/sonia/scm/store/TypedStoreContext.java index 2664444e55..b5627c1b06 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/TypedStoreContext.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/TypedStoreContext.java @@ -67,25 +67,15 @@ final class TypedStoreContext { void withMarshaller(ThrowingConsumer consumer) { Marshaller marshaller = createMarshaller(); - ClassLoader contextClassLoader = null; - Optional classLoader = parameters.getClassLoader(); - if (classLoader.isPresent()) { - contextClassLoader = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(classLoader.get()); - } - try { - consumer.consume(marshaller); - } catch (Exception e) { - throw new StoreException("failure during work with marshaller", e); - } finally { - if (contextClassLoader != null) { - Thread.currentThread().setContextClassLoader(contextClassLoader); - } - } + withClassLoader(consumer, marshaller); } void withUnmarshaller(ThrowingConsumer consumer) { Unmarshaller unmarshaller = createUnmarshaller(); + withClassLoader(consumer, unmarshaller); + } + + private void withClassLoader(ThrowingConsumer consumer, C consume) { ClassLoader contextClassLoader = null; Optional classLoader = parameters.getClassLoader(); if (classLoader.isPresent()) { @@ -93,9 +83,9 @@ final class TypedStoreContext { Thread.currentThread().setContextClassLoader(classLoader.get()); } try { - consumer.consume(unmarshaller); + consumer.consume(consume); } catch (Exception e) { - throw new StoreException("failure during work with unmarshaller", e); + throw new StoreException("failure during marshalling/unmarshalling", e); } finally { if (contextClassLoader != null) { Thread.currentThread().setContextClassLoader(contextClassLoader); From 14a1427502f7a34c8b31ccc003161882c14a49ba Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 28 Apr 2020 15:38:08 +0200 Subject: [PATCH 6/9] use java.util.function.Predicate as api instead of the one from guava --- .../sonia/scm/store/ConfigurationEntryStore.java | 15 +++++---------- .../scm/store/JAXBConfigurationEntryStore.java | 5 ++--- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/store/ConfigurationEntryStore.java b/scm-core/src/main/java/sonia/scm/store/ConfigurationEntryStore.java index 7c82ee7310..92cd37c407 100644 --- a/scm-core/src/main/java/sonia/scm/store/ConfigurationEntryStore.java +++ b/scm-core/src/main/java/sonia/scm/store/ConfigurationEntryStore.java @@ -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. Note: the default implementation use JAXB to + * A ConfigurationEntryStore can be used to store multiple entries of structured + * configuration data. Note: the default implementation use JAXB to * marshall the items. * * @author Sebastian Sdorra @@ -52,5 +47,5 @@ public interface ConfigurationEntryStore extends DataStore { * * @return filtered collection of values */ - public Collection getMatchingValues(Predicate predicate); + Collection getMatchingValues(Predicate predicate); } diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java index c2047486b4..7d871ca631 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java @@ -26,7 +26,6 @@ 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; @@ -37,7 +36,6 @@ import sonia.scm.xml.XmlStreams; import javax.xml.bind.JAXBElement; import javax.xml.bind.Marshaller; -import javax.xml.bind.Unmarshaller; import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamReader; import java.io.File; @@ -45,6 +43,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.Map.Entry; +import java.util.function.Predicate; //~--- JDK imports ------------------------------------------------------------ @@ -152,7 +151,7 @@ public class JAXBConfigurationEntryStore implements ConfigurationEntryStore getMatchingValues(Predicate predicate) { - return Collections2.filter(entries.values(), predicate); + return Collections2.filter(entries.values(), predicate::test); } private void load() { From 58876e4e911f3c751860b29a40627a507571a94b Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 28 Apr 2020 15:40:35 +0200 Subject: [PATCH 7/9] fixed some sonarqube findings --- .../store/JAXBConfigurationEntryStore.java | 28 ++----------------- .../scm/store/JAXBConfigurationStore.java | 7 ----- .../java/sonia/scm/store/JAXBDataStore.java | 9 ------ 3 files changed, 2 insertions(+), 42 deletions(-) diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java index 7d871ca631..b5d20aad53 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBConfigurationEntryStore.java @@ -24,8 +24,6 @@ package sonia.scm.store; -//~--- non-JDK imports -------------------------------------------------------- - import com.google.common.collect.Collections2; import com.google.common.collect.Maps; import org.slf4j.Logger; @@ -45,32 +43,13 @@ import java.util.Map; import java.util.Map.Entry; import java.util.function.Predicate; -//~--- JDK imports ------------------------------------------------------------ +import static javax.xml.stream.XMLStreamConstants.END_ELEMENT; -/** - * @param - * @author Sebastian Sdorra - */ public class JAXBConfigurationEntryStore implements ConfigurationEntryStore { - /** - * 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"; /** @@ -191,7 +170,7 @@ public class JAXBConfigurationEntryStore implements ConfigurationEntryStore implements ConfigurationEntryStore Date: Tue, 28 Apr 2020 16:42:15 +0200 Subject: [PATCH 8/9] fixed compiler error of InMemoryConfigurationEntryStore --- .../java/sonia/scm/store/InMemoryConfigurationEntryStore.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scm-test/src/main/java/sonia/scm/store/InMemoryConfigurationEntryStore.java b/scm-test/src/main/java/sonia/scm/store/InMemoryConfigurationEntryStore.java index d82c1ec3a4..f150b4bdf8 100644 --- a/scm-test/src/main/java/sonia/scm/store/InMemoryConfigurationEntryStore.java +++ b/scm-test/src/main/java/sonia/scm/store/InMemoryConfigurationEntryStore.java @@ -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 implements ConfigurationEntryStore { From a9d95623b9e83f5f0bfbcf4b5019996f5eb1ef95 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Mon, 25 May 2020 13:55:42 +0200 Subject: [PATCH 9/9] fix tests after changed TempDir dependency --- scm-core/pom.xml | 1 - scm-dao-xml/pom.xml | 1 - .../java/sonia/scm/store/TypedStoreContextTest.java | 10 +++++----- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/scm-core/pom.xml b/scm-core/pom.xml index 7d78979d96..a03c1bb72a 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -34,7 +34,6 @@ 2.0.0-SNAPSHOT - sonia.scm scm-core 2.0.0-SNAPSHOT scm-core diff --git a/scm-dao-xml/pom.xml b/scm-dao-xml/pom.xml index 42f49dd8f3..bfb331ede5 100644 --- a/scm-dao-xml/pom.xml +++ b/scm-dao-xml/pom.xml @@ -34,7 +34,6 @@ 2.0.0-SNAPSHOT - sonia.scm scm-dao-xml 2.0.0-SNAPSHOT scm-dao-xml diff --git a/scm-dao-xml/src/test/java/sonia/scm/store/TypedStoreContextTest.java b/scm-dao-xml/src/test/java/sonia/scm/store/TypedStoreContextTest.java index d29fd44298..66a390d2ae 100644 --- a/scm-dao-xml/src/test/java/sonia/scm/store/TypedStoreContextTest.java +++ b/scm-dao-xml/src/test/java/sonia/scm/store/TypedStoreContextTest.java @@ -26,7 +26,7 @@ package sonia.scm.store; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.junitpioneer.jupiter.TempDirectory; +import org.junit.jupiter.api.io.TempDir; import org.mockito.junit.jupiter.MockitoExtension; import javax.xml.bind.annotation.XmlAccessType; @@ -46,11 +46,11 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -@ExtendWith({MockitoExtension.class, TempDirectory.class}) +@ExtendWith(MockitoExtension.class) class TypedStoreContextTest { @Test - void shouldMarshallAndUnmarshall(@TempDirectory.TempDir Path tempDir) { + void shouldMarshallAndUnmarshall(@TempDir Path tempDir) { TypedStoreContext context = context(Sample.class); File file = tempDir.resolve("test.xml").toFile(); @@ -61,7 +61,7 @@ class TypedStoreContextTest { } @Test - void shouldWorkWithMarshallerAndUnmarshaller(@TempDirectory.TempDir Path tempDir) { + void shouldWorkWithMarshallerAndUnmarshaller(@TempDir Path tempDir) { TypedStoreContext context = context(Sample.class); File file = tempDir.resolve("test.xml").toFile(); @@ -101,7 +101,7 @@ class TypedStoreContextTest { } @Test - void shouldConfigureAdapter(@TempDirectory.TempDir Path tempDir) { + void shouldConfigureAdapter(@TempDir Path tempDir) { TypedStoreParameters params = params(SampleWithAdapter.class); when(params.getAdapters()).thenReturn(Collections.singleton(new AppendingAdapter("!")));