From 8be49a1184530c6853e3eb0ad1f649814f7420ac Mon Sep 17 00:00:00 2001 From: Michael Hirsch Date: Mon, 14 Nov 2016 11:23:50 +0100 Subject: [PATCH] Add filesystem artifact repository implementation (#336) Signed-off-by: Michael Hirsch --- examples/hawkbit-example-app/.gitignore | 1 + .../README.md | 23 +++ .../pom.xml | 3 +- .../artifact/repository/GridFsArtifact.java | 0 .../repository/MongoDBArtifactStore.java | 11 +- ...MongoDBArtifactStoreAutoConfiguration.java | 7 +- .../main/resources/META-INF/spring.factories | 2 +- .../hawkbit/artifact/TestConfiguration.java | 0 .../repository/MongoDBArtifactStoreTest.java | 6 +- extensions/pom.xml | 1 + .../.gitignore | 1 + .../README.md | 17 ++ .../pom.xml | 69 +++++++ .../repository/ArtifactFilesystem.java | 44 ++++ .../ArtifactFilesystemProperties.java | 32 +++ .../ArtifactFilesystemRepository.java | 188 ++++++++++++++++++ .../ArtifactFilesystemRepositoryTest.java | 93 +++++++++ .../repository/ArtifactFilesystemTest.java | 52 +++++ hawkbit-artifact-repository-mongo/README.md | 3 - hawkbit-autoconfigure/pom.xml | 11 +- .../ArtifactStoreAutoConfiguration.java | 36 ++++ .../main/resources/META-INF/spring.factories | 3 +- hawkbit-ddi-resource/pom.xml | 5 - .../resource/DdiArtifactDownloadTest.java | 4 +- .../rest/resource/DdiDeploymentBaseTest.java | 4 +- .../rest/resource/DdiRootControllerTest.java | 4 +- hawkbit-dmf-amqp/pom.xml | 5 - hawkbit-mgmt-resource/pom.xml | 5 - .../rest/resource/MgmtDownloadResource.java | 5 +- .../resource/MgmtDownloadResourceTest.java | 4 +- .../MgmtSoftwareModuleResourceTest.java | 17 +- ...MRessourceMisingMongoDbConnectionTest.java | 77 ------- .../hawkbit-repository-jpa/pom.xml | 2 +- .../repository/jpa/model/JpaArtifact.java | 8 +- .../jpa/AbstractJpaIntegrationTest.java | 4 - ...AbstractJpaIntegrationTestWithMongoDB.java | 73 ------- .../ArtifactManagementFailedMongoDBTest.java | 73 ------- .../jpa/ArtifactManagementTest.java | 45 ++--- .../jpa/SoftwareManagementTest.java | 29 +-- .../repository/jpa/SystemManagementTest.java | 2 +- .../TenantConfigurationManagementTest.java | 2 +- .../hawkbit-repository-test/pom.xml | 6 +- .../eclipse/hawkbit/TestConfiguration.java | 19 +- .../test/util/AbstractIntegrationTest.java | 32 +-- .../util/RestResourceConversionHelper.java | 34 ++-- ...bstractRestIntegrationTestWithMongoDB.java | 33 --- hawkbit-ui/pom.xml | 5 - pom.xml | 7 +- 48 files changed, 682 insertions(+), 425 deletions(-) create mode 100644 extensions/hawkbit-extension-artifact-repository-mongo/README.md rename {hawkbit-artifact-repository-mongo => extensions/hawkbit-extension-artifact-repository-mongo}/pom.xml (97%) rename {hawkbit-artifact-repository-mongo => extensions/hawkbit-extension-artifact-repository-mongo}/src/main/java/org/eclipse/hawkbit/artifact/repository/GridFsArtifact.java (100%) rename hawkbit-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactStore.java => extensions/hawkbit-extension-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/MongoDBArtifactStore.java (96%) rename hawkbit-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactStoreAutoConfiguration.java => extensions/hawkbit-extension-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/MongoDBArtifactStoreAutoConfiguration.java (79%) rename {hawkbit-artifact-repository-mongo => extensions/hawkbit-extension-artifact-repository-mongo}/src/main/resources/META-INF/spring.factories (51%) rename {hawkbit-artifact-repository-mongo => extensions/hawkbit-extension-artifact-repository-mongo}/src/test/java/org/eclipse/hawkbit/artifact/TestConfiguration.java (100%) rename hawkbit-artifact-repository-mongo/src/test/java/org/eclipse/hawkbit/artifact/repository/ArtifactStoreTest.java => extensions/hawkbit-extension-artifact-repository-mongo/src/test/java/org/eclipse/hawkbit/artifact/repository/MongoDBArtifactStoreTest.java (94%) create mode 100644 hawkbit-artifact-repository-filesystem/.gitignore create mode 100644 hawkbit-artifact-repository-filesystem/README.md create mode 100644 hawkbit-artifact-repository-filesystem/pom.xml create mode 100644 hawkbit-artifact-repository-filesystem/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystem.java create mode 100644 hawkbit-artifact-repository-filesystem/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemProperties.java create mode 100644 hawkbit-artifact-repository-filesystem/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemRepository.java create mode 100644 hawkbit-artifact-repository-filesystem/src/test/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemRepositoryTest.java create mode 100644 hawkbit-artifact-repository-filesystem/src/test/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemTest.java delete mode 100644 hawkbit-artifact-repository-mongo/README.md create mode 100644 hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/repository/ArtifactStoreAutoConfiguration.java delete mode 100644 hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/SMRessourceMisingMongoDbConnectionTest.java delete mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTestWithMongoDB.java delete mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementFailedMongoDBTest.java delete mode 100644 hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/AbstractRestIntegrationTestWithMongoDB.java diff --git a/examples/hawkbit-example-app/.gitignore b/examples/hawkbit-example-app/.gitignore index b83d22266..0b628eb5a 100644 --- a/examples/hawkbit-example-app/.gitignore +++ b/examples/hawkbit-example-app/.gitignore @@ -1 +1,2 @@ /target/ +/artifactrepo/* diff --git a/extensions/hawkbit-extension-artifact-repository-mongo/README.md b/extensions/hawkbit-extension-artifact-repository-mongo/README.md new file mode 100644 index 000000000..bd0d2951d --- /dev/null +++ b/extensions/hawkbit-extension-artifact-repository-mongo/README.md @@ -0,0 +1,23 @@ +# Eclipse.IoT hawkBit - Artifact Repository MongoDB +HawkBit Artifact Repository is a library for storing binary artifacts and metadata into MongoDB. + + +## Using Artifact Repository MongoDB Extension +The module contains a spring-boot autoconfiguration for easily integration into spring-boot projects. +For using this extension in the hawkbit-example-application you just need to add the maven dependency. +``` + + org.eclipse.hawkbit + hawkbit-extension-artifact-repository-mongo + ${project.version} + +``` + +If you do not have a mongoDB running you can use the the flapdoodle project to download and start an mongoDB on demand +``` + + de.flapdoodle.embed + de.flapdoodle.embed.mongo + ${flapdoodle.version} + +``` diff --git a/hawkbit-artifact-repository-mongo/pom.xml b/extensions/hawkbit-extension-artifact-repository-mongo/pom.xml similarity index 97% rename from hawkbit-artifact-repository-mongo/pom.xml rename to extensions/hawkbit-extension-artifact-repository-mongo/pom.xml index fb1d7df41..1270d0681 100644 --- a/hawkbit-artifact-repository-mongo/pom.xml +++ b/extensions/hawkbit-extension-artifact-repository-mongo/pom.xml @@ -16,7 +16,7 @@ 0.2.0-SNAPSHOT hawkbit-parent - hawkbit-artifact-repository-mongo + hawkbit-extension-artifact-repository-mongo hawkBit :: Artifact Repository Mongo @@ -70,7 +70,6 @@ de.flapdoodle.embed.mongo test - org.mockito diff --git a/hawkbit-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/GridFsArtifact.java b/extensions/hawkbit-extension-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/GridFsArtifact.java similarity index 100% rename from hawkbit-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/GridFsArtifact.java rename to extensions/hawkbit-extension-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/GridFsArtifact.java diff --git a/hawkbit-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactStore.java b/extensions/hawkbit-extension-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/MongoDBArtifactStore.java similarity index 96% rename from hawkbit-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactStore.java rename to extensions/hawkbit-extension-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/MongoDBArtifactStore.java index 1edfe4afb..a6b4b4084 100644 --- a/hawkbit-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactStore.java +++ b/extensions/hawkbit-extension-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/MongoDBArtifactStore.java @@ -44,13 +44,14 @@ import com.mongodb.gridfs.GridFSFile; * The file management which looks up all the file in the file tore. * */ -public class ArtifactStore implements ArtifactRepository { +public class MongoDBArtifactStore implements ArtifactRepository { - private static final Logger LOGGER = LoggerFactory.getLogger(ArtifactStore.class); + private static final Logger LOGGER = LoggerFactory.getLogger(MongoDBArtifactStore.class); /** - * The mongoDB field which holds the filename of the file to download. SP - * Server uses the SHA hash as a filename and lookup in the mongoDB. + * The mongoDB field which holds the filename of the file to download. + * hawkBit update-server uses the SHA hash as a filename and lookup in the + * mongoDB. */ private static final String FILENAME = "filename"; @@ -206,7 +207,7 @@ public class ArtifactStore implements ArtifactRepository { * @return a paged list of artifacts mapped from the given dbFiles */ private List map(final List dbFiles) { - return dbFiles.stream().map(ArtifactStore::map).collect(Collectors.toList()); + return dbFiles.stream().map(MongoDBArtifactStore::map).collect(Collectors.toList()); } /** diff --git a/hawkbit-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactStoreAutoConfiguration.java b/extensions/hawkbit-extension-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/MongoDBArtifactStoreAutoConfiguration.java similarity index 79% rename from hawkbit-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactStoreAutoConfiguration.java rename to extensions/hawkbit-extension-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/MongoDBArtifactStoreAutoConfiguration.java index 38df78dcb..9014ca1c8 100644 --- a/hawkbit-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactStoreAutoConfiguration.java +++ b/extensions/hawkbit-extension-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/MongoDBArtifactStoreAutoConfiguration.java @@ -13,17 +13,16 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** - * Auto configuration for the {@link ArtifactStore}. + * Auto configuration for the {@link MongoDBArtifactStore}. */ @Configuration -@ConditionalOnMissingBean(value = ArtifactRepository.class) -public class ArtifactStoreAutoConfiguration { +public class MongoDBArtifactStoreAutoConfiguration { /** * @return Default {@link ArtifactRepository} implementation. */ @Bean public ArtifactRepository artifactRepository() { - return new ArtifactStore(); + return new MongoDBArtifactStore(); } } diff --git a/hawkbit-artifact-repository-mongo/src/main/resources/META-INF/spring.factories b/extensions/hawkbit-extension-artifact-repository-mongo/src/main/resources/META-INF/spring.factories similarity index 51% rename from hawkbit-artifact-repository-mongo/src/main/resources/META-INF/spring.factories rename to extensions/hawkbit-extension-artifact-repository-mongo/src/main/resources/META-INF/spring.factories index 35e5415c9..09c60408f 100644 --- a/hawkbit-artifact-repository-mongo/src/main/resources/META-INF/spring.factories +++ b/extensions/hawkbit-extension-artifact-repository-mongo/src/main/resources/META-INF/spring.factories @@ -1,3 +1,3 @@ # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -org.eclipse.hawkbit.artifact.repository.ArtifactStoreAutoConfiguration \ No newline at end of file +org.eclipse.hawkbit.artifact.repository.MongoDBArtifactStoreAutoConfiguration \ No newline at end of file diff --git a/hawkbit-artifact-repository-mongo/src/test/java/org/eclipse/hawkbit/artifact/TestConfiguration.java b/extensions/hawkbit-extension-artifact-repository-mongo/src/test/java/org/eclipse/hawkbit/artifact/TestConfiguration.java similarity index 100% rename from hawkbit-artifact-repository-mongo/src/test/java/org/eclipse/hawkbit/artifact/TestConfiguration.java rename to extensions/hawkbit-extension-artifact-repository-mongo/src/test/java/org/eclipse/hawkbit/artifact/TestConfiguration.java diff --git a/hawkbit-artifact-repository-mongo/src/test/java/org/eclipse/hawkbit/artifact/repository/ArtifactStoreTest.java b/extensions/hawkbit-extension-artifact-repository-mongo/src/test/java/org/eclipse/hawkbit/artifact/repository/MongoDBArtifactStoreTest.java similarity index 94% rename from hawkbit-artifact-repository-mongo/src/test/java/org/eclipse/hawkbit/artifact/repository/ArtifactStoreTest.java rename to extensions/hawkbit-extension-artifact-repository-mongo/src/test/java/org/eclipse/hawkbit/artifact/repository/MongoDBArtifactStoreTest.java index 9ac19cde4..338ad62db 100644 --- a/hawkbit-artifact-repository-mongo/src/test/java/org/eclipse/hawkbit/artifact/repository/ArtifactStoreTest.java +++ b/extensions/hawkbit-extension-artifact-repository-mongo/src/test/java/org/eclipse/hawkbit/artifact/repository/MongoDBArtifactStoreTest.java @@ -34,12 +34,12 @@ import ru.yandex.qatools.allure.annotations.Stories; @Features("Component Tests - Repository") @Stories("Artifact Store MongoDB") @RunWith(SpringJUnit4ClassRunner.class) -@SpringApplicationConfiguration(classes = { ArtifactStoreAutoConfiguration.class, TestConfiguration.class }) +@SpringApplicationConfiguration(classes = { MongoDBArtifactStoreAutoConfiguration.class, TestConfiguration.class }) @TestPropertySource(properties = { "spring.data.mongodb.port=0", "spring.mongodb.embedded.version=3.2.7" }) -public class ArtifactStoreTest { +public class MongoDBArtifactStoreTest { @Autowired - private ArtifactStore artifactStoreUnderTest; + private MongoDBArtifactStore artifactStoreUnderTest; @Autowired private GridFsOperations gridFs; diff --git a/extensions/pom.xml b/extensions/pom.xml index d98ad0c48..160f7287c 100644 --- a/extensions/pom.xml +++ b/extensions/pom.xml @@ -22,6 +22,7 @@ hawkbit-extension-uaa + hawkbit-extension-artifact-repository-mongo diff --git a/hawkbit-artifact-repository-filesystem/.gitignore b/hawkbit-artifact-repository-filesystem/.gitignore new file mode 100644 index 000000000..2316d547d --- /dev/null +++ b/hawkbit-artifact-repository-filesystem/.gitignore @@ -0,0 +1 @@ +/artifactrepo/* diff --git a/hawkbit-artifact-repository-filesystem/README.md b/hawkbit-artifact-repository-filesystem/README.md new file mode 100644 index 000000000..2c1e51bbb --- /dev/null +++ b/hawkbit-artifact-repository-filesystem/README.md @@ -0,0 +1,17 @@ +# Eclipse.IoT hawkBit - Artifact Repository File System + +This module contains the implementation of the `ArtifactRepository` based on the file-system. +It's a very convenient and easy implementation of storing the artifact binaries into the file-system based on the SHA-1 hash naming. + +Due the limit of many file-systems of files within one directory, the files +are stored in different sub-directories based on the last four digits of the +SHA1-hash `/basepath/[two digit sha1]/[two digit sha1/sha1-hash-filename]`. + +# Compile + +#### Build hawkbit-artifact-repository-filesystem + +``` +$ cd hawkbit/hawkbit-artifact-repository-filesystem +$ mvn clean install +``` diff --git a/hawkbit-artifact-repository-filesystem/pom.xml b/hawkbit-artifact-repository-filesystem/pom.xml new file mode 100644 index 000000000..0d9500c07 --- /dev/null +++ b/hawkbit-artifact-repository-filesystem/pom.xml @@ -0,0 +1,69 @@ + + + 4.0.0 + + org.eclipse.hawkbit + hawkbit-parent + 0.2.0-SNAPSHOT + + hawkbit-artifact-repository-filesystem + + + + org.eclipse.hawkbit + hawkbit-core + ${project.version} + + + com.google.guava + guava + + + org.springframework + spring-core + + + org.springframework.boot + spring-boot-autoconfigure + + + commons-io + commons-io + + + org.easytesting + fest-assert-core + test + + + org.easytesting + fest-assert + test + + + org.mockito + mockito-core + test + + + ru.yandex.qatools.allure + allure-junit-adaptor + test + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + diff --git a/hawkbit-artifact-repository-filesystem/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystem.java b/hawkbit-artifact-repository-filesystem/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystem.java new file mode 100644 index 000000000..fed6acc86 --- /dev/null +++ b/hawkbit-artifact-repository-filesystem/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystem.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.artifact.repository; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; + +import org.eclipse.hawkbit.artifact.repository.model.DbArtifact; + +import com.google.common.base.Throwables; + +/** + * A {@link DbArtifact} implementation which dynamically creates a + * {@link FileInputStream} on calling {@link #getFileInputStream()}. + */ +public class ArtifactFilesystem extends DbArtifact { + + private final File file; + + ArtifactFilesystem(final File file) { + this.file = file; + } + + @Override + // suppress warning, this InputStream needs to be closed by the caller, this + // cannot be closed in this method + @SuppressWarnings("squid:S2095") + public InputStream getFileInputStream() { + try { + return new BufferedInputStream(new FileInputStream(file)); + } catch (final FileNotFoundException e) { + throw Throwables.propagate(e); + } + } +} diff --git a/hawkbit-artifact-repository-filesystem/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemProperties.java b/hawkbit-artifact-repository-filesystem/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemProperties.java new file mode 100644 index 000000000..1d19cd36f --- /dev/null +++ b/hawkbit-artifact-repository-filesystem/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemProperties.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.artifact.repository; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Configuration properties for the file-system repository, e.g. the base-path + * to store the files. + */ +@ConfigurationProperties("org.eclipse.hawkbit.repository.file") +public class ArtifactFilesystemProperties { + + /** + * The base-path of the directory to store the artifacts. + */ + private String path = "./artifactrepo"; + + public String getPath() { + return path; + } + + public void setPath(final String path) { + this.path = path; + } +} diff --git a/hawkbit-artifact-repository-filesystem/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemRepository.java b/hawkbit-artifact-repository-filesystem/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemRepository.java new file mode 100644 index 000000000..f8f4801e8 --- /dev/null +++ b/hawkbit-artifact-repository-filesystem/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemRepository.java @@ -0,0 +1,188 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.artifact.repository; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.DigestOutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.List; + +import org.apache.commons.io.FileUtils; +import org.eclipse.hawkbit.artifact.repository.model.DbArtifact; +import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash; + +import com.google.common.base.Splitter; +import com.google.common.io.BaseEncoding; +import com.google.common.io.ByteStreams; + +/** + * Implementation of the {@link ArtifactRepository} to store artifacts on the + * file-system. The files are stored by their SHA1 hash of the artifact binary. + * Duplicate files with the same SHA1 hash will only stored once. + * + * All files are stored flat in one base directory configured in the + * {@link ArtifactFilesystemProperties#getPath()}. + * + * Due the limit of many file-systems of files within one directory, the files + * are stored in different sub-directories based on the last four digits of the + * SHA1-hash {@code (/basepath/[two digit sha1]/[two digit sha1])}. + */ +public class ArtifactFilesystemRepository implements ArtifactRepository { + + private static final String TEMP_FILE_PREFIX = "tmp"; + private static final String TEMP_FILE_SUFFIX = "artifactrepo"; + private final ArtifactFilesystemProperties artifactResourceProperties; + + /** + * Constructor. + * + * @param artifactResourceProperties + * the properties which holds the necessary configuration for the + * file-system repository + */ + public ArtifactFilesystemRepository(final ArtifactFilesystemProperties artifactResourceProperties) { + this.artifactResourceProperties = artifactResourceProperties; + } + + @Override + public ArtifactFilesystem store(final InputStream content, final String filename, final String contentType) { + return store(content, filename, contentType, null); + } + + @Override + // suppress warning, of not strong enough hashing algorithm, SHA-1 and MD5 + // is not used security related + @SuppressWarnings("squid:S2070") + public ArtifactFilesystem store(final InputStream content, final String filename, final String contentType, + final DbArtifactHash hash) { + + final MessageDigest mdSHA1; + final MessageDigest mdMD5; + try { + mdSHA1 = MessageDigest.getInstance("SHA1"); + mdMD5 = MessageDigest.getInstance("MD5"); + } catch (final NoSuchAlgorithmException e) { + throw new ArtifactStoreException(e.getMessage(), e); + } + + final File file = createTempFile(); + final DbArtifact artifact = store(content, contentType, hash, mdSHA1, mdMD5, file); + return renameFileToSHA1Naming(file, artifact); + } + + @Override + public void deleteBySha1(final String sha1Hash) { + FileUtils.deleteQuietly(getFile(sha1Hash)); + } + + @Override + public ArtifactFilesystem getArtifactBySha1(final String sha1) { + final File file = getFile(sha1); + if (!file.exists()) { + return null; + } + final ArtifactFilesystem artifact = new ArtifactFilesystem(file); + artifact.setArtifactId(sha1); + artifact.setHashes(new DbArtifactHash(sha1, null)); + artifact.setSize(file.length()); + return artifact; + } + + private DbArtifact store(final InputStream content, final String contentType, final DbArtifactHash hash, + final MessageDigest mdSHA1, final MessageDigest mdMD5, final File file) { + final DbArtifact artifact = new DbArtifact(); + try (final DigestOutputStream outputstream = openFileOutputStream(file, mdSHA1, mdMD5)) { + final long artifactSize = ByteStreams.copy(content, outputstream); + outputstream.flush(); + final String sha1Hash = BaseEncoding.base16().lowerCase().encode(mdSHA1.digest()); + final String md5Hash = BaseEncoding.base16().lowerCase().encode(mdMD5.digest()); + artifact.setArtifactId(sha1Hash); + artifact.setSize(artifactSize); + artifact.setContentType(contentType); + artifact.setHashes(new DbArtifactHash(sha1Hash, md5Hash)); + checkHashes(artifact, hash); + } catch (final IOException e) { + throw new ArtifactStoreException(e.getMessage(), e); + } catch (final HashNotMatchException e) { + file.delete(); + throw e; + } + return artifact; + } + + private ArtifactFilesystem renameFileToSHA1Naming(final File file, final DbArtifact artifact) { + final File fileSHA1Naming = getFile(artifact.getHashes().getSha1()); + final ArtifactFilesystem fileSystemArtifact = new ArtifactFilesystem(fileSHA1Naming); + if (fileSHA1Naming.exists()) { + FileUtils.deleteQuietly(file); + } else if (!file.renameTo(fileSHA1Naming)) { + throw new ArtifactStoreException("Could not store the file " + fileSHA1Naming); + } + + file.delete(); + fileSystemArtifact.setArtifactId(artifact.getArtifactId()); + fileSystemArtifact.setContentType(artifact.getContentType()); + fileSystemArtifact.setHashes(artifact.getHashes()); + fileSystemArtifact.setSize(artifact.getSize()); + return fileSystemArtifact; + } + + private DbArtifact checkHashes(final DbArtifact artifact, final DbArtifactHash hash) { + if (hash == null) { + return artifact; + } + if (hash.getSha1() != null && !artifact.getHashes().getSha1().equals(hash.getSha1())) { + throw new HashNotMatchException("The given sh1 hash " + hash.getSha1() + + " does not match with the calcualted sha1 hash " + artifact.getHashes().getSha1(), + HashNotMatchException.SHA1); + } + if (hash.getMd5() != null && !artifact.getHashes().getMd5().equals(hash.getMd5())) { + throw new HashNotMatchException("The given md5 hash " + hash.getMd5() + + " does not match with the calcualted md5 hash " + artifact.getHashes().getMd5(), + HashNotMatchException.MD5); + } + return artifact; + } + + private File createTempFile() { + try { + return File.createTempFile(TEMP_FILE_PREFIX, TEMP_FILE_SUFFIX); + } catch (final IOException e) { + throw new ArtifactStoreException("Cannot create tempfile", e); + } + } + + private File getFile(final String sha1) { + final File aritfactDirectory = getSha1DirectoryPath(sha1).toFile(); + aritfactDirectory.mkdirs(); + return new File(aritfactDirectory, sha1); + } + + private Path getSha1DirectoryPath(final String sha1) { + final int length = sha1.length(); + final List folders = Splitter.fixedLength(2).splitToList(sha1.substring(length - 4, length)); + final String folder1 = folders.get(0); + final String folder2 = folders.get(1); + return Paths.get(artifactResourceProperties.getPath(), folder1, folder2); + } + + private DigestOutputStream openFileOutputStream(final File file, final MessageDigest mdSHA1, + final MessageDigest mdMD5) throws FileNotFoundException { + return new DigestOutputStream( + new DigestOutputStream(new BufferedOutputStream(new FileOutputStream(file)), mdMD5), mdSHA1); + } +} diff --git a/hawkbit-artifact-repository-filesystem/src/test/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemRepositoryTest.java b/hawkbit-artifact-repository-filesystem/src/test/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemRepositoryTest.java new file mode 100644 index 000000000..40006d086 --- /dev/null +++ b/hawkbit-artifact-repository-filesystem/src/test/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemRepositoryTest.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.artifact.repository; + +import static org.fest.assertions.Assertions.assertThat; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Random; + +import org.apache.commons.io.IOUtils; +import org.eclipse.hawkbit.artifact.repository.model.DbArtifact; +import org.fest.assertions.api.Assertions; +import org.junit.Test; + +import ru.yandex.qatools.allure.annotations.Description; +import ru.yandex.qatools.allure.annotations.Features; +import ru.yandex.qatools.allure.annotations.Stories; + +@Features("Unit Tests - Artifact File System Repository") +@Stories("Test storing artifact binaries in the file-system") +public class ArtifactFilesystemRepositoryTest { + + private final ArtifactFilesystemProperties artifactResourceProperties = new ArtifactFilesystemProperties(); + + private final ArtifactFilesystemRepository artifactFilesystemRepository = new ArtifactFilesystemRepository( + artifactResourceProperties); + + @Test + @Description("Verfies that an artifact can be successfully stored in the file-system repository") + public void storeSuccessfully() throws IOException { + final byte[] fileContent = randomBytes(); + final ArtifactFilesystem artifact = storeRandomArtifact(fileContent); + + final byte[] readContent = new byte[fileContent.length]; + IOUtils.read(artifact.getFileInputStream(), readContent); + + assertThat(readContent).isEqualTo(fileContent); + } + + @Test + @Description("Verfies that an artifact can be successfully stored in the file-system repository") + public void getStoredArtifactBasedOnSHA1Hash() { + + final byte[] fileContent = randomBytes(); + final ArtifactFilesystem artifact = storeRandomArtifact(fileContent); + + final DbArtifact artifactBySha1 = artifactFilesystemRepository + .getArtifactBySha1(artifact.getHashes().getSha1()); + assertThat(artifactBySha1).isNotNull(); + } + + @Test + @Description("Verfies that an artifact can be deleted in the file-system repository") + public void deleteStoredArtifactBySHA1Hash() { + final ArtifactFilesystem artifact = storeRandomArtifact(randomBytes()); + + artifactFilesystemRepository.deleteBySha1(artifact.getHashes().getSha1()); + + assertThat(artifactFilesystemRepository.getArtifactBySha1(artifact.getHashes().getSha1())).isNull(); + } + + @Test + @Description("Verfies that an artifact which does not exists is deleted quietly in the file-system repository") + public void deleteArtifactWhichDoesNotExistsBySHA1HashWithoutException() { + try { + artifactFilesystemRepository.deleteBySha1("sha1HashWhichDoesNotExists"); + } catch (final Exception e) { + Assertions.fail("did not expect an exception while deleting a file which does not exists"); + } + } + + private ArtifactFilesystem storeRandomArtifact(final byte[] fileContent) { + final String fileName = "filename.tmp"; + final ByteArrayInputStream inputStream = new ByteArrayInputStream(fileContent); + final ArtifactFilesystem store = artifactFilesystemRepository.store(inputStream, fileName, "application/txt"); + return store; + } + + private static byte[] randomBytes() { + final byte[] randomBytes = new byte[20]; + final Random ran = new Random(); + ran.nextBytes(randomBytes); + return randomBytes; + } + +} diff --git a/hawkbit-artifact-repository-filesystem/src/test/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemTest.java b/hawkbit-artifact-repository-filesystem/src/test/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemTest.java new file mode 100644 index 000000000..cb523577f --- /dev/null +++ b/hawkbit-artifact-repository-filesystem/src/test/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemTest.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.artifact.repository; + +import static org.fest.assertions.Assertions.assertThat; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; + +import org.apache.commons.io.IOUtils; +import org.fest.assertions.api.Assertions; +import org.junit.Test; + +import ru.yandex.qatools.allure.annotations.Description; +import ru.yandex.qatools.allure.annotations.Features; +import ru.yandex.qatools.allure.annotations.Stories; + +@Features("Unit Tests - Artifact File System Repository") +@Stories("Test storing artifact binaries in the file-system") +public class ArtifactFilesystemTest { + + @Test + @Description("Verifies that an exception is thrown on opening an InputStream when file does not exists") + public void getInputStreamOfNonExistingFileThrowsException() { + final File file = new File("fileWhichTotalDoesNotExists"); + final ArtifactFilesystem underTest = new ArtifactFilesystem(file); + try { + underTest.getFileInputStream(); + Assertions.fail("Expected a FileNotFoundException because file does not exists"); + } catch (final RuntimeException e) { + assertThat(e.getCause()).isInstanceOf(FileNotFoundException.class); + } + } + + @Test + @Description("Verifies that an InputStream can be opened if file exists") + public void getInputStreamOfExistingFile() throws IOException { + final File createTempFile = File.createTempFile(ArtifactFilesystemTest.class.getSimpleName(), ""); + createTempFile.deleteOnExit(); + + final ArtifactFilesystem underTest = new ArtifactFilesystem(createTempFile); + final byte[] buffer = new byte[1024]; + IOUtils.read(underTest.getFileInputStream(), buffer); + } +} diff --git a/hawkbit-artifact-repository-mongo/README.md b/hawkbit-artifact-repository-mongo/README.md deleted file mode 100644 index 3f05d04e6..000000000 --- a/hawkbit-artifact-repository-mongo/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# HawkBit Artifact Repositoy -HawkBit Artifact Repository is library for storing binary artifacts and metadata into MongoDB. -It has an spring-boot autoconfiguration for easily integration into spring-boot projects. \ No newline at end of file diff --git a/hawkbit-autoconfigure/pom.xml b/hawkbit-autoconfigure/pom.xml index 7eb1b6d3a..e9b102a88 100644 --- a/hawkbit-autoconfigure/pom.xml +++ b/hawkbit-autoconfigure/pom.xml @@ -8,8 +8,7 @@ http://www.eclipse.org/legal/epl-v10.html --> - + 4.0.0 org.eclipse.hawkbit @@ -68,7 +67,13 @@ ${project.version} true - + + org.eclipse.hawkbit + hawkbit-artifact-repository-filesystem + ${project.version} + true + + org.springframework spring-context-support diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/repository/ArtifactStoreAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/repository/ArtifactStoreAutoConfiguration.java new file mode 100644 index 000000000..f75343538 --- /dev/null +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/repository/ArtifactStoreAutoConfiguration.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.autoconfigure.repository; + +import org.eclipse.hawkbit.artifact.repository.ArtifactFilesystemProperties; +import org.eclipse.hawkbit.artifact.repository.ArtifactFilesystemRepository; +import org.eclipse.hawkbit.artifact.repository.ArtifactRepository; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Auto configuration for the {@link ArtifactFilesystemRepository}. + */ +@Configuration +@ConditionalOnMissingBean(ArtifactRepository.class) +@EnableConfigurationProperties(ArtifactFilesystemProperties.class) +public class ArtifactStoreAutoConfiguration { + + /** + * @param artifactFilesystemProperties + * the artifact file system properties + * @return Default {@link ArtifactRepository} implementation. + */ + @Bean + public ArtifactRepository artifactRepository(final ArtifactFilesystemProperties artifactFilesystemProperties) { + return new ArtifactFilesystemRepository(artifactFilesystemProperties); + } +} diff --git a/hawkbit-autoconfigure/src/main/resources/META-INF/spring.factories b/hawkbit-autoconfigure/src/main/resources/META-INF/spring.factories index 176c84bb0..86bde0447 100644 --- a/hawkbit-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/hawkbit-autoconfigure/src/main/resources/META-INF/spring.factories @@ -11,4 +11,5 @@ org.eclipse.hawkbit.autoconfigure.event.EventPublisherAutoConfiguration,\ org.eclipse.hawkbit.autoconfigure.scheduling.AsyncConfigurerAutoConfiguration,\ org.eclipse.hawkbit.autoconfigure.scheduling.ExecutorAutoConfiguration,\ org.eclipse.hawkbit.autoconfigure.amqp.AmqpAutoConfiguration,\ -org.eclipse.hawkbit.autoconfigure.security.InMemoryUserManagementConfiguration +org.eclipse.hawkbit.autoconfigure.security.InMemoryUserManagementConfiguration,\ +org.eclipse.hawkbit.autoconfigure.repository.ArtifactStoreAutoConfiguration diff --git a/hawkbit-ddi-resource/pom.xml b/hawkbit-ddi-resource/pom.xml index 9664789eb..c798534aa 100644 --- a/hawkbit-ddi-resource/pom.xml +++ b/hawkbit-ddi-resource/pom.xml @@ -133,11 +133,6 @@ fest-assert test - - de.flapdoodle.embed - de.flapdoodle.embed.mongo - test - ru.yandex.qatools.allure allure-junit-adaptor diff --git a/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactDownloadTest.java b/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactDownloadTest.java index d293afcd6..de42360fe 100644 --- a/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactDownloadTest.java +++ b/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactDownloadTest.java @@ -38,7 +38,7 @@ import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.test.util.WithUser; -import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB; +import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest; import org.junit.Before; import org.junit.Test; import org.springframework.boot.test.SpringApplicationConfiguration; @@ -62,7 +62,7 @@ import ru.yandex.qatools.allure.annotations.Stories; @Features("Component Tests - Direct Device Integration API") @Stories("Artifact Download Resource") @SpringApplicationConfiguration(classes = DownloadTestConfiguration.class) -public class DdiArtifactDownloadTest extends AbstractRestIntegrationTestWithMongoDB { +public class DdiArtifactDownloadTest extends AbstractRestIntegrationTest { private static final int ARTIFACT_SIZE = 5 * 1024 * 1024; diff --git a/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java b/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java index 7e13b8755..762e6f35c 100644 --- a/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java +++ b/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java @@ -37,7 +37,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult; import org.eclipse.hawkbit.repository.model.RepositoryModelConstants; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; -import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB; +import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest; import org.eclipse.hawkbit.rest.util.JsonBuilder; import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter; import org.fest.assertions.core.Condition; @@ -59,7 +59,7 @@ import ru.yandex.qatools.allure.annotations.Stories; */ @Features("Component Tests - Direct Device Integration API") @Stories("Deployment Action Resource") -public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoDB { +public class DdiDeploymentBaseTest extends AbstractRestIntegrationTest { private static final String HTTP_LOCALHOST = "http://localhost:8080/"; diff --git a/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java b/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java index 843b26479..7e7fb6c23 100644 --- a/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java +++ b/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java @@ -38,7 +38,7 @@ import org.eclipse.hawkbit.repository.test.matcher.Expect; import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents; import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule; import org.eclipse.hawkbit.repository.test.util.WithUser; -import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB; +import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest; import org.eclipse.hawkbit.rest.util.JsonBuilder; import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter; import org.eclipse.hawkbit.security.HawkbitSecurityProperties; @@ -57,7 +57,7 @@ import ru.yandex.qatools.allure.annotations.Stories; */ @Features("Component Tests - Direct Device Integration API") @Stories("Root Poll Resource") -public class DdiRootControllerTest extends AbstractRestIntegrationTestWithMongoDB { +public class DdiRootControllerTest extends AbstractRestIntegrationTest { @Autowired private HawkbitSecurityProperties securityProperties; diff --git a/hawkbit-dmf-amqp/pom.xml b/hawkbit-dmf-amqp/pom.xml index b9541f85e..540b4d53d 100644 --- a/hawkbit-dmf-amqp/pom.xml +++ b/hawkbit-dmf-amqp/pom.xml @@ -137,11 +137,6 @@ fest-assert test - - de.flapdoodle.embed - de.flapdoodle.embed.mongo - test - org.springframework.security spring-security-config diff --git a/hawkbit-mgmt-resource/pom.xml b/hawkbit-mgmt-resource/pom.xml index a425a4db7..173507809 100644 --- a/hawkbit-mgmt-resource/pom.xml +++ b/hawkbit-mgmt-resource/pom.xml @@ -131,11 +131,6 @@ fest-assert test - - de.flapdoodle.embed - de.flapdoodle.embed.mongo - test - ru.yandex.qatools.allure allure-junit-adaptor diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDownloadResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDownloadResource.java index 14b09cbb0..32976f4a6 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDownloadResource.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDownloadResource.java @@ -9,6 +9,7 @@ package org.eclipse.hawkbit.mgmt.rest.resource; import java.io.IOException; +import java.io.InputStream; import org.eclipse.hawkbit.artifact.repository.ArtifactRepository; import org.eclipse.hawkbit.artifact.repository.model.DbArtifact; @@ -82,8 +83,8 @@ public class MgmtDownloadResource implements MgmtDownloadRestApi { artifactCache.getId(), artifactCache.getDownloadType()); return new ResponseEntity<>(HttpStatus.NOT_FOUND); } - try { - ByteStreams.copy(artifact.getFileInputStream(), + try (InputStream inputstream = artifact.getFileInputStream()) { + ByteStreams.copy(inputstream, requestResponseContextHolder.getHttpServletResponse().getOutputStream()); } catch (final IOException e) { LOGGER.error("Cannot copy streams", e); diff --git a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDownloadResourceTest.java b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDownloadResourceTest.java index 92552954c..7ae128996 100644 --- a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDownloadResourceTest.java +++ b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDownloadResourceTest.java @@ -18,7 +18,7 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.SoftwareModule; -import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB; +import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest; import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter; import org.junit.Before; import org.junit.Test; @@ -30,7 +30,7 @@ import ru.yandex.qatools.allure.annotations.Stories; @Features("Component Tests - Management API") @Stories("Download Resource") -public class MgmtDownloadResourceTest extends AbstractRestIntegrationTestWithMongoDB { +public class MgmtDownloadResourceTest extends AbstractRestIntegrationTest { @Autowired private DownloadIdCache downloadIdCache; diff --git a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java index 2bda57ddf..7c3099ecb 100644 --- a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java +++ b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java @@ -27,6 +27,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -43,7 +44,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata; import org.eclipse.hawkbit.repository.test.util.HashGeneratorUtils; import org.eclipse.hawkbit.repository.test.util.WithUser; -import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB; +import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest; import org.eclipse.hawkbit.rest.json.model.ExceptionInfo; import org.eclipse.hawkbit.rest.util.JsonBuilder; import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter; @@ -70,7 +71,7 @@ import ru.yandex.qatools.allure.annotations.Stories; */ @Features("Component Tests - Management API") @Stories("Software Module Resource") -public class MgmtSoftwareModuleResourceTest extends AbstractRestIntegrationTestWithMongoDB { +public class MgmtSoftwareModuleResourceTest extends AbstractRestIntegrationTest { @Before public void assertPreparationOfRepo() { @@ -165,12 +166,12 @@ public class MgmtSoftwareModuleResourceTest extends AbstractRestIntegrationTestW assertThat(artifactManagement.countArtifactsAll()).as("Wrong artifact size").isEqualTo(1); // binary - assertTrue("Wrong artifact content", - IOUtils.contentEquals(new ByteArrayInputStream(random), - artifactManagement - .loadArtifactBinary( - softwareManagement.findSoftwareModuleById(sm.getId()).getArtifacts().get(0)) - .getFileInputStream())); + try (InputStream fileInputStream = artifactManagement + .loadArtifactBinary(softwareManagement.findSoftwareModuleById(sm.getId()).getArtifacts().get(0)) + .getFileInputStream()) { + assertTrue("Wrong artifact content", + IOUtils.contentEquals(new ByteArrayInputStream(random), fileInputStream)); + } // hashes assertThat(artifactManagement.findArtifactByFilename("origFilename").get(0).getSha1Hash()).as("Wrong sha1 hash") diff --git a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/SMRessourceMisingMongoDbConnectionTest.java b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/SMRessourceMisingMongoDbConnectionTest.java deleted file mode 100644 index 0c815831d..000000000 --- a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/SMRessourceMisingMongoDbConnectionTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.eclipse.hawkbit.mgmt.rest.resource; - -import static org.fest.assertions.api.Assertions.assertThat; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.fileUpload; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import org.apache.commons.lang3.RandomStringUtils; -import org.eclipse.hawkbit.exception.SpServerError; -import org.eclipse.hawkbit.repository.model.SoftwareModule; -import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB; -import org.eclipse.hawkbit.rest.json.model.ExceptionInfo; -import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter; -import org.junit.After; -import org.junit.Test; -import org.springframework.mock.web.MockMultipartFile; -import org.springframework.test.web.servlet.MvcResult; - -import ru.yandex.qatools.allure.annotations.Description; -import ru.yandex.qatools.allure.annotations.Features; -import ru.yandex.qatools.allure.annotations.Stories; - -/** - * Tests {@link MgmtSoftwareModuleResource} in case of missing MongoDB - * connection. - * - */ -@Features("Component Tests - Management API") -@Stories("Download Resource") -public class SMRessourceMisingMongoDbConnectionTest extends AbstractRestIntegrationTestWithMongoDB { - - @Test - @Description("Ensures that the correct error code is returned in case MongoDB unavailable.") - public void missingMongoDbConnectionResultsInErrorAtUpload() throws Exception { - mongodExecutable.stop(); - - assertThat(softwareManagement.findSoftwareModulesAll(pageReq)).hasSize(0); - assertThat(artifactManagement.countArtifactsAll()).isEqualTo(0); - SoftwareModule sm = entityFactory.generateSoftwareModule(softwareManagement.findSoftwareModuleTypeByKey("os"), - "name 1", "version 1", null, null); - sm = softwareManagement.createSoftwareModule(sm); - assertThat(artifactManagement.countArtifactsAll()).isEqualTo(0); - - // create test file - final byte random[] = RandomStringUtils.random(5 * 1024).getBytes(); - final MockMultipartFile file = new MockMultipartFile("file", "origFilename", null, random); - - // upload - final MvcResult mvcResult = mvc - .perform(fileUpload("/rest/v1/softwaremodules/{smId}/artifacts", sm.getId()).file(file)) - .andDo(MockMvcResultPrinter.print()).andExpect(status().isInternalServerError()).andReturn(); - - // check error result - final ExceptionInfo exceptionInfo = ResourceUtility - .convertException(mvcResult.getResponse().getContentAsString()); - assertThat(exceptionInfo.getErrorCode()).isEqualTo(SpServerError.SP_ARTIFACT_UPLOAD_FAILED.getKey()); - assertThat(exceptionInfo.getMessage()).isEqualTo(SpServerError.SP_ARTIFACT_UPLOAD_FAILED.getMessage()); - - // ensure that the JPA transaction was rolled back - assertThat(artifactManagement.countArtifactsAll()).isEqualTo(0); - - } - - @Override - @After - public void cleanCurrentCollection() { - // not needed, mongodb is stopped already - } - -} diff --git a/hawkbit-repository/hawkbit-repository-jpa/pom.xml b/hawkbit-repository/hawkbit-repository-jpa/pom.xml index 42fc9a62f..d1f275f81 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/pom.xml +++ b/hawkbit-repository/hawkbit-repository-jpa/pom.xml @@ -49,7 +49,7 @@ org.eclipse.hawkbit - hawkbit-artifact-repository-mongo + hawkbit-artifact-repository-filesystem ${project.version} diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaArtifact.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaArtifact.java index 13be80d71..847250491 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaArtifact.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaArtifact.java @@ -20,12 +20,10 @@ import javax.persistence.Table; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; +import org.eclipse.hawkbit.artifact.repository.model.DbArtifact; import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.SoftwareModule; -import com.mongodb.gridfs.GridFS; -import com.mongodb.gridfs.GridFSFile; - /** * JPA implementation of {@link LocalArtifact}. * @@ -73,9 +71,9 @@ public class JpaArtifact extends AbstractJpaTenantAwareBaseEntity implements Art * Constructs artifact. * * @param gridFsFileName - * that is the link to the {@link GridFS} entity. + * that is the link to the {@link DbArtifact} entity. * @param filename - * that is used by {@link GridFSFile} store. + * that is used by {@link DbArtifact} store. * @param softwareModule * of this artifact */ diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTest.java index a4b929eb0..3b22f9f74 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTest.java @@ -15,7 +15,6 @@ import org.eclipse.hawkbit.cache.TenantAwareCacheManager; import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.SpringApplicationConfiguration; -import org.springframework.data.mongodb.gridfs.GridFsOperations; @SpringApplicationConfiguration(classes = { org.eclipse.hawkbit.RepositoryApplicationConfiguration.class }) public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest { @@ -62,9 +61,6 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest @Autowired protected TargetInfoRepository targetInfoRepository; - @Autowired - protected GridFsOperations operations; - @Autowired protected RolloutGroupRepository rolloutGroupRepository; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTestWithMongoDB.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTestWithMongoDB.java deleted file mode 100644 index d71accc46..000000000 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTestWithMongoDB.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.eclipse.hawkbit.repository.jpa; - -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; - -import org.eclipse.hawkbit.cache.TenantAwareCacheManager; -import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.SpringApplicationConfiguration; - -@SpringApplicationConfiguration(classes = { org.eclipse.hawkbit.RepositoryApplicationConfiguration.class }) -public abstract class AbstractJpaIntegrationTestWithMongoDB extends AbstractIntegrationTest { - - @PersistenceContext - protected EntityManager entityManager; - - @Autowired - protected TargetRepository targetRepository; - - @Autowired - protected ActionRepository actionRepository; - - @Autowired - protected DistributionSetRepository distributionSetRepository; - - @Autowired - protected SoftwareModuleRepository softwareModuleRepository; - - @Autowired - protected TenantMetaDataRepository tenantMetaDataRepository; - - @Autowired - protected DistributionSetTypeRepository distributionSetTypeRepository; - - @Autowired - protected SoftwareModuleTypeRepository softwareModuleTypeRepository; - - @Autowired - protected TargetTagRepository targetTagRepository; - - @Autowired - protected DistributionSetTagRepository distributionSetTagRepository; - - @Autowired - protected SoftwareModuleMetadataRepository softwareModuleMetadataRepository; - - @Autowired - protected ActionStatusRepository actionStatusRepository; - - @Autowired - protected LocalArtifactRepository artifactRepository; - - @Autowired - protected TargetInfoRepository targetInfoRepository; - - @Autowired - protected RolloutGroupRepository rolloutGroupRepository; - - @Autowired - protected RolloutRepository rolloutRepository; - - @Autowired - protected TenantAwareCacheManager cacheManager; - -} diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementFailedMongoDBTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementFailedMongoDBTest.java deleted file mode 100644 index e28d1776a..000000000 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementFailedMongoDBTest.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.eclipse.hawkbit.repository.jpa; - -import static org.fest.assertions.api.Assertions.assertThat; -import static org.junit.Assert.fail; - -import java.io.IOException; -import java.net.UnknownHostException; - -import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException; -import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException; -import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; -import org.eclipse.hawkbit.repository.model.Artifact; -import org.junit.After; -import org.junit.Test; - -import ru.yandex.qatools.allure.annotations.Description; -import ru.yandex.qatools.allure.annotations.Features; -import ru.yandex.qatools.allure.annotations.Stories; - -@Features("Component Tests - Repository") -@Stories("Artifact Management") -public class ArtifactManagementFailedMongoDBTest extends AbstractJpaIntegrationTestWithMongoDB { - - @Test - @Description("Trys and fails to delete or create local artifact with a down mongodb and checks if expected ArtifactDeleteFailedException is thrown.") - public void deleteArtifactsWithNoMongoDb() throws UnknownHostException, IOException { - // ensure baseline - assertThat(artifactRepository.findAll()).isEmpty(); - - // prepare test - JpaSoftwareModule sm = new JpaSoftwareModule(softwareManagement.findSoftwareModuleTypeByKey("os"), "name 1", - "version 1", null, null); - sm = softwareModuleRepository.save(sm); - - final Artifact result = artifactManagement.createArtifact(new RandomGeneratedInputStream(5 * 1024), sm.getId(), - "file1", false); - - assertThat(artifactRepository.findAll()).hasSize(1); - - mongodExecutable.stop(); - try { - artifactManagement.deleteArtifact(result.getId()); - fail("deletion should have failed"); - } catch (final ArtifactDeleteFailedException e) { - - } - - try { - artifactManagement.createArtifact(new RandomGeneratedInputStream(5 * 1024), sm.getId(), "file2", false); - fail("Should not have worked with MongoDb down."); - } catch (final ArtifactUploadFailedException e) { - - } - - assertThat(artifactRepository.findAll()).hasSize(1); - - } - - @Override - @After - public void cleanCurrentCollection() { - // no need to clean, is stopped already - } - -} diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java index 63bffc0bd..0b9bee939 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java @@ -14,6 +14,7 @@ import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.security.NoSuchAlgorithmException; import org.apache.commons.io.IOUtils; @@ -28,8 +29,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.test.util.HashGeneratorUtils; import org.eclipse.hawkbit.repository.test.util.WithUser; import org.junit.Test; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.Query; import ru.yandex.qatools.allure.annotations.Description; import ru.yandex.qatools.allure.annotations.Features; @@ -44,7 +43,7 @@ import ru.yandex.qatools.allure.annotations.Stories; */ @Features("Component Tests - Repository") @Stories("Artifact Management") -public class ArtifactManagementTest extends AbstractJpaIntegrationTestWithMongoDB { +public class ArtifactManagementTest extends AbstractJpaIntegrationTest { /** * Test method for @@ -151,25 +150,17 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTestWithMongoD assertThat(result2.getId()).isNotNull(); assertThat(((JpaArtifact) result).getGridFsFileName()) .isNotEqualTo(((JpaArtifact) result2).getGridFsFileName()); - assertThat(operations.findOne( - new Query().addCriteria(Criteria.where("filename").is(((JpaArtifact) result).getGridFsFileName())))) - .isNotNull(); - assertThat(operations.findOne( - new Query().addCriteria(Criteria.where("filename").is(((JpaArtifact) result2).getGridFsFileName())))) - .isNotNull(); + + assertThat(binaryArtifactRepository.getArtifactBySha1(((JpaArtifact) result).getGridFsFileName())).isNotNull(); + assertThat(binaryArtifactRepository.getArtifactBySha1(((JpaArtifact) result2).getGridFsFileName())).isNotNull(); artifactManagement.deleteArtifact(result.getId()); - assertThat(operations.findOne( - new Query().addCriteria(Criteria.where("filename").is(((JpaArtifact) result).getGridFsFileName())))) - .isNull(); - assertThat(operations.findOne( - new Query().addCriteria(Criteria.where("filename").is(((JpaArtifact) result2).getGridFsFileName())))) - .isNotNull(); + + assertThat(binaryArtifactRepository.getArtifactBySha1(((JpaArtifact) result).getGridFsFileName())).isNull(); + assertThat(binaryArtifactRepository.getArtifactBySha1(((JpaArtifact) result2).getGridFsFileName())).isNotNull(); artifactManagement.deleteArtifact(result2.getId()); - assertThat(operations.findOne( - new Query().addCriteria(Criteria.where("filename").is(((JpaArtifact) result2).getGridFsFileName())))) - .isNull(); + assertThat(binaryArtifactRepository.getArtifactBySha1(((JpaArtifact) result2).getGridFsFileName())).isNull(); assertThat(artifactRepository.findAll()).hasSize(0); } @@ -198,18 +189,12 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTestWithMongoD assertThat(result2.getId()).isNotNull(); assertThat(((JpaArtifact) result).getGridFsFileName()).isEqualTo(((JpaArtifact) result2).getGridFsFileName()); - assertThat(operations.findOne( - new Query().addCriteria(Criteria.where("filename").is(((JpaArtifact) result).getGridFsFileName())))) - .isNotNull(); + assertThat(binaryArtifactRepository.getArtifactBySha1(((JpaArtifact) result).getGridFsFileName())).isNotNull(); artifactManagement.deleteArtifact(result.getId()); - assertThat(operations.findOne( - new Query().addCriteria(Criteria.where("filename").is(((JpaArtifact) result).getGridFsFileName())))) - .isNotNull(); + assertThat(binaryArtifactRepository.getArtifactBySha1(((JpaArtifact) result).getGridFsFileName())).isNotNull(); artifactManagement.deleteArtifact(result2.getId()); - assertThat(operations.findOne( - new Query().addCriteria(Criteria.where("filename").is(((JpaArtifact) result).getGridFsFileName())))) - .isNull(); + assertThat(binaryArtifactRepository.getArtifactBySha1(((JpaArtifact) result).getGridFsFileName())).isNull(); } /** @@ -253,8 +238,10 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTestWithMongoD final Artifact result = artifactManagement.createArtifact(new ByteArrayInputStream(random), sm.getId(), "file1", false); - assertTrue("The stored binary matches the given binary", IOUtils.contentEquals(new ByteArrayInputStream(random), - artifactManagement.loadArtifactBinary(result).getFileInputStream())); + try (InputStream fileInputStream = artifactManagement.loadArtifactBinary(result).getFileInputStream()) { + assertTrue("The stored binary matches the given binary", + IOUtils.contentEquals(new ByteArrayInputStream(random), fileInputStream)); + } } @Test diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SoftwareManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SoftwareManagementTest.java index 895210da4..4d4577062 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SoftwareManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SoftwareManagementTest.java @@ -45,8 +45,6 @@ import org.eclipse.hawkbit.repository.test.util.WithUser; import org.junit.Test; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.Query; import com.google.common.collect.Lists; import com.google.common.collect.Sets; @@ -57,7 +55,7 @@ import ru.yandex.qatools.allure.annotations.Stories; @Features("Component Tests - Repository") @Stories("Software Management") -public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoDB { +public class SoftwareManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Try to update non updatable fields results in repository doing nothing.") @@ -387,7 +385,7 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD } @Test - @Description("Deletes an artifact, which is assigned to a Distribution Set") + @Description("Deletes an artifact, which is assigned to a DistributionSet") public void softDeleteOfAssignedArtifact() { // Init DistributionSet @@ -464,12 +462,9 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD } @Test - @Description("Delete an softwaremodule with an artifact, which is also used by another softwaremodule.") + @Description("Delete an softwaremodule with an artifact, which is alsoused by another softwaremodule.") public void deleteSoftwareModulesWithSharedArtifact() throws IOException { - // Precondition: Make sure MongoDB is Empty - assertThat(operations.find(new Query())).hasSize(0); - // Init artifact binary data, target and DistributionSets final byte[] source = RandomUtils.nextBytes(1024); @@ -489,9 +484,6 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD moduleY = softwareManagement.findSoftwareModuleById(moduleY.getId()); final Artifact artifactY = moduleY.getArtifacts().iterator().next(); - // verify: that only one entry was created in mongoDB - assertThat(operations.find(new Query())).hasSize(1); - // [STEP5]: Delete SoftwareModuleX softwareManagement.deleteSoftwareModule(moduleX.getId()); @@ -515,9 +507,6 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD @Description("Delete two assigned softwaremodules which share an artifact.") public void deleteMultipleSoftwareModulesWhichShareAnArtifact() throws IOException { - // Precondition: Make sure MongoDB is Empty - assertThat(operations.find(new Query())).hasSize(0); - // Init artifact binary data, target and DistributionSets final byte[] source = RandomUtils.nextBytes(1024); final Target target = targetManagement.createTarget(new JpaTarget("test123")); @@ -540,9 +529,6 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD moduleY = softwareManagement.findSoftwareModuleById(moduleY.getId()); final Artifact artifactY = moduleY.getArtifacts().iterator().next(); - // verify: that only one entry was created in mongoDB - assertThat(operations.find(new Query())).hasSize(1); - // [STEP3]: Assign SoftwareModuleX to DistributionSetX and to target distributionSetManagement.assignSoftwareModules(disSetX, Sets.newHashSet(moduleX)); deploymentManagement.assignDistributionSet(disSetX, Lists.newArrayList(target)); @@ -611,17 +597,14 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD assertThat(artifactRepository.findAll()).hasSize(results.length); for (final Artifact result : results) { assertThat(result.getId()).isNotNull(); - assertThat(operations.findOne( - new Query().addCriteria(Criteria.where("filename").is(((JpaArtifact) result).getGridFsFileName())))) - .isNotNull(); + assertThat(binaryArtifactRepository.getArtifactBySha1(((JpaArtifact) result).getGridFsFileName())) + .isNotNull(); } } private void assertArtfiactNull(final Artifact... results) { for (final Artifact result : results) { - assertThat(operations.findOne( - new Query().addCriteria(Criteria.where("filename").is(((JpaArtifact) result).getGridFsFileName())))) - .isNull(); + assertThat(binaryArtifactRepository.getArtifactBySha1(((JpaArtifact) result).getGridFsFileName())).isNull(); } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SystemManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SystemManagementTest.java index 9cb937eea..4f6d5c91d 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SystemManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SystemManagementTest.java @@ -28,7 +28,7 @@ import ru.yandex.qatools.allure.annotations.Stories; @Features("Component Tests - Repository") @Stories("System Management") -public class SystemManagementTest extends AbstractJpaIntegrationTestWithMongoDB { +public class SystemManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Ensures that findTenants returns all tenants and not only restricted to the tenant which currently is logged in") diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TenantConfigurationManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TenantConfigurationManagementTest.java index 2065e9026..14f387ad1 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TenantConfigurationManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TenantConfigurationManagementTest.java @@ -30,7 +30,7 @@ import ru.yandex.qatools.allure.annotations.Stories; @Features("Component Tests - Repository") @Stories("Tenant Configuration Management") -public class TenantConfigurationManagementTest extends AbstractJpaIntegrationTestWithMongoDB { +public class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Tests that tenant specific configuration can be persisted and in case the tenant does not have specific configuration the default from environment is used instead.") diff --git a/hawkbit-repository/hawkbit-repository-test/pom.xml b/hawkbit-repository/hawkbit-repository-test/pom.xml index 350896f74..c3351ca33 100644 --- a/hawkbit-repository/hawkbit-repository-test/pom.xml +++ b/hawkbit-repository/hawkbit-repository-test/pom.xml @@ -31,7 +31,7 @@ org.eclipse.hawkbit - hawkbit-artifact-repository-mongo + hawkbit-artifact-repository-filesystem ${project.version} @@ -43,8 +43,8 @@ spring-context-support - de.flapdoodle.embed - de.flapdoodle.embed.mongo + org.springframework + spring-tx net._01001111 diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/TestConfiguration.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/TestConfiguration.java index 738513989..e70ef2ca2 100644 --- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/TestConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/TestConfiguration.java @@ -13,6 +13,9 @@ import java.util.concurrent.Executors; import org.eclipse.hawkbit.api.ArtifactUrlHandlerProperties; import org.eclipse.hawkbit.api.PropertyBasedArtifactUrlHandler; +import org.eclipse.hawkbit.artifact.repository.ArtifactFilesystemProperties; +import org.eclipse.hawkbit.artifact.repository.ArtifactFilesystemRepository; +import org.eclipse.hawkbit.artifact.repository.ArtifactRepository; import org.eclipse.hawkbit.cache.DefaultDownloadIdCache; import org.eclipse.hawkbit.cache.DownloadIdCache; import org.eclipse.hawkbit.cache.TenantAwareCacheManager; @@ -53,7 +56,6 @@ import org.springframework.security.concurrent.DelegatingSecurityContextExecutor import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.util.AntPathMatcher; -import com.mongodb.MongoClientOptions; /** * Spring context configuration required for Dev.Environment. @@ -64,10 +66,16 @@ import com.mongodb.MongoClientOptions; @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.PROXY, proxyTargetClass = false, securedEnabled = true) @EnableConfigurationProperties({ HawkbitServerProperties.class, DdiSecurityProperties.class, - ArtifactUrlHandlerProperties.class }) + ArtifactUrlHandlerProperties.class, ArtifactFilesystemProperties.class }) @Profile("test") @EnableAutoConfiguration public class TestConfiguration implements AsyncConfigurer { + + @Bean + public ArtifactRepository artifactRepository(final ArtifactFilesystemProperties artifactFilesystemProperties) { + return new ArtifactFilesystemRepository(artifactFilesystemProperties); + } + @Bean public TestRepositoryManagement testRepositoryManagement(final SystemSecurityContext systemSecurityContext, final SystemManagement systemManagement) { @@ -85,13 +93,6 @@ public class TestConfiguration implements AsyncConfigurer { return new PropertyBasedArtifactUrlHandler(urlHandlerProperties); } - @Bean - public MongoClientOptions options() { - return MongoClientOptions.builder().connectTimeout(500).maxWaitTime(500).connectionsPerHost(2) - .serverSelectionTimeout(500).build(); - - } - @Bean public TenantAware tenantAware() { return new SecurityContextTenantAware(); diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java index caf56426c..7c8fb8150 100644 --- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java @@ -12,8 +12,15 @@ import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpre import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.SYSTEM_ROLE; import static org.junit.rules.RuleChain.outerRule; +import java.io.File; +import java.io.IOException; + +import org.apache.commons.io.FileUtils; import org.eclipse.hawkbit.ExcludePathAwareShallowETagFilter; import org.eclipse.hawkbit.TestConfiguration; +import org.eclipse.hawkbit.artifact.repository.ArtifactFilesystemProperties; +import org.eclipse.hawkbit.artifact.repository.ArtifactRepository; +import org.eclipse.hawkbit.cache.TenantAwareCacheManager; import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ControllerManagement; import org.eclipse.hawkbit.repository.DeploymentManagement; @@ -54,13 +61,10 @@ import org.springframework.core.env.Environment; import org.springframework.data.auditing.AuditingHandler; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.gridfs.GridFsOperations; import org.springframework.hateoas.MediaTypes; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; @@ -68,8 +72,6 @@ import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; -import de.flapdoodle.embed.mongo.MongodExecutable; - @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ActiveProfiles({ "test" }) @@ -80,7 +82,6 @@ import de.flapdoodle.embed.mongo.MongodExecutable; // refreshed we e.g. get two instances of CacheManager which leads to very // strange test failures. @DirtiesContext(classMode = ClassMode.AFTER_CLASS) -@TestPropertySource(properties = { "spring.data.mongodb.port=0", "spring.mongodb.embedded.version=3.2.7" }) public abstract class AbstractIntegrationTest implements EnvironmentAware { private final static Logger LOG = LoggerFactory.getLogger(AbstractIntegrationTest.class); @@ -154,6 +155,15 @@ public abstract class AbstractIntegrationTest implements EnvironmentAware { @Autowired protected SystemSecurityContext systemSecurityContext; + @Autowired + private ArtifactFilesystemProperties artifactFilesystemProperties; + + @Autowired + protected ArtifactRepository binaryArtifactRepository; + + @Autowired + protected TenantAwareCacheManager cacheManager; + protected MockMvc mvc; protected SoftwareModuleType osType; @@ -165,12 +175,6 @@ public abstract class AbstractIntegrationTest implements EnvironmentAware { @Autowired protected TestdataFactory testdataFactory; - @Autowired - protected GridFsOperations operations; - - @Autowired - protected MongodExecutable mongodExecutable; - @Autowired protected ServiceMatcher serviceMatcher; @@ -232,8 +236,8 @@ public abstract class AbstractIntegrationTest implements EnvironmentAware { } @After - public void cleanCurrentCollection() { - operations.delete(new Query()); + public void cleanCurrentCollection() throws IOException { + FileUtils.deleteDirectory(new File(artifactFilesystemProperties.getPath())); } protected DefaultMockMvcBuilder createMvcWebAppContext() { diff --git a/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/RestResourceConversionHelper.java b/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/RestResourceConversionHelper.java index 5fc3c53a8..f85bc2b25 100644 --- a/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/RestResourceConversionHelper.java +++ b/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/RestResourceConversionHelper.java @@ -180,9 +180,9 @@ public final class RestResourceConversionHelper { response.setHeader(CONTENT_RANGE, "bytes " + r.getStart() + "-" + r.getEnd() + "/" + r.getTotal()); response.setHeader(CONTENT_LENGTH, String.valueOf(r.getLength())); - try { - copyStreams(file.getFileInputStream(), response.getOutputStream(), controllerManagement, statusId, - r.getStart(), r.getLength()); + try (InputStream inputStream = file.getFileInputStream()) { + copyStreams(inputStream, response.getOutputStream(), controllerManagement, statusId, r.getStart(), + r.getLength()); } catch (final IOException e) { LOG.error("fullfileRequest of file ({}) failed!", artifact.getFilename(), e); throw new FileSteamingFailedException(artifact.getFilename()); @@ -246,8 +246,9 @@ public final class RestResourceConversionHelper { response.setContentType("multipart/byteranges; boundary=" + MULTIPART_BOUNDARY); response.setStatus(SC_PARTIAL_CONTENT); - try { - for (final ByteRange r : ranges) { + for (final ByteRange r : ranges) { + try (InputStream inputStream = file.getFileInputStream()) { + // Add multipart boundary and header fields for every range. response.getOutputStream().println(); response.getOutputStream().println("--" + MULTIPART_BOUNDARY); @@ -255,19 +256,26 @@ public final class RestResourceConversionHelper { .println("Content-Range: bytes " + r.getStart() + "-" + r.getEnd() + "/" + r.getTotal()); // Copy single part range of multi part range. - copyStreams(file.getFileInputStream(), response.getOutputStream(), controllerManagement, statusId, - r.getStart(), r.getLength()); + copyStreams(inputStream, response.getOutputStream(), controllerManagement, statusId, r.getStart(), + r.getLength()); + } catch (final IOException e) { + throwFileStreamingFailedException(artifact, e); } - + } + try { // End with final multipart boundary. response.getOutputStream().println(); response.getOutputStream().print("--" + MULTIPART_BOUNDARY + "--"); } catch (final IOException e) { - LOG.error("multipartRangeRequest of file ({}) failed!", artifact.getFilename(), e); - throw new FileSteamingFailedException(artifact.getFilename()); + throwFileStreamingFailedException(artifact, e); } } + private static void throwFileStreamingFailedException(final Artifact artifact, final IOException e) { + LOG.error("multipartRangeRequest of file ({}) failed!", artifact.getFilename(), e); + throw new FileSteamingFailedException(artifact.getFilename()); + } + private static void handleStandardRangeRequest(final Artifact artifact, final HttpServletResponse response, final DbArtifact file, final ControllerManagement controllerManagement, final Long statusId, final List ranges) { @@ -276,9 +284,9 @@ public final class RestResourceConversionHelper { response.setHeader(CONTENT_LENGTH, String.valueOf(r.getLength())); response.setStatus(SC_PARTIAL_CONTENT); - try { - copyStreams(file.getFileInputStream(), response.getOutputStream(), controllerManagement, statusId, - r.getStart(), r.getLength()); + try (InputStream inputStream = file.getFileInputStream()) { + copyStreams(inputStream, response.getOutputStream(), controllerManagement, statusId, r.getStart(), + r.getLength()); } catch (final IOException e) { LOG.error("standardRangeRequest of file ({}) failed!", artifact.getFilename(), e); throw new FileSteamingFailedException(artifact.getFilename()); diff --git a/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/AbstractRestIntegrationTestWithMongoDB.java b/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/AbstractRestIntegrationTestWithMongoDB.java deleted file mode 100644 index 5974917ec..000000000 --- a/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/AbstractRestIntegrationTestWithMongoDB.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.eclipse.hawkbit.rest; - -import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest; -import org.eclipse.hawkbit.rest.configuration.RestConfiguration; -import org.eclipse.hawkbit.rest.util.FilterHttpResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.SpringApplicationConfiguration; -import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder; - -/** - * Abstract Test for Rest tests. - */ -@SpringApplicationConfiguration(classes = { RestConfiguration.class, - org.eclipse.hawkbit.RepositoryApplicationConfiguration.class }) -public abstract class AbstractRestIntegrationTestWithMongoDB extends AbstractIntegrationTest { - - @Autowired - private FilterHttpResponse filterHttpResponse; - - @Override - protected DefaultMockMvcBuilder createMvcWebAppContext() { - final DefaultMockMvcBuilder createMvcWebAppContext = super.createMvcWebAppContext(); - return createMvcWebAppContext.addFilter(filterHttpResponse); - } -} diff --git a/hawkbit-ui/pom.xml b/hawkbit-ui/pom.xml index c2de99b5f..d842a4814 100644 --- a/hawkbit-ui/pom.xml +++ b/hawkbit-ui/pom.xml @@ -271,11 +271,6 @@ fest-assert test - - de.flapdoodle.embed - de.flapdoodle.embed.mongo - test - ru.yandex.qatools.allure allure-junit-adaptor diff --git a/pom.xml b/pom.xml index f26a9f577..1fa28d57b 100644 --- a/pom.xml +++ b/pom.xml @@ -38,7 +38,7 @@ hawkbit-security-integration hawkbit-http-security hawkbit-ui - hawkbit-artifact-repository-mongo + hawkbit-artifact-repository-filesystem hawkbit-autoconfigure hawkbit-test-report examples @@ -670,11 +670,6 @@ json ${json.version} - - de.flapdoodle.embed - de.flapdoodle.embed.mongo - ${embedded-mongo.version} - org.easytesting fest-assert