From a034fa71e0742c3aaf057356ddefa2839a6158f2 Mon Sep 17 00:00:00 2001 From: Avgustin Marinov Date: Tue, 10 Dec 2024 13:26:42 +0200 Subject: [PATCH] Improve artifact binary cleanup - only after commit Signed-off-by: Avgustin Marinov --- .../repository/jpa/JpaRolloutExecutor.java | 17 ++---- .../RepositoryApplicationConfiguration.java | 25 ++++---- ...ansactionCommitDefaultServiceExecutor.java | 19 ++++-- .../jpa/management/JpaArtifactManagement.java | 58 ++++++++++--------- .../JpaSoftwareModuleManagement.java | 14 ++--- .../jpa/management/JpaTargetManagement.java | 24 ++++---- .../management/ArtifactManagementTest.java | 42 +++++++------- .../SoftwareModuleManagementTest.java | 49 ++++++++-------- 8 files changed, 128 insertions(+), 120 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java index a37cea47b0..47fe7ebd21 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java @@ -476,9 +476,8 @@ private void executeRunningGroups(final JpaRollout rollout, final List groupsToBeScheduled = rolloutGroupRepository.findByRolloutAndStatus(rollout, - RolloutGroupStatus.READY); - final long scheduledGroups = groupsToBeScheduled.stream() - .filter(group -> scheduleRolloutGroup(jpaRollout, group)).count(); - + final List groupsToBeScheduled = rolloutGroupRepository.findByRolloutAndStatus(rollout, RolloutGroupStatus.READY); + final long scheduledGroups = groupsToBeScheduled.stream().filter(group -> scheduleRolloutGroup(jpaRollout, group)).count(); return scheduledGroups == groupsToBeScheduled.size(); } @@ -630,8 +625,8 @@ private JpaRolloutGroup fillRolloutGroupWithTargets(final JpaRollout rollout, fi do { // Add up to TRANSACTION_TARGETS of the left targets // In case a TransactionException is thrown this loop aborts - final long assigned = assignTargetsToGroupInNewTransaction(rollout, group, groupTargetFilter, - Math.min(TRANSACTION_TARGETS, targetsLeftToAdd)); + final long assigned = assignTargetsToGroupInNewTransaction( + rollout, group, groupTargetFilter, Math.min(TRANSACTION_TARGETS, targetsLeftToAdd)); if (assigned == 0) { break; // percent > 100 or some could have disappeared } else { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java index ff0d08a1a6..98d4727ae3 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java @@ -357,8 +357,8 @@ RolloutGroupEvaluationManager evaluationManager( final List> successConditionEvaluators, final List> errorActionEvaluators, final List> successActionEvaluators) { - return new RolloutGroupEvaluationManager(errorConditionEvaluators, successConditionEvaluators, - errorActionEvaluators, successActionEvaluators); + return new RolloutGroupEvaluationManager( + errorConditionEvaluators, successConditionEvaluators, errorActionEvaluators, successActionEvaluators); } @Bean @@ -696,8 +696,8 @@ DistributionSetTagManagement distributionSetTagManagement( final DistributionSetTagRepository distributionSetTagRepository, final DistributionSetRepository distributionSetRepository, final VirtualPropertyReplacer virtualPropertyReplacer, final JpaProperties properties) { - return new JpaDistributionSetTagManagement(distributionSetTagRepository, distributionSetRepository, - virtualPropertyReplacer, properties.getDatabase()); + return new JpaDistributionSetTagManagement( + distributionSetTagRepository, distributionSetRepository, virtualPropertyReplacer, properties.getDatabase()); } /** @@ -735,8 +735,7 @@ SoftwareModuleTypeManagement softwareModuleTypeManagement( final SoftwareModuleRepository softwareModuleRepository, final JpaProperties properties) { return new JpaSoftwareModuleTypeManagement(distributionSetTypeRepository, softwareModuleTypeRepository, - virtualPropertyReplacer, softwareModuleRepository, - properties.getDatabase()); + virtualPropertyReplacer, softwareModuleRepository, properties.getDatabase()); } @Bean @@ -744,8 +743,7 @@ SoftwareModuleTypeManagement softwareModuleTypeManagement( RolloutHandler rolloutHandler(final TenantAware tenantAware, final RolloutManagement rolloutManagement, final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry, final PlatformTransactionManager txManager, final ContextAware contextAware) { - return new JpaRolloutHandler(tenantAware, rolloutManagement, rolloutExecutor, lockRegistry, txManager, - contextAware); + return new JpaRolloutHandler(tenantAware, rolloutManagement, rolloutExecutor, lockRegistry, txManager, contextAware); } @Bean @@ -777,8 +775,7 @@ RolloutManagement rolloutManagement(final TargetManagement targetManagement, final ContextAware contextAware) { return new JpaRolloutManagement(targetManagement, distributionSetManagement, eventPublisherHolder, virtualPropertyReplacer, properties.getDatabase(), rolloutApprovalStrategy, - tenantConfigurationManagement, systemSecurityContext, - contextAware); + tenantConfigurationManagement, systemSecurityContext, contextAware); } /** @@ -860,10 +857,12 @@ ControllerManagement controllerManagement(final ScheduledExecutorService executo @Bean @ConditionalOnMissingBean ArtifactManagement artifactManagement( - final EntityManager entityManager, final LocalArtifactRepository localArtifactRepository, - final SoftwareModuleRepository softwareModuleRepository, final Optional artifactRepository, + final EntityManager entityManager, final PlatformTransactionManager txManager, + final LocalArtifactRepository localArtifactRepository, final SoftwareModuleRepository softwareModuleRepository, + final Optional artifactRepository, final QuotaManagement quotaManagement, final TenantAware tenantAware) { - return new JpaArtifactManagement(entityManager, localArtifactRepository, softwareModuleRepository, artifactRepository.orElse(null), + return new JpaArtifactManagement( + entityManager, txManager, localArtifactRepository, softwareModuleRepository, artifactRepository.orElse(null), quotaManagement, tenantAware); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/executor/AfterTransactionCommitDefaultServiceExecutor.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/executor/AfterTransactionCommitDefaultServiceExecutor.java index e53200ab6c..986ea1e65c 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/executor/AfterTransactionCommitDefaultServiceExecutor.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/executor/AfterTransactionCommitDefaultServiceExecutor.java @@ -13,15 +13,15 @@ import java.util.List; import lombok.extern.slf4j.Slf4j; -import org.springframework.transaction.support.TransactionSynchronizationAdapter; +import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; /** - * A Service which calls register runnable. This runnables will executed after a - * successful spring transaction commit.The class is thread safe. + * A Service which calls register runnable. This runnables will be executed after a successful spring transaction commit. + * The class is thread safe. */ @Slf4j -public class AfterTransactionCommitDefaultServiceExecutor extends TransactionSynchronizationAdapter implements AfterTransactionCommitExecutor { +public class AfterTransactionCommitDefaultServiceExecutor implements TransactionSynchronization, AfterTransactionCommitExecutor { private static final ThreadLocal> THREAD_LOCAL_RUNNABLES = new ThreadLocal<>(); @@ -30,6 +30,14 @@ public class AfterTransactionCommitDefaultServiceExecutor extends TransactionSyn @SuppressWarnings({ "squid:S1217" }) public void afterCommit() { final List afterCommitRunnables = THREAD_LOCAL_RUNNABLES.get(); + if (afterCommitRunnables == null) { + log.trace("Transaction successfully committed, runnables is null"); + return; + } + + // removes the runnables that will process, so they would be able to start new transactions and + // inserting new after commit hooks + THREAD_LOCAL_RUNNABLES.remove(); log.debug("Transaction successfully committed, executing {} runnables", afterCommitRunnables.size()); for (final Runnable afterCommitRunnable : afterCommitRunnables) { log.debug("Executing runnable {}", afterCommitRunnable); @@ -44,8 +52,7 @@ public void afterCommit() { @Override @SuppressWarnings({ "squid:S1217" }) public void afterCompletion(final int status) { - final String transactionStatus = status == STATUS_COMMITTED ? "COMMITTED" : "ROLLEDBACK"; - log.debug("Transaction completed after commit with status {}", transactionStatus); + log.debug("Transaction completed after commit with status {}", status == STATUS_COMMITTED ? "COMMITTED" : "ROLLEDBACK"); THREAD_LOCAL_RUNNABLES.remove(); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaArtifactManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaArtifactManagement.java index ed83c8a369..9645a85d26 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaArtifactManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaArtifactManagement.java @@ -36,26 +36,31 @@ import org.eclipse.hawkbit.repository.exception.InvalidSHA1HashException; import org.eclipse.hawkbit.repository.exception.InvalidSHA256HashException; import org.eclipse.hawkbit.repository.jpa.EncryptionAwareDbArtifact; +import org.eclipse.hawkbit.repository.jpa.Jpa; import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper; import org.eclipse.hawkbit.repository.jpa.acm.AccessController; import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; +import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder; import org.eclipse.hawkbit.repository.jpa.repository.LocalArtifactRepository; import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository; import org.eclipse.hawkbit.repository.jpa.specifications.ArtifactSpecifications; +import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper; import org.eclipse.hawkbit.repository.jpa.utils.FileSizeAndStorageQuotaCheckingInputStream; import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper; import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.ArtifactUpload; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.tenancy.TenantAware; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.ConcurrencyFailureException; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.retry.annotation.Backoff; import org.springframework.retry.annotation.Retryable; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; @@ -70,6 +75,7 @@ public class JpaArtifactManagement implements ArtifactManagement { private final EntityManager entityManager; + private final PlatformTransactionManager txManager; private final LocalArtifactRepository localArtifactRepository; private final SoftwareModuleRepository softwareModuleRepository; @Nullable @@ -79,10 +85,13 @@ public class JpaArtifactManagement implements ArtifactManagement { public JpaArtifactManagement( final EntityManager entityManager, + final PlatformTransactionManager txManager, final LocalArtifactRepository localArtifactRepository, final SoftwareModuleRepository softwareModuleRepository, @Nullable final ArtifactRepository artifactRepository, - final QuotaManagement quotaManagement, final TenantAware tenantAware) { + final QuotaManagement quotaManagement, + final TenantAware tenantAware) { this.entityManager = entityManager; + this.txManager = txManager; this.localArtifactRepository = localArtifactRepository; this.softwareModuleRepository = softwareModuleRepository; this.artifactRepository = artifactRepository; @@ -137,18 +146,19 @@ public Artifact create(final ArtifactUpload artifactUpload) { @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) public void delete(final long id) { - final JpaArtifact toDelete = (JpaArtifact) get(id) - .orElseThrow(() -> new EntityNotFoundException(Artifact.class, id)); + final JpaArtifact toDelete = (JpaArtifact) get(id).orElseThrow(() -> new EntityNotFoundException(Artifact.class, id)); + final JpaSoftwareModule softwareModule = toDelete.getSoftwareModule(); // clearArtifactBinary checks (unconditionally) software module UPDATE access softwareModuleRepository.getAccessController().ifPresent(accessController -> - accessController.assertOperationAllowed(AccessController.Operation.UPDATE, - (JpaSoftwareModule) toDelete.getSoftwareModule())); - ((JpaSoftwareModule) toDelete.getSoftwareModule()).removeArtifact(toDelete); - softwareModuleRepository.save((JpaSoftwareModule) toDelete.getSoftwareModule()); + accessController.assertOperationAllowed(AccessController.Operation.UPDATE, softwareModule)); + softwareModule.removeArtifact(toDelete); + softwareModuleRepository.save(softwareModule); localArtifactRepository.deleteById(id); - clearArtifactBinary(toDelete.getSha1Hash()); + + final String sha1Hash = toDelete.getSha1Hash(); + AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(() -> clearArtifactBinary(sha1Hash)); } @Override @@ -211,39 +221,35 @@ public Optional loadArtifactBinary(final String sha1Hash, final long } /** - * Garbage collects artifact binaries if only referenced by given - * {@link SoftwareModule#getId()} or {@link SoftwareModule}'s that are + * Garbage collects artifact binaries if only referenced by given {@link SoftwareModule#getId()} or {@link SoftwareModule}'s that are * marked as deleted. *

* Software module related UPDATE permission shall be checked by the callers! + *

+ * Note: Internal method. Shall be called ONLY if @PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) + * has already been checked * * @param sha1Hash no longer needed */ - @PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) void clearArtifactBinary(final String sha1Hash) { assertArtifactRepositoryAvailable(); - // countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse will skip ACM checks and - // will return total count as it should be - final long count = localArtifactRepository.countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse( - sha1Hash, - tenantAware.getCurrentTenant()); - if (count <= 1) { // 1 artifact is the one being deleted! - // removes the real artifact ONLY AFTER the delete of artifact or software module - // in local history has passed successfully (caller has permission and no errors) - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - - @Override - public void afterCommit() { + DeploymentHelper.runInNewTransaction(txManager, "clearArtifactBinary", status -> { + // countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse will skip ACM checks and will return total count as it should be + if (localArtifactRepository.countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse(sha1Hash, tenantAware.getCurrentTenant()) <= 0) { // 1 artifact is the one being deleted! + // removes the real artifact ONLY AFTER the delete of artifact or software module + // in local history has passed successfully (caller has permission and no errors) + AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(() -> { try { log.debug("deleting artifact from repository {}", sha1Hash); artifactRepository.deleteBySha1(tenantAware.getCurrentTenant(), sha1Hash); } catch (final ArtifactStoreException e) { throw new ArtifactDeleteFailedException(e); } - } - }); - } // else there are still other artifacts that need the binary + }); + } // else there are still other artifacts that need the binary + return null; + }); } private AbstractDbArtifact storeArtifact(final ArtifactUpload artifactUpload, final boolean isSmEncrypted) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSoftwareModuleManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSoftwareModuleManagement.java index 359dcde581..913d3c8883 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSoftwareModuleManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSoftwareModuleManagement.java @@ -48,6 +48,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata_; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule_; import org.eclipse.hawkbit.repository.jpa.model.SwMetadataCompositeKey; +import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder; import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository; import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleMetadataRepository; import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository; @@ -199,10 +200,6 @@ public void delete(final Collection ids) { final Set assignedModuleIds = new HashSet<>(); swModulesToDelete.forEach(swModule -> { - - // delete binary data of artifacts - deleteGridFsArtifacts(swModule); - // execute this count operation without access limitations since we have to // ensure it's not assigned when deleting it. if (distributionSetRepository.countByModulesId(swModule.getId()) <= 0) { @@ -210,6 +207,8 @@ public void delete(final Collection ids) { } else { assignedModuleIds.add(swModule.getId()); } + // schedule delete binary data of artifacts + deleteGridFsArtifacts(swModule); }); if (!assignedModuleIds.isEmpty()) { @@ -507,10 +506,9 @@ private static Specification metadataBySoftwareModule private void deleteGridFsArtifacts(final JpaSoftwareModule swModule) { softwareModuleRepository.getAccessController().ifPresent(accessController -> accessController.assertOperationAllowed(AccessController.Operation.DELETE, swModule)); - for (final Artifact localArtifact : swModule.getArtifacts()) { - ((JpaArtifactManagement) artifactManagement) - .clearArtifactBinary(localArtifact.getSha1Hash()); - } + final Set sha1Hashes = swModule.getArtifacts().stream().map(Artifact::getSha1Hash).collect(Collectors.toSet()); + AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(() -> + sha1Hashes.forEach(sha1Hash -> ((JpaArtifactManagement) artifactManagement).clearArtifactBinary(sha1Hash))); } private Specification buildSmSearchQuerySpec(final String searchText) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetManagement.java index dc93617317..a49e6b6c11 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetManagement.java @@ -60,6 +60,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_; import org.eclipse.hawkbit.repository.jpa.model.TargetMetadataCompositeKey; +import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder; import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository; import org.eclipse.hawkbit.repository.jpa.repository.TargetFilterQueryRepository; import org.eclipse.hawkbit.repository.jpa.repository.TargetMetadataRepository; @@ -648,15 +649,20 @@ public Map getControllerAttributes(final String controllerId) { } @Override + @Transactional + @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, + backoff = @Backoff(delay = Constants.TX_RT_DELAY)) public void requestControllerAttributes(final String controllerId) { final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId); targetRepository.getAccessController() .ifPresent(acm -> acm.assertOperationAllowed(AccessController.Operation.UPDATE, target)); target.setRequestControllerAttributes(true); - eventPublisherHolder.getEventPublisher() - .publishEvent(new TargetAttributesRequestedEvent(tenantAware.getCurrentTenant(), target.getId(), - target.getControllerId(), target.getAddress() != null ? target.getAddress().toString() : null, - JpaTarget.class, eventPublisherHolder.getApplicationId())); + AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(() -> + eventPublisherHolder.getEventPublisher() + .publishEvent(new TargetAttributesRequestedEvent( + tenantAware.getCurrentTenant(), target.getId(), target.getControllerId(), + target.getAddress() != null ? target.getAddress().toString() : null, + JpaTarget.class, eventPublisherHolder.getApplicationId()))); } @Override @@ -718,14 +724,12 @@ public List createMetaData(final String controllerId, final Coll final JpaTarget updatedTarget = JpaManagementHelper.touch(entityManager, targetRepository, target); final List createdMetadata = md.stream() - .map(meta -> targetMetadataRepository - .save(new JpaTargetMetadata(meta.getKey(), meta.getValue(), updatedTarget))) + .map(meta -> targetMetadataRepository.save(new JpaTargetMetadata(meta.getKey(), meta.getValue(), updatedTarget))) .collect(Collectors.toUnmodifiableList()); // TargetUpdatedEvent is not sent within the touch() method due to the // "lastModifiedAt" field being ignored in JpaTarget - eventPublisherHolder.getEventPublisher() - .publishEvent(new TargetUpdatedEvent(updatedTarget, eventPublisherHolder.getApplicationId())); + eventPublisherHolder.getEventPublisher().publishEvent(new TargetUpdatedEvent(updatedTarget, eventPublisherHolder.getApplicationId())); return createdMetadata; } @@ -738,8 +742,8 @@ public void deleteMetaData(final String controllerId, final String key) { final JpaTargetMetadata metadata = (JpaTargetMetadata) getMetaDataByControllerId(controllerId, key) .orElseThrow(() -> new EntityNotFoundException(TargetMetadata.class, controllerId, key)); - final JpaTarget target = JpaManagementHelper.touch(entityManager, targetRepository, - getByControllerIdAndThrowIfNotFound(controllerId)); + final JpaTarget target = JpaManagementHelper.touch( + entityManager, targetRepository, getByControllerIdAndThrowIfNotFound(controllerId)); targetRepository.getAccessController() .ifPresent(acm -> acm.assertOperationAllowed(AccessController.Operation.UPDATE, target)); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/ArtifactManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/ArtifactManagementTest.java index caba65f219..77f70c47aa 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/ArtifactManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/ArtifactManagementTest.java @@ -64,12 +64,12 @@ */ @Feature("Component Tests - Repository") @Story("Artifact Management") -public class ArtifactManagementTest extends AbstractJpaIntegrationTest { +class ArtifactManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verifies that management get access react as specfied on calls for non existing entities by means of Optional not present.") @ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 1) }) - public void nonExistingEntityAccessReturnsNotPresent() { + void nonExistingEntityAccessReturnsNotPresent() { final SoftwareModule module = testdataFactory.createSoftwareModuleOs(); assertThat(artifactManagement.get(NOT_EXIST_IDL)).isNotPresent(); @@ -85,7 +85,7 @@ public void nonExistingEntityAccessReturnsNotPresent() { @Description("Verifies that management queries react as specfied on calls for non existing entities " + " by means of throwing EntityNotFoundException.") @ExpectEvents({ @Expect(type = SoftwareModuleDeletedEvent.class, count = 0) }) - public void entityQueriesReferringToNotExistingEntitiesThrowsException() throws URISyntaxException { + void entityQueriesReferringToNotExistingEntitiesThrowsException() throws URISyntaxException { final String artifactData = "test"; final int artifactSize = artifactData.length(); @@ -110,7 +110,7 @@ public void entityQueriesReferringToNotExistingEntitiesThrowsException() throws @Test @Description("Test if a local artifact can be created by API including metadata.") - public void createArtifact() throws IOException { + void createArtifact() throws IOException { // check baseline assertThat(softwareModuleRepository.findAll()).hasSize(0); @@ -161,7 +161,7 @@ public void createArtifact() throws IOException { @Test @Description("Verifies that artifact management does not create artifacts with illegal filename.") - public void entityQueryWithIllegalFilenameThrowsException() throws URISyntaxException { + void entityQueryWithIllegalFilenameThrowsException() throws URISyntaxException { final String illegalFilename = ".xml"; final String artifactData = "test"; final int artifactSize = artifactData.length(); @@ -176,7 +176,7 @@ public void entityQueryWithIllegalFilenameThrowsException() throws URISyntaxExce @Test @Description("Verifies that the quota specifying the maximum number of artifacts per software module is enforced.") - public void createArtifactsUntilQuotaIsExceeded() throws IOException { + void createArtifactsUntilQuotaIsExceeded() throws IOException { // create a software module final long smId = softwareModuleRepository.save(new JpaSoftwareModule(osType, "sm1", "1.0")).getId(); @@ -205,7 +205,7 @@ public void createArtifactsUntilQuotaIsExceeded() throws IOException { @Test @Description("Verifies that the quota specifying the maximum artifact storage is enforced (across software modules).") - public void createArtifactsUntilStorageQuotaIsExceeded() throws IOException { + void createArtifactsUntilStorageQuotaIsExceeded() throws IOException { // create as many small artifacts as possible w/o violating the storage // quota @@ -235,7 +235,7 @@ public void createArtifactsUntilStorageQuotaIsExceeded() throws IOException { @Test @Description("Verifies that you cannot create artifacts which exceed the configured maximum size.") - public void createArtifactFailsIfTooLarge() { + void createArtifactFailsIfTooLarge() { // create a software module final JpaSoftwareModule sm1 = softwareModuleRepository.save(new JpaSoftwareModule(osType, "sm1", "1.0")); @@ -248,7 +248,7 @@ public void createArtifactFailsIfTooLarge() { @Test @Description("Tests hard delete directly on repository.") - public void hardDeleteSoftwareModule() throws IOException { + void hardDeleteSoftwareModule() throws IOException { final JpaSoftwareModule sm = softwareModuleRepository .save(new JpaSoftwareModule(osType, "name 1", "version 1")); @@ -268,7 +268,7 @@ public void hardDeleteSoftwareModule() throws IOException { */ @Test @Description("Tests the deletion of a local artifact including metadata.") - public void deleteArtifact() throws IOException { + void deleteArtifact() throws IOException { final JpaSoftwareModule sm = softwareModuleRepository .save(new JpaSoftwareModule(osType, "name 1", "version 1")); final JpaSoftwareModule sm2 = softwareModuleRepository @@ -320,7 +320,7 @@ public void deleteArtifact() throws IOException { @Test @Description("Test the deletion of an artifact metadata where the binary is still linked to another metadata element. " + "The expected result is that the metadata is deleted but the binary kept.") - public void deleteDuplicateArtifacts() throws IOException { + void deleteDuplicateArtifacts() throws IOException { final JpaSoftwareModule sm = softwareModuleRepository .save(new JpaSoftwareModule(osType, "name 1", "version 1")); final JpaSoftwareModule sm2 = softwareModuleRepository @@ -353,7 +353,7 @@ public void deleteDuplicateArtifacts() throws IOException { @Test @Description("Verifies that you cannot delete an artifact which exists with the same hash, in the same tenant and the SoftwareModule is not deleted .") - public void deleteArtifactWithSameHashAndSoftwareModuleIsNotDeletedInSameTenants() throws IOException { + void deleteArtifactWithSameHashAndSoftwareModuleIsNotDeletedInSameTenants() throws IOException { final JpaSoftwareModule sm = softwareModuleRepository .save(new JpaSoftwareModule(osType, "name 1", "version 1")); @@ -395,7 +395,7 @@ public void deleteArtifactWithSameHashAndSoftwareModuleIsNotDeletedInSameTenants @Test @Description("Verifies that you can not delete artifacts from another tenant which exists in another tenant with the same hash and the SoftwareModule is not deleted") - public void deleteArtifactWithSameHashAndSoftwareModuleIsNotDeletedInDifferentTenants() throws Exception { + void deleteArtifactWithSameHashAndSoftwareModuleIsNotDeletedInDifferentTenants() throws Exception { final String tenant1 = "mytenant"; final String tenant2 = "tenant2"; @@ -422,7 +422,7 @@ public void deleteArtifactWithSameHashAndSoftwareModuleIsNotDeletedInDifferentTe @Test @Description("Loads an local artifact based on given ID.") - public void findArtifact() throws IOException { + void findArtifact() throws IOException { final int artifactSize = 5 * 1024; try (final InputStream inputStream = new RandomGeneratedInputStream(artifactSize)) { final Artifact artifact = createArtifactForSoftwareModule( @@ -433,7 +433,7 @@ public void findArtifact() throws IOException { @Test @Description("Loads an artifact binary based on given ID.") - public void loadStreamOfArtifact() throws IOException { + void loadStreamOfArtifact() throws IOException { final int artifactSize = 5 * 1024; final byte[] randomBytes = randomBytes(artifactSize); try (final InputStream input = new ByteArrayInputStream(randomBytes)) { @@ -448,7 +448,7 @@ public void loadStreamOfArtifact() throws IOException { @Test @WithUser(allSpPermissions = true, removeFromAllPermission = { SpPermission.DOWNLOAD_REPOSITORY_ARTIFACT }) @Description("Trys and fails to load an artifact without required permission. Checks if expected InsufficientPermissionException is thrown.") - public void loadArtifactBinaryWithoutDownloadArtifactThrowsPermissionDenied() { + void loadArtifactBinaryWithoutDownloadArtifactThrowsPermissionDenied() { assertThatExceptionOfType(InsufficientPermissionException.class) .as("Should not have worked with missing permission.") .isThrownBy(() -> artifactManagement.loadArtifactBinary("123", 1, false)); @@ -456,7 +456,7 @@ public void loadArtifactBinaryWithoutDownloadArtifactThrowsPermissionDenied() { @Test @Description("Searches an artifact through the relations of a software module.") - public void findArtifactBySoftwareModule() throws IOException { + void findArtifactBySoftwareModule() throws IOException { final SoftwareModule sm = testdataFactory.createSoftwareModuleOs(); assertThat(artifactManagement.findBySoftwareModule(PAGE, sm.getId())).isEmpty(); @@ -469,7 +469,7 @@ public void findArtifactBySoftwareModule() throws IOException { @Test @Description("Searches an artifact through the relations of a software module and the filename.") - public void findByFilenameAndSoftwareModule() throws IOException { + void findByFilenameAndSoftwareModule() throws IOException { final SoftwareModule sm = testdataFactory.createSoftwareModuleOs(); assertThat(artifactManagement.getByFilenameAndSoftwareModule("file1", sm.getId())).isNotPresent(); @@ -485,7 +485,7 @@ public void findByFilenameAndSoftwareModule() throws IOException { @Test @Description("Verifies that creation of an artifact with none matching hashes fails.") - public void createArtifactWithNoneMatchingHashes() throws IOException, NoSuchAlgorithmException { + void createArtifactWithNoneMatchingHashes() throws IOException, NoSuchAlgorithmException { final SoftwareModule sm = testdataFactory.createSoftwareModuleOs(); final byte[] testData = RandomStringUtils.randomAlphanumeric(100).getBytes(); @@ -517,7 +517,7 @@ public void createArtifactWithNoneMatchingHashes() throws IOException, NoSuchAlg @Test @Description("Verifies that creation of an artifact with matching hashes works.") - public void createArtifactWithMatchingHashes() throws IOException, NoSuchAlgorithmException { + void createArtifactWithMatchingHashes() throws IOException, NoSuchAlgorithmException { final SoftwareModule sm = testdataFactory.createSoftwareModuleOs(); final byte[] testData = RandomStringUtils.randomAlphanumeric(100).getBytes(); @@ -540,7 +540,7 @@ public void createArtifactWithMatchingHashes() throws IOException, NoSuchAlgorit @Test @Description("Verifies that creation of an existing artifact returns a full hash list.") - public void createExistingArtifactReturnsFullHashList() throws IOException, NoSuchAlgorithmException { + void createExistingArtifactReturnsFullHashList() throws IOException, NoSuchAlgorithmException { final SoftwareModule smOs = testdataFactory.createSoftwareModuleOs(); final SoftwareModule smApp = testdataFactory.createSoftwareModuleApp(); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/SoftwareModuleManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/SoftwareModuleManagementTest.java index c9827204a5..20e7a367ab 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/SoftwareModuleManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/SoftwareModuleManagementTest.java @@ -57,13 +57,13 @@ @Feature("Component Tests - Repository") @Story("Software Module Management") -public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest { +class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verifies that management get access reacts as specified on calls for non existing entities by means " + "of Optional not present.") @ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 1) }) - public void nonExistingEntityAccessReturnsNotPresent() { + void nonExistingEntityAccessReturnsNotPresent() { final SoftwareModule module = testdataFactory.createSoftwareModuleApp(); assertThat(softwareModuleManagement.get(1234L)).isNotPresent(); @@ -78,7 +78,7 @@ public void nonExistingEntityAccessReturnsNotPresent() { @Description("Verifies that management queries react as specfied on calls for non existing entities " + " by means of throwing EntityNotFoundException.") @ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 1) }) - public void entityQueriesReferringToNotExistingEntitiesThrowsException() { + void entityQueriesReferringToNotExistingEntitiesThrowsException() { final SoftwareModule module = testdataFactory.createSoftwareModuleApp(); verifyThrownExceptionBy( @@ -138,7 +138,7 @@ public void entityQueriesReferringToNotExistingEntitiesThrowsException() { @Test @Description("Calling update without changing fields results in no recorded change in the repository including unchanged audit fields.") - public void updateNothingResultsInUnchangedRepository() { + void updateNothingResultsInUnchangedRepository() { final SoftwareModule ah = testdataFactory.createSoftwareModuleOs(); final SoftwareModule updated = softwareModuleManagement @@ -151,7 +151,7 @@ public void updateNothingResultsInUnchangedRepository() { @Test @Description("Calling update for changed fields results in change in the repository.") - public void updateSoftwareModuleFieldsToNewValue() { + void updateSoftwareModuleFieldsToNewValue() { final SoftwareModule ah = testdataFactory.createSoftwareModuleOs(); final SoftwareModule updated = softwareModuleManagement @@ -166,7 +166,7 @@ public void updateSoftwareModuleFieldsToNewValue() { @Test @Description("Create Software Module call fails when called for existing entity.") - public void createModuleCallFailsForExistingModule() { + void createModuleCallFailsForExistingModule() { testdataFactory.createSoftwareModuleOs(); assertThatExceptionOfType(EntityAlreadyExistsException.class) .as("Should not have worked as module already exists.") @@ -175,7 +175,7 @@ public void createModuleCallFailsForExistingModule() { @Test @Description("searched for software modules based on the various filter options, e.g. name,desc,type, version.") - public void findSoftwareModuleByFilters() { + void findSoftwareModuleByFilters() { final SoftwareModule ah = softwareModuleManagement .create(entityFactory.softwareModule().create().type(appType).name("agent-hub").version("1.0.1")); final SoftwareModule jvm = softwareModuleManagement @@ -217,7 +217,7 @@ public void findSoftwareModuleByFilters() { @Test @Description("Searches for software modules based on a list of IDs.") - public void findSoftwareModulesById() { + void findSoftwareModulesById() { final List modules = Arrays.asList(testdataFactory.createSoftwareModuleOs().getId(), testdataFactory.createSoftwareModuleApp().getId(), 624355263L); @@ -227,7 +227,7 @@ public void findSoftwareModulesById() { @Test @Description("Searches for software modules by type.") - public void findSoftwareModulesByType() { + void findSoftwareModulesByType() { // found in test final SoftwareModule one = testdataFactory.createSoftwareModuleOs("one"); final SoftwareModule two = testdataFactory.createSoftwareModuleOs("two"); @@ -242,7 +242,7 @@ public void findSoftwareModulesByType() { @Test @Description("Counts all software modules in the repsitory that are not marked as deleted.") - public void countSoftwareModulesAll() { + void countSoftwareModulesAll() { // found in test testdataFactory.createSoftwareModuleOs("one"); testdataFactory.createSoftwareModuleOs("two"); @@ -256,7 +256,7 @@ public void countSoftwareModulesAll() { @Test @Description("Deletes an artifact, which is not assigned to a Distribution Set") - public void hardDeleteOfNotAssignedArtifact() { + void hardDeleteOfNotAssignedArtifact() { // [STEP1]: Create SoftwareModuleX with Artifacts final SoftwareModule unassignedModule = createSoftwareModuleWithArtifacts(osType, "moduleX", "3.0.2", 2); @@ -282,8 +282,7 @@ public void hardDeleteOfNotAssignedArtifact() { @Test @Description("Deletes an artifact, which is assigned to a DistributionSet") - public void softDeleteOfAssignedArtifact() { - + void softDeleteOfAssignedArtifact() { // [STEP1]: Create SoftwareModuleX with ArtifactX SoftwareModule assignedModule = createSoftwareModuleWithArtifacts(osType, "moduleX", "3.0.2", 2); @@ -315,7 +314,7 @@ public void softDeleteOfAssignedArtifact() { @Test @Description("Delete an artifact, which has been assigned to a rolled out DistributionSet in the past") - public void softDeleteOfHistoricalAssignedArtifact() { + void softDeleteOfHistoricalAssignedArtifact() { // Init target final Target target = testdataFactory.createTarget(); @@ -355,7 +354,7 @@ public void softDeleteOfHistoricalAssignedArtifact() { @Test @Description("Delete an software module with an artifact, which is also used by another software module.") - public void deleteSoftwareModulesWithSharedArtifact() { + void deleteSoftwareModulesWithSharedArtifact() { // Init artifact binary data, target and DistributionSets final int artifactSize = 1024; @@ -401,7 +400,7 @@ public void deleteSoftwareModulesWithSharedArtifact() { @Test @Description("Delete two assigned softwaremodules which share an artifact.") - public void deleteMultipleSoftwareModulesWhichShareAnArtifact() { + void deleteMultipleSoftwareModulesWhichShareAnArtifact() { // Init artifact binary data, target and DistributionSets final int artifactSize = 1024; final byte[] source = RandomUtils.nextBytes(artifactSize); @@ -460,7 +459,7 @@ public void deleteMultipleSoftwareModulesWhichShareAnArtifact() { @Test @Description("Verifies that all undeleted software modules are found in the repository.") - public void countSoftwareModuleTypesAll() { + void countSoftwareModuleTypesAll() { testdataFactory.createSoftwareModuleOs(); // one soft deleted @@ -474,7 +473,7 @@ public void countSoftwareModuleTypesAll() { @Test @Description("Verifies that software modules are returned that are assigned to given DS.") - public void findSoftwareModuleByAssignedTo() { + void findSoftwareModuleByAssignedTo() { // test modules final SoftwareModule one = testdataFactory.createSoftwareModuleOs(); testdataFactory.createSoftwareModuleOs("notassigned"); @@ -491,7 +490,7 @@ public void findSoftwareModuleByAssignedTo() { @Test @Description("Checks that metadata for a software module can be created.") - public void createSoftwareModuleMetadata() { + void createSoftwareModuleMetadata() { final String knownKey1 = "myKnownKey1"; final String knownValue1 = "myKnownValue1"; @@ -523,7 +522,7 @@ public void createSoftwareModuleMetadata() { @Test @Description("Verifies the enforcement of the metadata quota per software module.") - public void createSoftwareModuleMetadataUntilQuotaIsExceeded() { + void createSoftwareModuleMetadataUntilQuotaIsExceeded() { // add meta data one by one final SoftwareModule module = testdataFactory.createSoftwareModuleApp("m1"); @@ -569,7 +568,7 @@ public void createSoftwareModuleMetadataUntilQuotaIsExceeded() { @Test @Description("Checks that metadata for a software module cannot be created for an existing key.") - public void createSoftwareModuleMetadataFailsIfKeyExists() { + void createSoftwareModuleMetadataFailsIfKeyExists() { final String knownKey1 = "myKnownKey1"; final String knownValue1 = "myKnownValue1"; @@ -597,7 +596,7 @@ public void createSoftwareModuleMetadataFailsIfKeyExists() { @Test @WithUser(allSpPermissions = true) @Description("Checks that metadata for a software module can be updated.") - public void updateSoftwareModuleMetadata() { + void updateSoftwareModuleMetadata() { final String knownKey = "myKnownKey"; final String knownValue = "myKnownValue"; final String knownUpdateValue = "myNewUpdatedValue"; @@ -637,7 +636,7 @@ public void updateSoftwareModuleMetadata() { @Test @Description("Verifies that existing metadata can be deleted.") - public void deleteSoftwareModuleMetadata() { + void deleteSoftwareModuleMetadata() { final String knownKey1 = "myKnownKey1"; final String knownValue1 = "myKnownValue1"; @@ -660,7 +659,7 @@ public void deleteSoftwareModuleMetadata() { @Test @Description("Verifies that non existing metadata find results in exception.") - public void findSoftwareModuleMetadataFailsIfEntryDoesNotExist() { + void findSoftwareModuleMetadataFailsIfEntryDoesNotExist() { final String knownKey1 = "myKnownKey1"; final String knownValue1 = "myKnownValue1"; @@ -674,7 +673,7 @@ public void findSoftwareModuleMetadataFailsIfEntryDoesNotExist() { @Test @Description("Queries and loads the metadata related to a given software module.") - public void findAllSoftwareModuleMetadataBySwId() { + void findAllSoftwareModuleMetadataBySwId() { final SoftwareModule sw1 = testdataFactory.createSoftwareModuleApp(); final int metadataCountSw1 = 8;