From 6654d71969d8f64c9f6e4b79f2ad53ed6c9409dc Mon Sep 17 00:00:00 2001 From: Kai Zimmermann Date: Fri, 13 May 2016 08:44:34 +0200 Subject: [PATCH] Started with #100 by means of splitting the management services into API and Jpa implementation. Signed-off-by: Kai Zimmermann --- .../RepositoryApplicationConfiguration.java | 2 +- .../eventbus/EntityChangeEventListener.java | 12 +- .../repository/ArtifactManagement.java | 563 +++----- .../repository/ControllerManagement.java | 640 ++------- .../repository/DeploymentManagement.java | 1132 ++++----------- .../DistributionSetAssignmentResult.java | 1 + .../repository/DistributionSetManagement.java | 1217 +++++------------ .../hawkbit/repository/ReportManagement.java | 416 +----- .../repository/RolloutGroupManagement.java | 155 +-- .../hawkbit/repository/RolloutManagement.java | 602 +------- .../hawkbit/repository/RolloutScheduler.java | 1 + .../TenantConfigurationManagement.java | 161 +-- .../{ => jpa}/ActionRepository.java | 2 +- .../{ => jpa}/ActionStatusRepository.java | 2 +- .../{ => jpa}/BaseEntityRepository.java | 2 +- .../DistributionSetMetadataRepository.java | 2 +- .../{ => jpa}/DistributionSetRepository.java | 4 +- .../DistributionSetTagRepository.java | 2 +- .../DistributionSetTypeRepository.java | 2 +- .../EclipseLinkTargetInfoRepository.java | 2 +- .../ExternalArtifactProviderRepository.java | 2 +- .../{ => jpa}/ExternalArtifactRepository.java | 2 +- .../repository/jpa/JpaArtifactManagement.java | 270 ++++ .../jpa/JpaControllerManagement.java | 417 ++++++ .../jpa/JpaDeploymentManagement.java | 652 +++++++++ .../jpa/JpaDistributionSetManagement.java | 663 +++++++++ .../repository/jpa/JpaReportManagement.java | 433 ++++++ .../jpa/JpaRolloutGroupManagement.java | 178 +++ .../repository/jpa/JpaRolloutManagement.java | 653 +++++++++ .../jpa/JpaTenantConfigurationManagement.java | 170 +++ .../{ => jpa}/LocalArtifactRepository.java | 2 +- .../{ => jpa}/NoCountPagingRepository.java | 2 +- .../{ => jpa}/OffsetBasedPageRequest.java | 2 +- .../{ => jpa}/RolloutGroupRepository.java | 2 +- .../{ => jpa}/RolloutRepository.java | 2 +- .../RolloutTargetGroupRepository.java | 2 +- .../{ => jpa}/SoftwareManagement.java | 5 +- .../SoftwareModuleMetadataRepository.java | 2 +- .../{ => jpa}/SoftwareModuleRepository.java | 2 +- .../SoftwareModuleTypeRepository.java | 2 +- .../{ => jpa}/SystemManagement.java | 2 +- .../repository/{ => jpa}/TagManagement.java | 2 +- .../TargetFilterQueryManagement.java | 2 +- .../TargetFilterQueryRepository.java | 2 +- .../{ => jpa}/TargetInfoRepository.java | 2 +- .../{ => jpa}/TargetManagement.java | 3 +- .../{ => jpa}/TargetRepository.java | 2 +- .../{ => jpa}/TargetTagRepository.java | 2 +- .../{ => jpa}/TargetWithActionType.java | 2 +- .../TenantConfigurationRepository.java | 2 +- .../{ => jpa}/TenantKeyGenerator.java | 2 +- .../{ => jpa}/TenantMetaDataRepository.java | 2 +- .../{ => jpa}/TenantStatsManagement.java | 8 +- .../model/helper/SystemManagementHolder.java | 2 +- .../condition/PauseRolloutGroupAction.java | 2 +- ...artNextGroupRolloutGroupSuccessAction.java | 2 +- .../ThresholdRolloutGroupErrorCondition.java | 2 +- ...ThresholdRolloutGroupSuccessCondition.java | 2 +- .../hawkbit/AbstractIntegrationTest.java | 42 +- .../org/eclipse/hawkbit/TestDataUtil.java | 4 +- .../repository/ControllerManagementTest.java | 10 +- .../repository/DeploymentManagementTest.java | 23 +- .../DistributionSetManagementTest.java | 2 +- .../repository/ReportManagementTest.java | 2 +- .../repository/RolloutManagementTest.java | 22 +- .../hawkbit/repository/TagManagementTest.java | 1 + .../TargetFilterQueryManagenmentTest.java | 1 + .../TargetManagementSearchTest.java | 10 +- .../repository/TargetManagementTest.java | 2 +- .../rsql/RSQLRolloutGroupFields.java | 2 +- .../utils/RepositoryDataGenerator.java | 24 +- .../controller/ArtifactStoreController.java | 2 +- .../hawkbit/controller/RootController.java | 11 +- .../rest/resource/DistributionSetMapper.java | 2 +- .../resource/DistributionSetResource.java | 8 +- .../resource/DistributionSetTagResource.java | 2 +- .../resource/DistributionSetTypeMapper.java | 2 +- .../resource/DistributionSetTypeResource.java | 4 +- .../resource/DownloadArtifactResource.java | 2 +- .../rest/resource/RolloutResource.java | 2 +- .../rest/resource/SoftwareModuleMapper.java | 2 +- .../rest/resource/SoftwareModuleResource.java | 2 +- .../resource/SoftwareModuleTypeResource.java | 2 +- .../resource/SystemManagementResource.java | 2 +- .../hawkbit/rest/resource/TargetResource.java | 2 +- .../rest/resource/TargetTagResource.java | 4 +- .../resource/DistributionSetResourceTest.java | 8 +- .../rest/resource/TargetResourceTest.java | 7 +- .../artifacts/details/ArtifactBeanQuery.java | 2 +- .../UploadViewConfirmationWindowLayout.java | 2 +- .../smtable/BaseSwModuleBeanQuery.java | 4 +- .../SoftwareModuleAddUpdateWindow.java | 2 +- .../smtable/SoftwareModuleTable.java | 2 +- .../CreateUpdateSoftwareTypeLayout.java | 2 +- .../smtype/SMTypeFilterButtonClick.java | 2 +- .../common/DistributionSetTypeBeanQuery.java | 2 +- .../common/SoftwareModuleTypeBeanQuery.java | 4 +- .../tagdetails/AbstractTargetTagToken.java | 2 +- .../tagdetails/DistributionTagToken.java | 2 +- .../ui/common/tagdetails/TargetTagToken.java | 2 +- .../CreateUpdateDistSetTypeLayout.java | 4 +- .../dstable/DistributionSetDetails.java | 2 +- .../dstable/DistributionSetTable.java | 15 +- .../dstable/ManageDistBeanQuery.java | 4 +- .../footer/DSDeleteActionsLayout.java | 2 +- ...DistributionsConfirmationWindowLayout.java | 2 +- .../smtable/SwModuleBeanQuery.java | 4 +- .../distributions/smtable/SwModuleTable.java | 2 +- .../smtype/DistSMTypeFilterButtonClick.java | 2 +- .../CreateOrUpdateFilterHeader.java | 2 +- .../CustomTargetBeanQuery.java | 2 +- .../FilterQueryValidation.java | 2 +- .../TargetFilterBeanQuery.java | 2 +- .../filtermanagement/TargetFilterTable.java | 2 +- .../DistributionAddUpdateWindowLayout.java | 4 +- .../dstable/DistributionBeanQuery.java | 4 +- .../management/dstable/DistributionTable.java | 2 +- .../dstag/DistributionTagBeanQuery.java | 4 +- .../management/footer/CountMessageLabel.java | 2 +- .../footer/DeleteActionsLayout.java | 2 +- .../ManangementConfirmationWindowLayout.java | 2 +- .../management/tag/CreateUpdateTagLayout.java | 2 +- .../targettable/BulkUploadHandler.java | 4 +- .../TargetAddUpdateWindowLayout.java | 2 +- .../targettable/TargetBeanQuery.java | 4 +- .../TargetBulkUpdateWindowLayout.java | 2 +- .../management/targettable/TargetTable.java | 4 +- .../CustomTargetTagFilterButtonClick.java | 2 +- .../targettag/TargetTagBeanQuery.java | 4 +- .../targettag/TargetTagFilterButtons.java | 2 +- .../rollout/AddUpdateRolloutWindowLayout.java | 2 +- .../rollout/DistributionBeanQuery.java | 2 +- .../ui/rollout/rollout/RolloutBeanQuery.java | 2 +- .../DefaultDistributionSetTypeLayout.java | 2 +- .../hawkbit/ui/utils/HawkbitCommonUtil.java | 2 +- 135 files changed, 4711 insertions(+), 4055 deletions(-) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/ActionRepository.java (99%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/ActionStatusRepository.java (98%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/BaseEntityRepository.java (97%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/DistributionSetMetadataRepository.java (96%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/DistributionSetRepository.java (99%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/DistributionSetTagRepository.java (97%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/DistributionSetTypeRepository.java (98%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/EclipseLinkTargetInfoRepository.java (98%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/ExternalArtifactProviderRepository.java (94%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/ExternalArtifactRepository.java (96%) create mode 100644 hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaArtifactManagement.java create mode 100644 hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java create mode 100644 hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java create mode 100644 hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java create mode 100644 hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaReportManagement.java create mode 100644 hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutGroupManagement.java create mode 100644 hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java create mode 100644 hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/LocalArtifactRepository.java (98%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/NoCountPagingRepository.java (98%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/OffsetBasedPageRequest.java (98%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/RolloutGroupRepository.java (99%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/RolloutRepository.java (98%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/RolloutTargetGroupRepository.java (95%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/SoftwareManagement.java (99%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/SoftwareModuleMetadataRepository.java (97%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/SoftwareModuleRepository.java (99%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/SoftwareModuleTypeRepository.java (97%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/SystemManagement.java (99%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/TagManagement.java (99%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/TargetFilterQueryManagement.java (99%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/TargetFilterQueryRepository.java (96%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/TargetInfoRepository.java (98%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/TargetManagement.java (99%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/TargetRepository.java (99%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/TargetTagRepository.java (97%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/TargetWithActionType.java (97%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/TenantConfigurationRepository.java (97%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/TenantKeyGenerator.java (95%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/TenantMetaDataRepository.java (97%) rename hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/{ => jpa}/TenantStatsManagement.java (82%) diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java index d5e8db96f..1c562364f 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java @@ -12,8 +12,8 @@ import java.util.HashMap; import java.util.Map; import org.eclipse.hawkbit.aspects.ExceptionMappingAspectHandler; -import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; +import org.eclipse.hawkbit.repository.jpa.SystemManagement; import org.eclipse.hawkbit.repository.model.helper.AfterTransactionCommitExecutorHolder; import org.eclipse.hawkbit.repository.model.helper.CacheManagerHolder; import org.eclipse.hawkbit.repository.model.helper.SecurityTokenGeneratorHolder; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/EntityChangeEventListener.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/EntityChangeEventListener.java index 7bfbd957e..2f63a3122 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/EntityChangeEventListener.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/eventbus/EntityChangeEventListener.java @@ -19,7 +19,7 @@ import org.eclipse.hawkbit.eventbus.event.TargetCreatedEvent; import org.eclipse.hawkbit.eventbus.event.TargetDeletedEvent; import org.eclipse.hawkbit.eventbus.event.TargetInfoUpdateEvent; import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor; -import org.eclipse.hawkbit.repository.TargetRepository; +import org.eclipse.hawkbit.repository.jpa.TargetRepository; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetInfo; import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity; @@ -66,7 +66,7 @@ public class EntityChangeEventListener { * in case exception happens in the * {@link ProceedingJoinPoint#proceed()} */ - @Around("execution(* org.eclipse.hawkbit.repository.TargetInfoRepository.save(..))") + @Around("execution(* org.eclipse.hawkbit.repository.jpa.TargetInfoRepository.save(..))") // Exception squid:S00112 - Is aspectJ proxy @SuppressWarnings({ "squid:S00112" }) public Object targetCreated(final ProceedingJoinPoint joinpoint) throws Throwable { @@ -93,7 +93,7 @@ public class EntityChangeEventListener { * in case exception happens in the * {@link ProceedingJoinPoint#proceed()} */ - @Around("execution(* org.eclipse.hawkbit.repository.TargetRepository.deleteByIdIn(..))") + @Around("execution(* org.eclipse.hawkbit.repository.jpa.TargetRepository.deleteByIdIn(..))") // Exception squid:S00112 - Is aspectJ proxy @SuppressWarnings({ "squid:S00112" }) public Object targetDeletedById(final ProceedingJoinPoint joinpoint) throws Throwable { @@ -115,8 +115,8 @@ public class EntityChangeEventListener { * in case exception happens in the * {@link ProceedingJoinPoint#proceed()} */ - @Around("execution(* org.eclipse.hawkbit.repository.TargetRepository.delete(..))") - // Exception squid:S00112 - Is aspectJ proxy + @Around("execution(* org.eclipse.hawkbit.repository.jpa.TargetRepository.delete(..))") + // Exception squid:S00112 - Is aspectJ proxy @SuppressWarnings({ "squid:S00112", "unchecked" }) public Object targetDeleted(final ProceedingJoinPoint joinpoint) throws Throwable { final String currentTenant = tenantAware.getCurrentTenant(); @@ -146,7 +146,7 @@ public class EntityChangeEventListener { afterCommit.afterCommit(() -> eventBus.post(new TargetDeletedEvent(tenant, targetId))); } - private boolean isTargetInfoNew(final Object targetInfo) { + private static boolean isTargetInfoNew(final Object targetInfo) { return ((TargetInfo) targetInfo).isNew(); } diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ArtifactManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ArtifactManagement.java index 024ec11cb..977b9f171 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ArtifactManagement.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ArtifactManagement.java @@ -13,11 +13,7 @@ import java.util.List; import javax.validation.constraints.NotNull; -import org.eclipse.hawkbit.artifact.repository.ArtifactRepository; -import org.eclipse.hawkbit.artifact.repository.ArtifactStoreException; -import org.eclipse.hawkbit.artifact.repository.HashNotMatchException; import org.eclipse.hawkbit.artifact.repository.model.DbArtifact; -import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException; import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException; @@ -31,47 +27,110 @@ import org.eclipse.hawkbit.repository.model.ExternalArtifact; import org.eclipse.hawkbit.repository.model.ExternalArtifactProvider; import org.eclipse.hawkbit.repository.model.LocalArtifact; import org.eclipse.hawkbit.repository.model.SoftwareModule; -import org.eclipse.hawkbit.repository.specifications.SoftwareModuleSpecification; import org.hibernate.validator.constraints.NotEmpty; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.domain.Specification; -import org.springframework.data.jpa.repository.Modifying; import org.springframework.hateoas.Identifiable; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; /** * Service for {@link Artifact} management operations. * */ -@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) -@Validated -@Service -public class ArtifactManagement { +public interface ArtifactManagement { - private static final Logger LOG = LoggerFactory.getLogger(ArtifactManagement.class); + /** + * Creates {@link ExternalArtifact} based on given provider. + * + * @param externalRepository + * the artifact is located in + * @param urlSuffix + * of the artifact + * {@link ExternalArtifactProvider#getDefaultSuffix()} is used if + * empty. + * @param moduleId + * to assign the artifact to + * + * @return created {@link ExternalArtifact} + * + * @throws EntityNotFoundException + * if {@link SoftwareModule} with given ID does not exist + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) + ExternalArtifact createExternalArtifact(@NotNull ExternalArtifactProvider externalRepository, String urlSuffix, + @NotNull Long moduleId); - @Autowired - private LocalArtifactRepository localArtifactRepository; + /** + * Persists {@link ExternalArtifactProvider} based on given properties. + * + * @param name + * of the provided + * @param description + * which is optional + * @param basePath + * of all {@link ExternalArtifact}s of the provider + * @param defaultUrlSuffix + * that is used if {@link ExternalArtifact#getUrlSuffix()} is + * empty. + * @return created {@link ExternalArtifactProvider} + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) + ExternalArtifactProvider createExternalArtifactProvider(@NotEmpty String name, String description, + @NotNull String basePath, String defaultUrlSuffix); - @Autowired - private ExternalArtifactRepository externalArtifactRepository; + /** + * Persists artifact binary as provided by given InputStream. assign the + * artifact in addition to given {@link SoftwareModule}. + * + * @param inputStream + * to read from for artifact binary + * @param moduleId + * to assign the new artifact to + * @param filename + * of the artifact + * @param overrideExisting + * to true if the artifact binary can be overridden + * if it already exists + * + * @return uploaded {@link LocalArtifact} + * + * @throw ArtifactUploadFailedException if upload fails + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + default LocalArtifact createLocalArtifact(final InputStream inputStream, final Long moduleId, final String filename, + final boolean overrideExisting) { + return createLocalArtifact(inputStream, moduleId, filename, null, null, overrideExisting, null); + } - @Autowired - private SoftwareModuleRepository softwareModuleRepository; - - @Autowired - private ExternalArtifactProviderRepository externalArtifactProviderRepository; - - @Autowired - private ArtifactRepository artifactRepository; + /** + * Persists artifact binary as provided by given InputStream. assign the + * artifact in addition to given {@link SoftwareModule}. + * + * @param inputStream + * to read from for artifact binary + * @param moduleId + * to assign the new artifact to + * @param filename + * of the artifact + * @param overrideExisting + * to true if the artifact binary can be overridden + * if it already exists + * @param contentType + * the contentType of the file + * + * @return uploaded {@link LocalArtifact} + * + * @throw ArtifactUploadFailedException if upload fails + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + default LocalArtifact createLocalArtifact(final InputStream inputStream, final Long moduleId, final String filename, + final boolean overrideExisting, final String contentType) { + return createLocalArtifact(inputStream, moduleId, filename, null, null, overrideExisting, contentType); + } /** * Persists artifact binary as provided by given InputStream. assign the @@ -105,59 +164,112 @@ public class ArtifactManagement { * @throws InvalidSHA1HashException * if check against provided SHA1 checksum failed */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) - public LocalArtifact createLocalArtifact(@NotNull final InputStream stream, @NotNull final Long moduleId, - @NotEmpty final String filename, final String providedMd5Sum, final String providedSha1Sum, - final boolean overrideExisting, final String contentType) { - DbArtifact result = null; + LocalArtifact createLocalArtifact(@NotNull InputStream stream, @NotNull Long moduleId, @NotEmpty String filename, + String providedMd5Sum, String providedSha1Sum, boolean overrideExisting, String contentType); - final SoftwareModule softwareModule = getModuleAndThrowExceptionIfThatFails(moduleId); + /** + * Deletes {@link Artifact} based on given id. + * + * @param id + * of the {@link Artifact} that has to be deleted. + * @throws ArtifactDeleteFailedException + * if deletion failed (MongoDB is not available) + * + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) + void deleteExternalArtifact(@NotNull Long id); - final LocalArtifact existing = checkForExistingArtifact(filename, overrideExisting, softwareModule); + /** + * Deletes a local artifact. + * + * @param existing + * the related local artifact + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) + void deleteLocalArtifact(@NotNull LocalArtifact existing); - try { - result = artifactRepository.store(stream, filename, contentType, - new DbArtifactHash(providedSha1Sum, providedMd5Sum)); - } catch (final ArtifactStoreException e) { - throw new ArtifactUploadFailedException(e); - } catch (final HashNotMatchException e) { - if (e.getHashFunction().equals(HashNotMatchException.SHA1)) { - throw new InvalidSHA1HashException(e.getMessage(), e); - } else { - throw new InvalidMD5HashException(e.getMessage(), e); - } - } - if (result == null) { - return null; - } + /** + * Deletes {@link Artifact} based on given id. + * + * @param id + * of the {@link Artifact} that has to be deleted. + * @throws ArtifactDeleteFailedException + * if deletion failed (MongoDB is not available) + * + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) + void deleteLocalArtifact(@NotNull Long id); - return storeArtifactMetadata(softwareModule, filename, result, existing); - } + /** + * Searches for {@link Artifact} with given {@link Identifiable}. + * + * @param id + * to search for + * @return found {@link Artifact} or null is it could not be + * found. + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + Artifact findArtifact(@NotNull Long id); - private static LocalArtifact checkForExistingArtifact(final String filename, final boolean overrideExisting, - final SoftwareModule softwareModule) { - if (softwareModule.getLocalArtifactByFilename(filename).isPresent()) { - if (overrideExisting) { - LOG.debug("overriding existing artifact with new filename {}", filename); - return softwareModule.getLocalArtifactByFilename(filename).get(); - } else { - throw new EntityAlreadyExistsException("File with that name already exists in the Software Module"); - } - } - return null; - } + /** + * Find by artifact by software module id and filename. + * + * @param filename + * file name + * @param softwareModuleId + * software module id. + * @return LocalArtifact if artifact present + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR + + SpringEvalExpressions.IS_CONTROLLER) + List findByFilenameAndSoftwareModule(@NotNull String filename, @NotNull Long softwareModuleId); - private SoftwareModule getModuleAndThrowExceptionIfThatFails(final Long moduleId) { - final SoftwareModule softwareModule = findSoftwareModuleWithDetails(moduleId); + /** + * Find all local artifact by sha1 and return the first artifact. + * + * @param sha1 + * the sha1 + * @return the first local artifact + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR + + SpringEvalExpressions.IS_CONTROLLER) + LocalArtifact findFirstLocalArtifactsBySHA1(@NotNull String sha1); - if (softwareModule == null) { - LOG.debug("no software module with ID {} exists", moduleId); - throw new EntityNotFoundException("Software Module: " + moduleId); - } - return softwareModule; - } + /** + * Searches for {@link Artifact} with given file name. + * + * @param filename + * to search for + * @return found List of {@link LocalArtifact}s. + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR + + SpringEvalExpressions.IS_CONTROLLER) + List findLocalArtifactByFilename(@NotNull String filename); + + /** + * Get local artifact for a base software module. + * + * @param pageReq + * Pageable + * @param swId + * software module id + * @return Page + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + Page findLocalArtifactBySoftwareModule(@NotNull Pageable pageReq, @NotNull Long swId); + + /** + * Finds {@link SoftwareModule} by given id. + * + * @param id + * to search for + * @return the found {@link SoftwareModule}s or null if not + * found. + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR + + SpringEvalExpressions.IS_CONTROLLER) + SoftwareModule findSoftwareModuleById(@NotNull Long id); /** * Retrieves software module including details ( @@ -170,7 +282,7 @@ public class ArtifactManagement { * @return the found {@link SoftwareModule}s */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - protected SoftwareModule findSoftwareModuleWithDetails(@NotNull final Long id) { + default SoftwareModule findSoftwareModuleWithDetails(@NotNull final Long id) { final SoftwareModule result = findSoftwareModuleById(id); if (result != null) { result.getArtifacts().size(); @@ -179,211 +291,6 @@ public class ArtifactManagement { return result; } - /** - * Find all local artifact by sha1 and return the first artifact. - * - * @param sha1 - * the sha1 - * @return the first local artifact - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR - + SpringEvalExpressions.IS_CONTROLLER) - public LocalArtifact findFirstLocalArtifactsBySHA1(final String sha1) { - return localArtifactRepository.findFirstByGridFsFileName(sha1); - } - - /** - * Finds {@link SoftwareModule} by given id. - * - * @param id - * to search for - * @return the found {@link SoftwareModule}s or null if not - * found. - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR - + SpringEvalExpressions.IS_CONTROLLER) - protected SoftwareModule findSoftwareModuleById(@NotNull final Long id) { - - final Specification spec = SoftwareModuleSpecification.byId(id); - - return softwareModuleRepository.findOne(spec); - } - - private LocalArtifact storeArtifactMetadata(final SoftwareModule softwareModule, final String providedFilename, - final DbArtifact result, final LocalArtifact existing) { - LocalArtifact artifact = existing; - if (existing == null) { - artifact = new LocalArtifact(result.getHashes().getSha1(), providedFilename, softwareModule); - } - artifact.setMd5Hash(result.getHashes().getMd5()); - artifact.setSha1Hash(result.getHashes().getSha1()); - artifact.setSize(result.getSize()); - - LOG.debug("storing new artifact into repository {}", artifact); - return localArtifactRepository.save(artifact); - } - - /** - * Persists {@link ExternalArtifactProvider} based on given properties. - * - * @param name - * of the provided - * @param description - * which is optional - * @param basePath - * of all {@link ExternalArtifact}s of the provider - * @param defaultUrlSuffix - * that is used if {@link ExternalArtifact#getUrlSuffix()} is - * empty. - * @return created {@link ExternalArtifactProvider} - */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) - public ExternalArtifactProvider createExternalArtifactProvider(@NotEmpty final String name, - final String description, @NotNull final String basePath, final String defaultUrlSuffix) { - return externalArtifactProviderRepository - .save(new ExternalArtifactProvider(name, description, basePath, defaultUrlSuffix)); - } - - /** - * Creates {@link ExternalArtifact} based on given provider. - * - * @param externalRepository - * the artifact is located in - * @param urlSuffix - * of the artifact - * {@link ExternalArtifactProvider#getDefaultSuffix()} is used if - * empty. - * @param moduleId - * to assign the artifact to - * - * @return created {@link ExternalArtifact} - * - * @throws EntityNotFoundException - * if {@link SoftwareModule} with given ID does not exist - */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) - public ExternalArtifact createExternalArtifact(@NotNull final ExternalArtifactProvider externalRepository, - final String urlSuffix, @NotNull final Long moduleId) { - - final SoftwareModule module = getModuleAndThrowExceptionIfThatFails(moduleId); - return externalArtifactRepository.save(new ExternalArtifact(externalRepository, urlSuffix, module)); - } - - /** - * Deletes {@link Artifact} based on given id. - * - * @param id - * of the {@link Artifact} that has to be deleted. - * @throws ArtifactDeleteFailedException - * if deletion failed (MongoDB is not available) - * - */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) - public void deleteLocalArtifact(@NotNull final Long id) { - final LocalArtifact existing = localArtifactRepository.findOne(id); - - if (null == existing) { - return; - } - - deleteGridFsArtifact(existing); - - existing.getSoftwareModule().removeArtifact(existing); - softwareModuleRepository.save(existing.getSoftwareModule()); - localArtifactRepository.delete(id); - } - - /** - * Delete a grid fs file. - * - * @param existing - * the related local artifact - */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) - public void deleteGridFsArtifact(@NotNull final LocalArtifact existing) { - if (existing == null) { - return; - } - - boolean artifactIsOnlyUsedByOneSoftwareModule = true; - for (final LocalArtifact lArtifact : localArtifactRepository - .findByGridFsFileName(existing.getGridFsFileName())) { - if (!lArtifact.getSoftwareModule().isDeleted() - && Long.compare(lArtifact.getSoftwareModule().getId(), existing.getSoftwareModule().getId()) != 0) { - artifactIsOnlyUsedByOneSoftwareModule = false; - break; - } - } - - if (artifactIsOnlyUsedByOneSoftwareModule) { - try { - LOG.debug("deleting artifact from repository {}", existing.getGridFsFileName()); - artifactRepository.deleteBySha1(existing.getGridFsFileName()); - } catch (final ArtifactStoreException e) { - throw new ArtifactDeleteFailedException(e); - } - } - } - - /** - * Deletes {@link Artifact} based on given id. - * - * @param id - * of the {@link Artifact} that has to be deleted. - * @throws ArtifactDeleteFailedException - * if deletion failed (MongoDB is not available) - * - */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) - public void deleteExternalArtifact(@NotNull final Long id) { - final ExternalArtifact existing = externalArtifactRepository.findOne(id); - - if (null == existing) { - return; - } - - existing.getSoftwareModule().removeArtifact(existing); - softwareModuleRepository.save(existing.getSoftwareModule()); - externalArtifactRepository.delete(id); - } - - /** - * Searches for {@link Artifact} with given file name. - * - * @param filename - * to search for - * @return found List of {@link LocalArtifact}s. - */ - @NotNull - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR - + SpringEvalExpressions.IS_CONTROLLER) - public List findLocalArtifactByFilename(@NotNull final String filename) { - return localArtifactRepository.findByFilename(filename); - } - - /** - * Searches for {@link Artifact} with given {@link Identifiable}. - * - * @param id - * to search for - * @return found {@link Artifact} or null is it could not be - * found. - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public Artifact findArtifact(@NotNull final Long id) { - return localArtifactRepository.findOne(id); - } - /** * Loads {@link org.eclipse.hawkbit.artifact.server.json.model.Artifact} * from store for given {@link LocalArtifact}. @@ -398,98 +305,6 @@ public class ArtifactManagement { */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DOWNLOAD_ARTIFACT + SpringEvalExpressions.HAS_AUTH_OR + SpringEvalExpressions.HAS_CONTROLLER_DOWNLOAD) - public DbArtifact loadLocalArtifactBinary(@NotNull final LocalArtifact artifact) { - final DbArtifact result = artifactRepository.getArtifactBySha1(artifact.getGridFsFileName()); - if (result == null) { - throw new GridFSDBFileNotFoundException(artifact.getGridFsFileName()); - } + DbArtifact loadLocalArtifactBinary(@NotNull LocalArtifact artifact); - return result; - } - - /** - * Persists artifact binary as provided by given InputStream. assign the - * artifact in addition to given {@link SoftwareModule}. - * - * @param inputStream - * to read from for artifact binary - * @param moduleId - * to assign the new artifact to - * @param filename - * of the artifact - * @param overrideExisting - * to true if the artifact binary can be overridden - * if it already exists - * @param contentType - * the contentType of the file - * - * @return uploaded {@link LocalArtifact} - * - * @throw ArtifactUploadFailedException if upload fails - */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) - public LocalArtifact createLocalArtifact(final InputStream inputStream, final Long moduleId, final String filename, - final boolean overrideExisting, final String contentType) { - return createLocalArtifact(inputStream, moduleId, filename, null, null, overrideExisting, contentType); - } - - /** - * Persists artifact binary as provided by given InputStream. assign the - * artifact in addition to given {@link SoftwareModule}. - * - * @param inputStream - * to read from for artifact binary - * @param moduleId - * to assign the new artifact to - * @param filename - * of the artifact - * @param overrideExisting - * to true if the artifact binary can be overdiden - * if it already exists - * - * @return uploaded {@link LocalArtifact} - * - * @throw ArtifactUploadFailedException if upload failes - */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) - public LocalArtifact createLocalArtifact(final InputStream inputStream, final Long moduleId, final String filename, - final boolean overrideExisting) { - return createLocalArtifact(inputStream, moduleId, filename, null, null, overrideExisting, null); - } - - /** - * Get local artifact for a base software module. - * - * @param pageReq - * Pageable - * @param swId - * software module id - * @return Page - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public Page findLocalArtifactBySoftwareModule(@NotNull final Pageable pageReq, - @NotNull final Long swId) { - return localArtifactRepository.findBySoftwareModuleId(pageReq, swId); - } - - /** - * Find by artifact by software module id and filename. - * - * @param filename - * file name - * @param softwareModuleId - * software module id. - * @return LocalArtifact if artifact present - */ - @NotNull - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR - + SpringEvalExpressions.IS_CONTROLLER) - public List findByFilenameAndSoftwareModule(@NotNull final String filename, - @NotNull final Long softwareModuleId) { - return localArtifactRepository.findByFilenameAndSoftwareModuleId(filename, softwareModuleId); - } -} +} \ No newline at end of file diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java index 11879edfd..b70ad3bdd 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java @@ -12,10 +12,6 @@ import java.net.URI; import java.util.List; import java.util.Map; -import javax.persistence.EntityManager; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Root; import javax.validation.constraints.NotNull; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; @@ -26,78 +22,102 @@ import org.eclipse.hawkbit.repository.exception.ToManyStatusEntriesException; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.ActionStatus; -import org.eclipse.hawkbit.repository.model.ActionStatus_; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.LocalArtifact; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetInfo; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; -import org.eclipse.hawkbit.repository.model.Target_; -import org.eclipse.hawkbit.repository.model.TenantConfiguration; -import org.eclipse.hawkbit.repository.specifications.ActionSpecifications; -import org.eclipse.hawkbit.security.HawkbitSecurityProperties; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.hibernate.validator.constraints.NotEmpty; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.jpa.domain.Specification; -import org.springframework.data.jpa.repository.Modifying; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; /** - * Service layer for all operations of the controller API (with access - * permissions only for the controller). - * - * + * Service layer for all operations of the DDI API (with access permissions only + * for the controller). * */ -@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) -@Validated -@Service -public class ControllerManagement { - private static final Logger LOG = LoggerFactory.getLogger(ControllerManagement.class); - private static final Logger LOG_DOS = LoggerFactory.getLogger("server-security.dos"); +public interface ControllerManagement { - public static final String SERVER_MESSAGE_PREFIX = "Update Server: "; + String SERVER_MESSAGE_PREFIX = "Update Server: "; - @Autowired - private EntityManager entityManager; + /** + * Simple addition of a new {@link ActionStatus} entry to the {@link Action} + * . No state changes. + * + * @param statusMessage + * to add to the action + */ + @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) + void addInformationalActionStatus(@NotNull ActionStatus statusMessage); - @Autowired - private ActionRepository actionRepository; + /** + * Adds an {@link ActionStatus} for a cancel {@link Action} including + * potential state changes for the target and the {@link Action} itself. + * + * @param actionStatus + * to be added + * @return the persisted {@link Action} + * + * @throws EntityAlreadyExistsException + * if a given entity already exists + * + */ + @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) + Action addCancelActionStatus(@NotNull ActionStatus actionStatus); - @Autowired - private TargetRepository targetRepository; + /** + * Adds an {@link ActionStatus} entry for an update {@link Action} including + * potential state changes for the target and the {@link Action} itself. + * + * @param actionStatus + * to be added + * @return the updated {@link Action} + * + * @throws EntityAlreadyExistsException + * if a given entity already exists + * @throws ToManyStatusEntriesException + * if more than the allowed number of status entries are + * inserted + */ + @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) + Action addUpdateActionStatus(@NotNull ActionStatus actionStatus); - @Autowired - private TargetManagement targetManagement; + /** + * Retrieves all {@link Action}s which are active and assigned to a + * {@link Target}. + * + * @param target + * the target to retrieve the actions from + * @return a list of actions assigned to given target which are active + */ + @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) + List findActionByTargetAndActive(@NotNull Target target); - @Autowired - private DeploymentManagement deploymentManagement; + /** + * Get the {@link Action} entity for given actionId with all lazy + * attributes. + * + * @param actionId + * to be id of the action + * @return the corresponding {@link Action} + */ + @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) + Action findActionWithDetails(@NotNull Long actionId); - @Autowired - private TargetInfoRepository targetInfoRepository; - - @Autowired - private SoftwareModuleRepository softwareModuleRepository; - - @Autowired - private ActionStatusRepository actionStatusRepository; - - @Autowired - private HawkbitSecurityProperties securityProperties; - - @Autowired - private TenantConfigurationRepository tenantConfigurationRepository; - - @Autowired - private TenantConfigurationManagement tenantConfigurationManagement; + /** + * register new target in the repository (plug-and-play). + * + * @param controllerId + * reference + * @param address + * the client IP address of the target, might be {@code null} + * @return target reference + */ + @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) + Target findOrRegisterTargetIfItDoesNotexist(@NotEmpty String controllerId, URI address); /** * Retrieves all {@link SoftwareModule}s which are assigned to the given @@ -110,46 +130,13 @@ public class ControllerManagement { * {@code distributionSet} */ @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) - public String findPollingTime() { - final TenantConfigurationKey configurationKey = TenantConfigurationKey.POLLING_TIME_INTERVAL; - final Class propertyType = String.class; - tenantConfigurationManagement.validateTenantConfigurationDataType(configurationKey, propertyType); - final TenantConfiguration tenantConfiguration = tenantConfigurationRepository - .findByKey(configurationKey.getKeyName()); - return tenantConfigurationManagement - .buildTenantConfigurationValueByKey(configurationKey, propertyType, tenantConfiguration).getValue(); - } - - /** - * Refreshes the time of the last time the controller has been connected to - * the server. - * - * @param targetid - * of the target to to update - * @param address - * the client address of the target, might be {@code null} - * @return the updated target - * - * @throws EntityNotFoundException - * if target with given ID could not be found - */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) - public Target updateLastTargetQuery(@NotEmpty final String targetid, final URI address) { - final Target target = targetRepository.findByControllerId(targetid); - if (target == null) { - throw new EntityNotFoundException(targetid); - } - - return updateLastTargetQuery(target.getTargetInfo(), address).getTarget(); - } + List findSoftwareModulesByDistributionSet(@NotNull DistributionSet distributionSet); /** * Retrieves last {@link UpdateAction} for a download of an artifact of * given module and target. * - * @param targetId + * @param controllerId * to look for * @param module * that should be assigned to the target @@ -159,17 +146,28 @@ public class ControllerManagement { * if action for given combination could not be found */ @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) - public Action getActionForDownloadByTargetAndSoftwareModule(@NotNull final String targetId, - @NotNull final SoftwareModule module) { - final List action = actionRepository.findActionByTargetAndSoftwareModule(targetId, module); + Action getActionForDownloadByTargetAndSoftwareModule(@NotEmpty String controllerId, @NotNull SoftwareModule module); - if (action.isEmpty() || action.get(0).isCancelingOrCanceled()) { - throw new EntityNotFoundException( - "No assigment found for module " + module.getId() + " to target " + targetId); - } + /** + * @return current {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}. + */ + @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) + String getPollingTime(); - return action.get(0); - } + /** + * An direct access to the security token of an + * {@link Target#getSecurityToken()} without authorization. This is + * necessary to be able to access the security-token without any + * security-context information because the security-token is used for + * authentication. + * + * @param controllerId + * the ID of the controller to retrieve the security token for + * @return the security context of the target, in case no target exists for + * the given controllerId {@code null} is returned + */ + @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) + String getSecurityTokenByControllerId(@NotEmpty String controllerId); /** * Checks if a given target has currently or has even been assigned to the @@ -178,7 +176,7 @@ public class ControllerManagement { * assigned or had ever been assigned to the target and so it's visible to a * specific target e.g. for downloading. * - * @param targetId + * @param controllerId * the ID of the target to check * @param localArtifact * the artifact to verify if the given target had even been @@ -187,14 +185,56 @@ public class ControllerManagement { * relation to the given artifact through the action history, * otherwise {@code false} */ - public boolean hasTargetArtifactAssigned(@NotNull final String targetId, - @NotNull final LocalArtifact localArtifact) { - final Target target = targetRepository.findByControllerId(targetId); - if (target == null) { - return false; - } - return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(target, localArtifact)) > 0; - } + @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) + boolean hasTargetArtifactAssigned(@NotNull String controllerId, @NotNull LocalArtifact localArtifact); + + /** + * Registers retrieved status for given {@link Target} and {@link Action} if + * it does not exist yet. + * + * @param action + * to the handle status for + * @param message + * for the status + * @return the update action in case the status has been changed to + * {@link Status#RETRIEVED} + */ + @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) + Action registerRetrieved(@NotNull Action action, String message); + + /** + * Updates attributes of the controller. + * + * @param controllerId + * to update + * @param attributes + * to insert + * + * @return updated {@link Target} + * + * @throws EntityNotFoundException + * if target that has to be updated could not be found + * @throws ToManyAttributeEntriesException + * if maximum + */ + @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) + Target updateControllerAttributes(@NotEmpty String controllerId, @NotNull Map attributes); + + /** + * Refreshes the time of the last time the controller has been connected to + * the server. + * + * @param controllerId + * of the target to to update + * @param address + * the client address of the target, might be {@code null} + * @return the updated target + * + * @throws EntityNotFoundException + * if target with given ID could not be found + */ + @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) + Target updateLastTargetQuery(@NotEmpty String controllerId, URI address); /** * Refreshes the time of the last time the controller has been connected to @@ -207,83 +247,12 @@ public class ControllerManagement { * @return the updated target * */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) - public TargetInfo updateLastTargetQuery(@NotNull final TargetInfo target, final URI address) { + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + default TargetInfo updateLastTargetQuery(@NotNull final TargetInfo target, @NotNull final URI address) { return updateTargetStatus(target, null, System.currentTimeMillis(), address); } - /** - * Retrieves all {@link Action}s which are active and assigned to a - * {@link Target}. - * - * @param target - * the target to retrieve the actions from - * @return a list of actions assigned to given target which are active - */ - @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) - public List findActionByTargetAndActive(final Target target) { - return actionRepository.findByTargetAndActiveOrderByIdAsc(target, true); - } - - /** - * Retrieves all {@link SoftwareModule}s which are assigned to the given - * {@link DistributionSet}. - * - * @param distributionSet - * the distribution set which should be assigned to the returned - * {@link SoftwareModule}s - * @return a list of {@link SoftwareModule}s assigned to given - * {@code distributionSet} - */ - @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) - public List findSoftwareModulesByDistributionSet(final DistributionSet distributionSet) { - return softwareModuleRepository.findByAssignedTo(distributionSet); - } - - /** - * Get the {@link Action} entity for given actionId with all lazy - * attributes. - * - * @param actionId - * to be id of the action - * @return the corresponding {@link Action} - */ - @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) - public Action findActionWithDetails(@NotNull final Long actionId) { - return actionRepository.findById(actionId); - } - - /** - * register new target in the repository (plug-and-play). - * - * @param targetid - * reference - * @param address - * the client IP address of the target, might be {@code null} - * @return target reference - */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) - public Target findOrRegisterTargetIfItDoesNotexist(@NotEmpty final String targetid, final URI address) { - final Specification spec = (targetRoot, query, cb) -> cb.equal(targetRoot.get(Target_.controllerId), - targetid); - - Target target = targetRepository.findOne(spec); - - if (target == null) { - target = new Target(targetid); - target.setDescription("Plug and Play target: " + targetid); - target.setName(targetid); - return targetManagement.createTarget(target, TargetUpdateStatus.REGISTERED, System.currentTimeMillis(), - address); - } - - return updateLastTargetQuery(target.getTargetInfo(), address).getTarget(); - } - /** * Update selective the target status of a given {@code target}. * @@ -300,313 +269,8 @@ public class ControllerManagement { * the client address of the target, might be {@code null} * @return the updated TargetInfo */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) - public TargetInfo updateTargetStatus(@NotNull final TargetInfo targetInfo, final TargetUpdateStatus status, - final Long lastTargetQuery, final URI address) { - final TargetInfo mtargetInfo = entityManager.merge(targetInfo); - if (status != null) { - mtargetInfo.setUpdateStatus(status); - } - if (lastTargetQuery != null) { - mtargetInfo.setLastTargetQuery(lastTargetQuery); - } - if (address != null) { - mtargetInfo.setAddress(address.toString()); - } - return targetInfoRepository.save(mtargetInfo); - } + TargetInfo updateTargetStatus(@NotNull TargetInfo targetInfo, TargetUpdateStatus status, Long lastTargetQuery, + URI address); - /** - * Adds an {@link ActionStatus} for a {@link UpdateAction} and cancels the - * {@link UpdateAction} if necessary. - * - * @param actionStatus - * to be updated - * @param action - * the status is for - * @return the persisted {@link Action} - * - */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) - public Action addCancelActionStatus(@NotNull final ActionStatus actionStatus, final Action action) { - - checkForToManyStatusEntries(action); - action.setStatus(actionStatus.getStatus()); - - switch (actionStatus.getStatus()) { - case WARNING: - case ERROR: - case RUNNING: - break; - case CANCELED: - case FINISHED: - // in case of successful cancellation we also report the success at - // the canceled action itself. - actionStatus.addMessage( - ControllerManagement.SERVER_MESSAGE_PREFIX + "Cancellation completion is finished sucessfully."); - deploymentManagement.successCancellation(action); - break; - case RETRIEVED: - actionStatus.addMessage(ControllerManagement.SERVER_MESSAGE_PREFIX + "Cancellation request retrieved."); - break; - default: - } - actionRepository.save(action); - actionStatusRepository.save(actionStatus); - - return action; - } - - /** - * Updates an {@link ActionStatus} for a {@link UpdateAction}. - * - * @param actionStatus - * to be updated - * @param action - * the update is for - * @return the persisted {@link Action} - * - * @throws EntityAlreadyExistsException - * if a given entity already exists - * @throws ToManyStatusEntriesException - * if more than the allowed number of status entries are - * inserted - */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) - public Action addUpdateActionStatus(@NotNull final ActionStatus actionStatus, final Action action) { - - if (!action.isActive()) { - LOG.debug("Update of actionStatus {} for action {} not possible since action not active anymore.", - actionStatus.getId(), action.getId()); - return action; - } - return handleAddUpdateActionStatus(actionStatus, action); - } - - /** - * Sets {@link TargetUpdateStatus} based on given {@link ActionStatus}. - * - * @param actionStatus - * @param action - * @return - */ - public Action handleAddUpdateActionStatus(final ActionStatus actionStatus, final Action action) { - LOG.debug("addUpdateActionStatus for action {}", action.getId()); - - final Action mergedAction = entityManager.merge(action); - Target mergedTarget = mergedAction.getTarget(); - // check for a potential DOS attack - checkForToManyStatusEntries(action); - - switch (actionStatus.getStatus()) { - case ERROR: - mergedTarget = deploymentManagement.updateTargetInfo(mergedTarget, TargetUpdateStatus.ERROR, false); - handleErrorOnAction(mergedAction, mergedTarget); - break; - case FINISHED: - handleFinishedAndStoreInTargetStatus(mergedTarget, mergedAction); - break; - case CANCELED: - case WARNING: - case RUNNING: - deploymentManagement.updateTargetInfo(mergedTarget, TargetUpdateStatus.PENDING, false); - break; - default: - break; - } - - actionStatusRepository.save(actionStatus); - - LOG.debug("addUpdateActionStatus {} for target {} is finished.", action.getId(), mergedTarget.getId()); - - return actionRepository.save(mergedAction); - } - - private void handleErrorOnAction(final Action mergedAction, final Target mergedTarget) { - mergedAction.setActive(false); - mergedAction.setStatus(Status.ERROR); - mergedTarget.setAssignedDistributionSet(null); - targetManagement.updateTarget(mergedTarget); - } - - private void checkForToManyStatusEntries(final Action action) { - if (securityProperties.getDos().getMaxStatusEntriesPerAction() > 0) { - - final Long statusCount = actionStatusRepository.countByAction(action); - - if (statusCount >= securityProperties.getDos().getMaxStatusEntriesPerAction()) { - LOG_DOS.error( - "Potential denial of service (DOS) attack identfied. More status entries in the system than permitted ({})!", - securityProperties.getDos().getMaxStatusEntriesPerAction()); - throw new ToManyStatusEntriesException( - String.valueOf(securityProperties.getDos().getMaxStatusEntriesPerAction())); - } - } - } - - private void handleFinishedAndStoreInTargetStatus(final Target target, final Action action) { - action.setActive(false); - action.setStatus(Status.FINISHED); - final TargetInfo targetInfo = target.getTargetInfo(); - final DistributionSet ds = entityManager.merge(action.getDistributionSet()); - targetInfo.setInstalledDistributionSet(ds); - if (target.getAssignedDistributionSet() != null && targetInfo.getInstalledDistributionSet() != null && target - .getAssignedDistributionSet().getId().equals(targetInfo.getInstalledDistributionSet().getId())) { - targetInfo.setUpdateStatus(TargetUpdateStatus.IN_SYNC); - targetInfo.setInstallationDate(System.currentTimeMillis()); - } else { - targetInfo.setUpdateStatus(TargetUpdateStatus.PENDING); - targetInfo.setInstallationDate(System.currentTimeMillis()); - } - targetInfoRepository.save(targetInfo); - entityManager.detach(ds); - } - - /** - * Updates attributes of the controller. - * - * @param targetid - * to update - * @param data - * to insert - * - * @return updated {@link Target} - * - * @throws EntityNotFoundException - * if target that has to be updated could not be found - * @throws ToManyAttributeEntriesException - * if maximum - */ - @Modifying - @NotNull - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) - public Target updateControllerAttributes(@NotEmpty final String targetid, @NotNull final Map data) { - final Target target = targetRepository.findByControllerId(targetid); - - if (target == null) { - throw new EntityNotFoundException(targetid); - } - - target.getTargetInfo().getControllerAttributes().putAll(data); - - if (target.getTargetInfo().getControllerAttributes().size() > securityProperties.getDos() - .getMaxAttributeEntriesPerTarget()) { - LOG_DOS.info("Target tries to insert more than the allowed number of entries ({}). DOS attack anticipated!", - securityProperties.getDos().getMaxAttributeEntriesPerTarget()); - throw new ToManyAttributeEntriesException( - String.valueOf(securityProperties.getDos().getMaxAttributeEntriesPerTarget())); - } - - target.getTargetInfo().setLastTargetQuery(System.currentTimeMillis()); - target.getTargetInfo().setRequestControllerAttributes(false); - return targetRepository.save(target); - } - - /** - * Registers retrieved status for given {@link Target} and {@link Action} if - * it does not exist yet. - * - * @param action - * to the handle status for - * @param target - * to the handle status for - * @param message - * for the status - * @return the update action in case the status has been changed to - * {@link Status#RETRIEVED} - */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) - public Action registerRetrieved(final Action action, final String message) { - return handleRegisterRetrieved(action, message); - } - - /** - * Registers retrieved status for given {@link Target} and {@link Action} if - * it does not exist yet. - * - * @param action - * to the handle status for - * @param message - * for the status - * @return the updated action in case the status has been changed to - * {@link Status#RETRIEVED} - */ - public Action handleRegisterRetrieved(final Action action, final String message) { - // do a manual query with CriteriaBuilder to avoid unnecessary field - // queries and an extra - // count query made by spring-data when using pageable requests, we - // don't need an extra count - // query, we just want to check if the last action status is a retrieved - // or not. - final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); - final CriteriaQuery queryActionStatus = cb.createQuery(Object[].class); - final Root actionStatusRoot = queryActionStatus.from(ActionStatus.class); - final CriteriaQuery query = queryActionStatus - .multiselect(actionStatusRoot.get(ActionStatus_.id), actionStatusRoot.get(ActionStatus_.status)) - .where(cb.equal(actionStatusRoot.get(ActionStatus_.action), action)) - .orderBy(cb.desc(actionStatusRoot.get(ActionStatus_.id))); - final List resultList = entityManager.createQuery(query).setFirstResult(0).setMaxResults(1) - .getResultList(); - - // if the latest status is not in retrieve state then we add a retrieved - // state again, we want - // to document a deployment retrieved status and a cancel retrieved - // status, but multiple - // retrieves after the other we don't want to store to protect to - // overflood action status in - // case controller retrieves a action multiple times. - if (resultList.isEmpty() || resultList.get(0)[1] != Status.RETRIEVED) { - // document that the status has been retrieved - actionStatusRepository - .save(new ActionStatus(action, Status.RETRIEVED, System.currentTimeMillis(), message)); - - // don't change the action status itself in case the action is in - // canceling state otherwise - // we modify the action status and the controller won't get the - // cancel job anymore. - if (!action.isCancelingOrCanceled()) { - final Action actionMerge = entityManager.merge(action); - actionMerge.setStatus(Status.RETRIEVED); - return actionRepository.save(actionMerge); - } - } - return action; - } - - /** - * @param statusMessage - */ - @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - public void addActionStatusMessage(final ActionStatus statusMessage) { - actionStatusRepository.save(statusMessage); - } - - /** - * An direct access to the security token of an - * {@link Target#getSecurityToken()} without authorization. This is - * necessary to be able to access the security-token without any - * security-context information because the security-token is used for - * authentication. - * - * @param controllerId - * the ID of the controller to retrieve the security token for - * @return the security context of the target, in case no target exists for - * the given controllerId {@code null} is returned - */ - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - public String getSecurityTokenByControllerId(final String controllerId) { - final Target target = targetRepository.findByControllerId(controllerId); - return target != null ? target.getSecurityToken() : null; - } -} +} \ No newline at end of file diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java index 805b66d75..789383ce7 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java @@ -9,114 +9,38 @@ package org.eclipse.hawkbit.repository; import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; import java.util.stream.Collectors; -import javax.persistence.EntityManager; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Join; -import javax.persistence.criteria.JoinType; -import javax.persistence.criteria.ListJoin; -import javax.persistence.criteria.Root; import javax.validation.constraints.NotNull; -import org.eclipse.hawkbit.Constants; -import org.eclipse.hawkbit.eventbus.event.CancelTargetAssignmentEvent; -import org.eclipse.hawkbit.eventbus.event.TargetAssignDistributionSetEvent; -import org.eclipse.hawkbit.eventbus.event.TargetInfoUpdateEvent; -import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; -import org.eclipse.hawkbit.repository.exception.ForceQuitActionNotAllowedException; -import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException; +import org.eclipse.hawkbit.repository.jpa.TargetWithActionType; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.ActionType; -import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.ActionWithStatusCount; -import org.eclipse.hawkbit.repository.model.Action_; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetType; -import org.eclipse.hawkbit.repository.model.DistributionSet_; import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.RolloutGroup; -import org.eclipse.hawkbit.repository.model.Rollout_; -import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.Target; -import org.eclipse.hawkbit.repository.model.TargetInfo; -import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; -import org.eclipse.hawkbit.repository.specifications.TargetSpecifications; import org.hibernate.validator.constraints.NotEmpty; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.data.domain.AuditorAware; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.jpa.domain.Specification; -import org.springframework.data.jpa.repository.Modifying; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Isolation; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; - -import com.google.common.collect.Lists; -import com.google.common.eventbus.EventBus; /** - * Business service facade for managing all deployment related data and actions. - * + * A DeploymentManagement service provides operations for the deployment of + * {@link DistributionSet}s to {@link Target}s. * */ -@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) -@Validated -@Service -public class DeploymentManagement { - private static final Logger LOG = LoggerFactory.getLogger(DeploymentManagement.class); - - @Autowired - private EntityManager entityManager; - - @Autowired - private ActionRepository actionRepository; - - @Autowired - private DistributionSetRepository distributoinSetRepository; - - @Autowired - private SoftwareModuleRepository softwareModuleRepository; - - @Autowired - private TargetRepository targetRepository; - - @Autowired - private ActionStatusRepository actionStatusRepository; - - @Autowired - private TargetManagement targetManagement; - - @Autowired - private TargetInfoRepository targetInfoRepository; - - @Autowired - private AuditorAware auditorProvider; - - @Autowired - private EventBus eventBus; - - @Autowired - private AfterTransactionCommitExecutor afterCommit; +public interface DeploymentManagement { /** * method assigns the {@link DistributionSet} to all {@link Target}s. @@ -134,19 +58,81 @@ public class DeploymentManagement { * {@link SoftwareModuleType} are not assigned as define by the * {@link DistributionSetType}. * */ - @Transactional(isolation = Isolation.READ_COMMITTED) - @Modifying @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET) - @CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true) - public DistributionSetAssignmentResult assignDistributionSet(@NotNull final DistributionSet pset, - @NotEmpty final List targets) { - - return assignDistributionSetByTargetId(pset, - targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList()), - ActionType.FORCED, Action.NO_FORCE_TIME); + DistributionSetAssignmentResult assignDistributionSet(@NotNull DistributionSet pset, + @NotEmpty List targets); + /** + * method assigns the {@link DistributionSet} to all {@link Target}s by + * their IDs with a specific {@link ActionType} and {@code forcetime}. + * + * @param dsID + * the ID of the distribution set to assign + * @param actionType + * the type of the action to apply on the assignment + * @param forcedTimestamp + * the time when the action should be forced, only necessary for + * {@link ActionType#TIMEFORCED} + * @param targetIDs + * the IDs of the target to assign the distribution set + * @return the assignment result + * + * @throw IncompleteDistributionSetException if mandatory + * {@link SoftwareModuleType} are not assigned as define by the + * {@link DistributionSetType}. + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET) + // Exception squid:S2095: see + // https://jira.sonarsource.com/browse/SONARJAVA-1478 + @SuppressWarnings({ "squid:S2095" }) + default DistributionSetAssignmentResult assignDistributionSet(@NotNull final Long dsID, final ActionType actionType, + final long forcedTimestamp, @NotEmpty final String... targetIDs) { + return assignDistributionSet(dsID, Arrays.stream(targetIDs) + .map(t -> new TargetWithActionType(t, actionType, forcedTimestamp)).collect(Collectors.toList())); } + /** + * method assigns the {@link DistributionSet} to all {@link Target}s by + * their IDs with a specific {@link ActionType} and {@code forcetime}. + * + * @param dsID + * the ID of the distribution set to assign + * @param targets + * a list of all targets and their action type + * @return the assignment result + * + * @throw IncompleteDistributionSetException if mandatory + * {@link SoftwareModuleType} are not assigned as define by the + * {@link DistributionSetType}. + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET) + DistributionSetAssignmentResult assignDistributionSet(@NotNull Long dsID, + @NotEmpty List targets); + + // TODO document: why are rollouts in the signature ? can all the parameters + // be null or the list empty? + /** + * method assigns the {@link DistributionSet} to all {@link Target}s by + * their IDs with a specific {@link ActionType} and {@code forcetime}. + * + * @param dsID + * the ID of the distribution set to assign + * @param targets + * a list of all targets and their action type + * @param rollout + * the rollout for this assignment + * @param rolloutGroup + * the rollout group for this assignment + * @return the assignment result + * + * @throw IncompleteDistributionSetException if mandatory + * {@link SoftwareModuleType} are not assigned as define by the + * {@link DistributionSetType}. + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET) + DistributionSetAssignmentResult assignDistributionSet(@NotNull Long dsID, + @NotEmpty List targets, Rollout rollout, RolloutGroup rolloutGroup); + /** * method assigns the {@link DistributionSet} to all {@link Target}s by * their IDs. @@ -167,338 +153,12 @@ public class DeploymentManagement { * {@link SoftwareModuleType} are not assigned as define by the * {@link DistributionSetType}. */ - @Modifying - @Transactional(isolation = Isolation.READ_COMMITTED) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET) - @CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true) - public DistributionSetAssignmentResult assignDistributionSet(@NotNull final Long dsID, + default DistributionSetAssignmentResult assignDistributionSet(@NotNull final Long dsID, @NotEmpty final String... targetIDs) { return assignDistributionSet(dsID, ActionType.FORCED, Action.NO_FORCE_TIME, targetIDs); } - /** - * method assigns the {@link DistributionSet} to all {@link Target}s by - * their IDs with a specific {@link ActionType} and {@code forcetime}. - * - * @param dsID - * the ID of the distribution set to assign - * @param actionType - * the type of the action to apply on the assignment - * @param forcedTimestamp - * the time when the action should be forced, only necessary for - * {@link ActionType#TIMEFORCED} - * @param targetIDs - * the IDs of the target to assign the distribution set - * @return the assignment result - * - * @throw IncompleteDistributionSetException if mandatory - * {@link SoftwareModuleType} are not assigned as define by the - * {@link DistributionSetType}. - */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET) - @CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true) - // Exception squid:S2095: see - // https://jira.sonarsource.com/browse/SONARJAVA-1478 - @SuppressWarnings({ "squid:S2095" }) - public DistributionSetAssignmentResult assignDistributionSet(@NotNull final Long dsID, final ActionType actionType, - final long forcedTimestamp, @NotEmpty final String... targetIDs) { - return assignDistributionSet(dsID, Arrays.stream(targetIDs) - .map(t -> new TargetWithActionType(t, actionType, forcedTimestamp)).collect(Collectors.toList())); - } - - /** - * method assigns the {@link DistributionSet} to all {@link Target}s by - * their IDs with a specific {@link ActionType} and {@code forcetime}. - * - * @param dsID - * the ID of the distribution set to assign - * @param targets - * a list of all targets and their action type - * @return the assignment result - * - * @throw IncompleteDistributionSetException if mandatory - * {@link SoftwareModuleType} are not assigned as define by the - * {@link DistributionSetType}. - */ - @Modifying - @Transactional(isolation = Isolation.READ_COMMITTED) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET) - @CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true) - public DistributionSetAssignmentResult assignDistributionSet(@NotNull final Long dsID, - final List targets) { - final DistributionSet set = distributoinSetRepository.findOne(dsID); - if (set == null) { - throw new EntityNotFoundException( - String.format("no %s with id %d found", DistributionSet.class.getSimpleName(), dsID)); - } - - return assignDistributionSetToTargets(set, targets, null, null); - } - - /** - * method assigns the {@link DistributionSet} to all {@link Target}s by - * their IDs with a specific {@link ActionType} and {@code forcetime}. - * - * @param dsID - * the ID of the distribution set to assign - * @param targets - * a list of all targets and their action type - * @param rollout - * the rollout for this assignment - * @param rolloutGroup - * the rollout group for this assignment - * @return the assignment result - * - * @throw IncompleteDistributionSetException if mandatory - * {@link SoftwareModuleType} are not assigned as define by the - * {@link DistributionSetType}. - */ - @Modifying - @Transactional(isolation = Isolation.READ_COMMITTED) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET) - @CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true) - public DistributionSetAssignmentResult assignDistributionSet(@NotNull final Long dsID, - final List targets, final Rollout rollout, final RolloutGroup rolloutGroup) { - final DistributionSet set = distributoinSetRepository.findOne(dsID); - if (set == null) { - throw new EntityNotFoundException( - String.format("no %s with id %d found", DistributionSet.class.getSimpleName(), dsID)); - } - - return assignDistributionSetToTargets(set, targets, rollout, rolloutGroup); - } - - /** - * method assigns the {@link DistributionSet} to all {@link Target}s by - * their IDs with a specific {@link ActionType} and {@code forcetime}. - * - * @param dsID - * the ID of the distribution set to assign - * @param targets - * a list of all targets and their action type - * @param rollout - * the rollout for this assignment - * @param rolloutGroup - * the rollout group for this assignment - * @return the assignment result - * - * @throw IncompleteDistributionSetException if mandatory - * {@link SoftwareModuleType} are not assigned as define by the - * {@link DistributionSetType}. - */ - private DistributionSetAssignmentResult assignDistributionSetToTargets(@NotNull final DistributionSet set, - final List targetsWithActionType, final Rollout rollout, - final RolloutGroup rolloutGroup) { - - if (!set.isComplete()) { - throw new IncompleteDistributionSetException( - "Distribution set of type " + set.getType().getKey() + " is incomplete: " + set.getId()); - } - - final List controllerIDs = targetsWithActionType.stream().map(TargetWithActionType::getTargetId) - .collect(Collectors.toList()); - - LOG.debug("assignDistribution({}) to {} targets", set, controllerIDs.size()); - - final Map targetsWithActionMap = targetsWithActionType.stream() - .collect(Collectors.toMap(TargetWithActionType::getTargetId, Function.identity())); - - // split tIDs length into max entries in-statement because many database - // have constraint of max entries in in-statements e.g. Oracle with - // maximum 1000 elements, so we need to split the entries here and - // execute multiple statements we take the target only into account if - // the requested operation is no duplicate of a previous one - final List targets = Lists.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT).stream() - .map(ids -> targetRepository - .findAll(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(ids, set.getId()))) - .flatMap(t -> t.stream()).collect(Collectors.toList()); - - if (targets.isEmpty()) { - // detaching as it is not necessary to persist the set itself - entityManager.detach(set); - // return with nothing as all targets had the DS already assigned - return new DistributionSetAssignmentResult(Collections.emptyList(), 0, targetsWithActionType.size(), - Collections.emptyList(), targetManagement); - } - - final List> targetIds = Lists.partition( - targets.stream().map(Target::getId).collect(Collectors.toList()), Constants.MAX_ENTRIES_IN_STATEMENT); - - // override all active actions and set them into canceling state, we - // need to remember which one we have been switched to canceling state - // because for targets which we have changed to canceling we don't want - // to publish the new action update event. - final Set targetIdsCancellList = new HashSet<>(); - targetIds.forEach(ids -> targetIdsCancellList.addAll(overrideObsoleteUpdateActions(ids))); - - // cancel all scheduled actions which are in-active, these actions were - // not active before and the manual assignment which has been done - // cancels the - targetIds.forEach(tIds -> actionRepository.switchStatus(Status.CANCELED, tIds, false, Status.SCHEDULED)); - - // set assigned distribution set and TargetUpdateStatus - final String currentUser; - if (auditorProvider != null) { - currentUser = auditorProvider.getCurrentAuditor(); - } else { - currentUser = null; - } - - targetIds.forEach(tIds -> targetRepository.setAssignedDistributionSet(set, System.currentTimeMillis(), - currentUser, tIds)); - targetIds.forEach(tIds -> targetInfoRepository.setTargetUpdateStatus(TargetUpdateStatus.PENDING, tIds)); - final Map targetIdsToActions = actionRepository - .save(targets.stream().map(t -> createTargetAction(targetsWithActionMap, t, set, rollout, rolloutGroup)) - .collect(Collectors.toList())) - .stream().collect(Collectors.toMap(a -> a.getTarget().getControllerId(), Function.identity())); - - // create initial action status when action is created so we remember - // the initial running status because we will change the status - // of the action itself and with this action status we have a nicer - // action history. - targetIdsToActions.values().forEach(action -> { - final ActionStatus actionStatus = new ActionStatus(); - actionStatus.setAction(action); - actionStatus.setOccurredAt(action.getCreatedAt()); - actionStatus.setStatus(Status.RUNNING); - actionStatusRepository.save(actionStatus); - }); - - // flush to get action IDs - entityManager.flush(); - // collect updated target and actions IDs in order to return them - final DistributionSetAssignmentResult result = new DistributionSetAssignmentResult( - targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList()), targets.size(), - controllerIDs.size() - targets.size(), - targetIdsToActions.values().stream().map(Action::getId).collect(Collectors.toList()), targetManagement); - - LOG.debug("assignDistribution({}) finished {}", set, result); - - final List softwareModules = softwareModuleRepository.findByAssignedTo(set); - - // detaching as it is not necessary to persist the set itself - entityManager.detach(set); - - sendDistributionSetAssignmentEvent(targets, targetIdsCancellList, targetIdsToActions, softwareModules); - - return result; - } - - private void sendDistributionSetAssignmentEvent(final List targets, final Set targetIdsCancellList, - final Map targetIdsToActions, final List softwareModules) { - targets.stream().filter(t -> !!!targetIdsCancellList.contains(t.getId())) - .forEach(t -> assignDistributionSetEvent(t, targetIdsToActions.get(t.getControllerId()).getId(), - softwareModules)); - } - - private static Action createTargetAction(final Map targetsWithActionMap, - final Target target, final DistributionSet set, final Rollout rollout, final RolloutGroup rolloutGroup) { - final Action actionForTarget = new Action(); - final TargetWithActionType targetWithActionType = targetsWithActionMap.get(target.getControllerId()); - actionForTarget.setActionType(targetWithActionType.getActionType()); - actionForTarget.setForcedTime(targetWithActionType.getForceTime()); - actionForTarget.setActive(true); - actionForTarget.setStatus(Status.RUNNING); - actionForTarget.setTarget(target); - actionForTarget.setDistributionSet(set); - actionForTarget.setRollout(rollout); - actionForTarget.setRolloutGroup(rolloutGroup); - return actionForTarget; - } - - /** - * Sends the {@link TargetAssignDistributionSetEvent} for a specific target - * to the {@link EventBus}. - * - * @param target - * the Target which has been assigned to a distribution set - * @param actionId - * the action id of the assignment - * @param softwareModules - * the software modules which have been assigned - */ - private void assignDistributionSetEvent(final Target target, final Long actionId, - final List softwareModules) { - target.getTargetInfo().setUpdateStatus(TargetUpdateStatus.PENDING); - afterCommit.afterCommit(() -> { - eventBus.post(new TargetInfoUpdateEvent(target.getTargetInfo())); - eventBus.post(new TargetAssignDistributionSetEvent(target.getOptLockRevision(), target.getTenant(), - target.getControllerId(), actionId, softwareModules, target.getTargetInfo().getAddress(), - target.getSecurityToken())); - }); - } - - /** - * Removes {@link UpdateAction}s that are no longer necessary and sends - * cancellations to the controller. - * - * @param myTarget - * to override {@link UpdateAction}s - */ - private Set overrideObsoleteUpdateActions(final List targetsIds) { - - final Set cancelledTargetIds = new HashSet<>(); - - // Figure out if there are potential target/action combinations that - // need to be considered - // for cancelation - final List activeActions = actionRepository - .findByActiveAndTargetIdInAndActionStatusNotEqualToAndDistributionSetRequiredMigrationStep(targetsIds, - Action.Status.CANCELING); - activeActions.forEach(action -> { - action.setStatus(Status.CANCELING); - // document that the status has been retrieved - - actionStatusRepository.save(new ActionStatus(action, Status.CANCELING, System.currentTimeMillis(), - "manual cancelation requested")); - - cancelAssignDistributionSetEvent(action.getTarget(), action.getId()); - - cancelledTargetIds.add(action.getTarget().getId()); - }); - - actionRepository.save(activeActions); - - return cancelledTargetIds; - - } - - private DistributionSetAssignmentResult assignDistributionSetByTargetId(@NotNull final DistributionSet set, - @NotEmpty final List tIDs, final ActionType actionType, final long forcedTime) { - - return assignDistributionSetToTargets(set, tIDs.stream() - .map(t -> new TargetWithActionType(t, actionType, forcedTime)).collect(Collectors.toList()), null, - null); - - } - - /** - * Internal helper method used only inside service level. As a result is no - * additional security necessary. - * - * @param target - * to update - * @param status - * of the target - * @param setInstalledDate - * to set - * - * @return updated target - */ - Target updateTargetInfo(@NotNull final Target target, @NotNull final TargetUpdateStatus status, - final boolean setInstalledDate) { - final TargetInfo ts = target.getTargetInfo(); - ts.setUpdateStatus(status); - - if (setInstalledDate) { - ts.setInstallationDate(System.currentTimeMillis()); - } - targetInfoRepository.save(ts); - return entityManager.merge(target); - } - /** * Cancels given {@link Action} for given {@link Target}. The method will * immediately add a {@link ActionStatus.Status#CANCELED} status to the @@ -516,90 +176,32 @@ public class DeploymentManagement { * in case the given action is not active or is already a cancel * action */ - @Modifying - @Transactional(isolation = Isolation.READ_COMMITTED) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) - public Action cancelAction(@NotNull final Action action, @NotNull final Target target) { - LOG.debug("cancelAction({}, {})", action, target); - if (action.isCancelingOrCanceled()) { - throw new CancelActionNotAllowedException("Actions in canceling or canceled state cannot be canceled"); - } - final Action myAction = entityManager.merge(action); - - if (myAction.isActive()) { - LOG.debug("action ({}) was still active. Change to {}.", action, Status.CANCELING); - myAction.setStatus(Status.CANCELING); - - // document that the status has been retrieved - actionStatusRepository.save(new ActionStatus(myAction, Status.CANCELING, System.currentTimeMillis(), - "manual cancelation requested")); - final Action saveAction = actionRepository.save(myAction); - cancelAssignDistributionSetEvent(target, myAction.getId()); - - return saveAction; - } else { - throw new CancelActionNotAllowedException( - "Action [id: " + action.getId() + "] is not active and cannot be canceled"); - } - } + Action cancelAction(@NotNull Action action, @NotNull Target target); /** - * Sends the {@link CancelTargetAssignmentEvent} for a specific target to - * the {@link EventBus}. + * counts all actions associated to a specific target. * + * @param spec + * the specification to filter the count result * @param target - * the Target which has been assigned to a distribution set - * @param actionId - * the action id of the assignment + * the target associated to the actions to count + * @return the count value of found actions associated to the target */ - private void cancelAssignDistributionSetEvent(final Target target, final Long actionId) { - afterCommit.afterCommit(() -> eventBus.post(new CancelTargetAssignmentEvent(target.getOptLockRevision(), - target.getTenant(), target.getControllerId(), actionId, target.getTargetInfo().getAddress()))); - } + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + Long countActionsByTarget(@NotNull Specification spec, @NotNull Target target); /** - * Force cancels given {@link Action} for given {@link Target}. Force - * canceling means that the action is marked as canceled on the SP server - * and a cancel request is sent to the target. But however it's not tracked, - * if the targets handles the cancel request or not. + * counts all actions associated to a specific target. * - * @param action - * to be canceled * @param target - * for which the action needs cancellation - * - * @return generated {@link CancelAction} or null if not in - * {@link Target#getActiveActions()}. - * @throws CancelActionNotAllowedException - * in case the given action is not active + * the target associated to the actions to count + * @return the count value of found actions associated to the target */ - @Modifying - @Transactional(isolation = Isolation.READ_COMMITTED) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) - public Action forceQuitAction(@NotNull final Action action) { - final Action mergedAction = entityManager.merge(action); - - if (!mergedAction.isCancelingOrCanceled()) { - throw new ForceQuitActionNotAllowedException( - "Action [id: " + action.getId() + "] is not canceled yet and cannot be force quit"); - } - - if (!mergedAction.isActive()) { - throw new ForceQuitActionNotAllowedException( - "Action [id: " + action.getId() + "] is not active and cannot be force quit"); - } - - LOG.warn("action ({}) was still activ and has been force quite.", action); - - // document that the status has been retrieved - actionStatusRepository.save(new ActionStatus(mergedAction, Status.CANCELED, System.currentTimeMillis(), - "A force quit has been performed.")); - - successCancellation(mergedAction); - - return actionRepository.save(mergedAction); - } + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + Long countActionsByTarget(@NotNull Target target); + // TODO: validate parameters /** * Creates an action entry into the action repository. In case of existing * scheduled actions the scheduled actions gets canceled. A scheduled action @@ -614,96 +216,13 @@ public class DeploymentManagement { * @param forcedTime * the forcedTime of the action * @param rollout - * the rollout for this action + * the roll out for this action * @param rolloutGroup - * the rolloutgroup for this action + * the roll out group for this action */ - @Modifying - @Transactional(isolation = Isolation.READ_COMMITTED) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) - public void createScheduledAction(final List targets, final DistributionSet distributionSet, - final ActionType actionType, final long forcedTime, final Rollout rollout, - final RolloutGroup rolloutGroup) { - // cancel all current scheduled actions for this target. E.g. an action - // is already scheduled and a next action is created then cancel the - // current scheduled action to cancel. E.g. a new scheduled action is - // created. - final List targetIds = targets.stream().map(t -> t.getId()).collect(Collectors.toList()); - actionRepository.switchStatus(Action.Status.CANCELED, targetIds, false, Action.Status.SCHEDULED); - targets.forEach(target -> { - final Action action = new Action(); - action.setTarget(target); - action.setActive(false); - action.setDistributionSet(distributionSet); - action.setActionType(actionType); - action.setForcedTime(forcedTime); - action.setStatus(Status.SCHEDULED); - action.setRollout(rollout); - action.setRolloutGroup(rolloutGroup); - actionRepository.save(action); - }); - } - - /** - * Starting an action which is scheduled, e.g. in case of rollout a - * scheduled action must be started now. - * - * @param action - * the action to start now. - * @return the action which has been started - */ - @Modifying - @Transactional(isolation = Isolation.READ_COMMITTED) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR - + SpringEvalExpressions.IS_SYSTEM_CODE) - public Action startScheduledAction(@NotNull final Action action) { - - final Action mergedAction = entityManager.merge(action); - final Target mergedTarget = entityManager.merge(action.getTarget()); - - // check if we need to override running update actions - final Set overrideObsoleteUpdateActions = overrideObsoleteUpdateActions( - Collections.singletonList(action.getTarget().getId())); - - final boolean hasDistributionSetAlreadyAssigned = targetRepository - .count(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot( - Collections.singletonList(mergedTarget.getControllerId()), - action.getDistributionSet().getId())) == 0; - if (hasDistributionSetAlreadyAssigned) { - // the target has already the distribution set assigned, we don't - // need to start the scheduled action, just finished it. - mergedAction.setStatus(Status.FINISHED); - mergedAction.setActive(false); - return actionRepository.save(mergedAction); - } - - mergedAction.setActive(true); - mergedAction.setStatus(Status.RUNNING); - final Action savedAction = actionRepository.save(mergedAction); - - final ActionStatus actionStatus = new ActionStatus(); - actionStatus.setAction(action); - actionStatus.setOccurredAt(action.getCreatedAt()); - actionStatus.setStatus(Status.RUNNING); - actionStatusRepository.save(actionStatus); - - mergedTarget.setAssignedDistributionSet(action.getDistributionSet()); - final TargetInfo targetInfo = mergedTarget.getTargetInfo(); - targetInfo.setUpdateStatus(TargetUpdateStatus.PENDING); - targetRepository.save(mergedTarget); - targetInfoRepository.save(targetInfo); - - // in case we canceled an action before for this target, then don't fire - // assignment event - if (!overrideObsoleteUpdateActions.contains(savedAction.getId())) { - final List softwareModules = softwareModuleRepository - .findByAssignedTo(action.getDistributionSet()); - // send distribution set assignment event - - assignDistributionSetEvent(mergedAction.getTarget(), mergedAction.getId(), softwareModules); - } - return savedAction; - } + void createScheduledAction(List targets, DistributionSet distributionSet, ActionType actionType, + long forcedTime, Rollout rollout, RolloutGroup rolloutGroup); /** * Get the {@link Action} entity for given actionId. @@ -713,269 +232,19 @@ public class DeploymentManagement { * @return the corresponding {@link Action} */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - public Action findAction(@NotNull final Long actionId) { - return actionRepository.findOne(actionId); - } + Action findAction(@NotNull Long actionId); /** - * Get the {@link Action} entity for given actionId with all lazy attributes - * (i.e. distributionSet, target, target.assignedDs). + * Retrieves all actions for a specific rollout and in a specific status. * - * @param actionId - * to be id of the action - * @return the corresponding {@link Action} + * @param rollout + * the rollout the actions beglong to + * @param actionStatus + * the status of the actions + * @return the actions referring a specific rollout an in a specific status */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - public Action findActionWithDetails(@NotNull final Long actionId) { - return actionRepository.findById(actionId); - } - - /** - * Retrieves all {@link Action}s of a specific target. - * - * @param pageable - * pagination parameter - * @param target - * of which the actions have to be searched - * @return a paged list of actions associated with the given target - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - public Slice findActionsByTarget(final Pageable pageable, final Target target) { - return actionRepository.findByTarget(pageable, target); - } - - /** - * Retrieves all {@link Action}s of a specific target ordered by action ID. - * - * @param target - * the target associated with the actions - * @return a list of actions associated with the given target ordered by - * action ID - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - public List findActionsByTarget(final Target target) { - return actionRepository.findByTarget(target); - } - - /** - * Retrieves all {@link Action}s of a specific target ordered by action ID. - * - * @param target - * the target associated with the actions - * @return a list of actions associated with the given target ordered by - * action ID - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - public List findActionsWithStatusCountByTargetOrderByIdDesc(final Target target) { - final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); - final CriteriaQuery query = cb.createQuery(ActionWithStatusCount.class); - final Root actionRoot = query.from(Action.class); - final ListJoin actionStatusJoin = actionRoot.join(Action_.actionStatus, JoinType.LEFT); - final Join actionDsJoin = actionRoot.join(Action_.distributionSet); - final Join actionRolloutJoin = actionRoot.join(Action_.rollout, JoinType.LEFT); - - final CriteriaQuery multiselect = query.distinct(true).multiselect( - actionRoot.get(Action_.id), actionRoot.get(Action_.actionType), actionRoot.get(Action_.active), - actionRoot.get(Action_.forcedTime), actionRoot.get(Action_.status), actionRoot.get(Action_.createdAt), - actionRoot.get(Action_.lastModifiedAt), actionDsJoin.get(DistributionSet_.id), - actionDsJoin.get(DistributionSet_.name), actionDsJoin.get(DistributionSet_.version), - cb.count(actionStatusJoin), actionRolloutJoin.get(Rollout_.name)); - multiselect.where(cb.equal(actionRoot.get(Action_.target), target)); - multiselect.orderBy(cb.desc(actionRoot.get(Action_.id))); - multiselect.groupBy(actionRoot.get(Action_.id)); - return entityManager.createQuery(multiselect).getResultList(); - } - - /** - * Retrieves all {@link Action}s assigned to a specific {@link Target} and a - * given specification. - * - * @param specifiction - * the specification to narrow down the search - * @param target - * the target which must be assigned to the actions - * @param pageable - * the page request - * @return a slice of actions assigned to the specific target and the - * specification - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - public Slice findActionsByTarget(final Specification specifiction, final Target target, - final Pageable pageable) { - - return actionRepository.findAll((Specification) (root, query, cb) -> cb - .and(specifiction.toPredicate(root, query, cb), cb.equal(root.get(Action_.target), target)), pageable); - } - - /** - * Retrieves all {@link Action}s which are referring the given - * {@link Target}. - * - * @param foundTarget - * the target to find actions for - * @param pageable - * the pageable request to limit, sort the actions - * @return a slice of actions found for a specific target - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - public Slice findActionsByTarget(final Target foundTarget, final Pageable pageable) { - return actionRepository.findByTarget(pageable, foundTarget); - } - - /** - * Retrieves all active {@link Action}s of a specific target ordered by - * action ID. - * - * @param pageable - * the pagination parameter - * @param target - * the target associated with the actions - * @return a paged list of actions associated with the given target - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - public Page findActiveActionsByTarget(final Pageable pageable, final Target target) { - return actionRepository.findByActiveAndTarget(pageable, target, true); - } - - /** - * Retrieves all active {@link Action}s of a specific target ordered by - * action ID. - * - * @param target - * the target associated with the actions - * @return a list of actions associated with the given target - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - public List findActiveActionsByTarget(final Target target) { - return actionRepository.findByActiveAndTarget(target, true); - } - - /** - * Retrieves all inactive {@link Action}s of a specific target ordered by - * action ID. - * - * @param target - * the target associated with the actions - * @return a list of actions associated with the given target - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - public List findInActiveActionsByTarget(final Target target) { - return actionRepository.findByActiveAndTarget(target, false); - } - - /** - * Retrieves all inactive {@link Action}s of a specific target ordered by - * action ID. - * - * @param pageable - * the pagination parameter - * @param target - * the target associated with the actions - * @return a paged list of actions associated with the given target - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - public Page findInActiveActionsByTarget(final Pageable pageable, final Target target) { - return actionRepository.findByActiveAndTarget(pageable, target, false); - } - - /** - * counts all actions associated to a specific target. - * - * @param target - * the target associated to the actions to count - * @return the count value of found actions associated to the target - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - public Long countActionsByTarget(@NotNull final Target target) { - return actionRepository.countByTarget(target); - } - - /** - * counts all actions associated to a specific target. - * - * @param spec - * the specification to filter the count result - * @param target - * the target associated to the actions to count - * @return the count value of found actions associated to the target - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - public Long countActionsByTarget(@NotNull final Specification spec, @NotNull final Target target) { - return actionRepository.count((root, query, cb) -> cb.and(spec.toPredicate(root, query, cb), - cb.equal(root.get(Action_.target), target))); - } - - /** - * Updates a {@link TargetAction} and forces the {@link TargetAction} if - * it's not already forced. - * - * @param targetId - * the ID of the target - * @param actionId - * the ID of the action - * @return the updated or the found {@link TargetAction} - */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) - public Action forceTargetAction(final Long actionId) { - final Action action = actionRepository.findOne(actionId); - if (action != null && !action.isForced()) { - action.setActionType(ActionType.FORCED); - return actionRepository.save(action); - } - return action; - } - - /** - * Retrieves all the {@link ActionStatus} entries of the given - * {@link Action} and {@link Target}. - * - * @param pageReq - * pagination parameter - * @param action - * to be filtered on - * @param withMessages - * to true if {@link ActionStatus#getMessages()} - * need to be fetched. - * @return the corresponding {@link Page} of {@link ActionStatus} - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - public Page findActionStatusByAction(final Pageable pageReq, final Action action, - final boolean withMessages) { - if (withMessages) { - return actionStatusRepository.getByAction(pageReq, action); - } else { - return actionStatusRepository.findByAction(pageReq, action); - } - } - - /** - * This method is called, when cancellation has been successful. It sets the - * action to canceled, resets the meta data of the target and in case there - * is a new action this action is triggered. - * - * @param action - * the action which is set to canceled - */ - void successCancellation(final Action action) { - - // set action inactive - action.setActive(false); - action.setStatus(Status.CANCELED); - - final Target target = action.getTarget(); - final List nextActiveActions = actionRepository.findByTargetAndActiveOrderByIdAsc(target, true).stream() - .filter(a -> !a.getId().equals(action.getId())).collect(Collectors.toList()); - - if (nextActiveActions.isEmpty()) { - target.setAssignedDistributionSet(target.getTargetInfo().getInstalledDistributionSet()); - updateTargetInfo(target, TargetUpdateStatus.IN_SYNC, false); - } else { - target.setAssignedDistributionSet(nextActiveActions.get(0).getDistributionSet()); - } - targetManagement.updateTarget(target); - } + List findActionsByRolloutAndStatus(@NotNull Rollout rollout, @NotNull Action.Status actionStatus); /** * Retrieving all actions referring to a given rollout with a specific @@ -994,22 +263,191 @@ public class DeploymentManagement { */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR + SpringEvalExpressions.IS_SYSTEM_CODE) - public List findActionsByRolloutGroupParentAndStatus(final Rollout rollout, - final RolloutGroup rolloutGroupParent, final Action.Status actionStatus) { - return actionRepository.findByRolloutAndRolloutGroupParentAndStatus(rollout, rolloutGroupParent, actionStatus); - } + List findActionsByRolloutGroupParentAndStatus(@NotNull Rollout rollout, + @NotNull RolloutGroup rolloutGroupParent, @NotNull Action.Status actionStatus); /** - * Retrieves all actions for a specific rollout and in a specific status. + * Retrieves all {@link Action}s of a specific target. * - * @param rollout - * the rollout the actions beglong to - * @param actionStatus - * the status of the actions - * @return the actions referring a specific rollout an in a specific status + * @param pageable + * pagination parameter + * @param target + * of which the actions have to be searched + * @return a paged list of actions associated with the given target */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - public List findActionsByRolloutAndStatus(final Rollout rollout, final Action.Status actionStatus) { - return actionRepository.findByRolloutAndStatus(rollout, actionStatus); - } -} + Slice findActionsByTarget(@NotNull Pageable pageable, @NotNull Target target); + + /** + * Retrieves all {@link Action}s assigned to a specific {@link Target} and a + * given specification. + * + * @param specifiction + * the specification to narrow down the search + * @param target + * the target which must be assigned to the actions + * @param pageable + * the page request + * @return a slice of actions assigned to the specific target and the + * specification + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + Slice findActionsByTarget(@NotNull Specification specifiction, @NotNull Target target, + @NotNull Pageable pageable); + + /** + * Retrieves all {@link Action}s of a specific target ordered by action ID. + * + * @param target + * the target associated with the actions + * @return a list of actions associated with the given target ordered by + * action ID + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + List findActionsByTarget(@NotNull Target target); + + /** + * Retrieves all {@link Action}s which are referring the given + * {@link Target}. + * + * @param foundTarget + * the target to find actions for + * @param pageable + * the pageable request to limit, sort the actions + * @return a slice of actions found for a specific target + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + Slice findActionsByTarget(@NotNull Target foundTarget, @NotNull Pageable pageable); + + /** + * Retrieves all the {@link ActionStatus} entries of the given + * {@link Action} and {@link Target}. + * + * @param pageReq + * pagination parameter + * @param action + * to be filtered on + * @param withMessages + * to true if {@link ActionStatus#getMessages()} + * need to be fetched. + * @return the corresponding {@link Page} of {@link ActionStatus} + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + Page findActionStatusByAction(@NotNull Pageable pageReq, @NotNull Action action, + boolean withMessages); + + /** + * Retrieves all {@link Action}s of a specific target ordered by action ID. + * + * @param target + * the target associated with the actions + * @return a list of actions associated with the given target ordered by + * action ID + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + List findActionsWithStatusCountByTargetOrderByIdDesc(@NotNull Target target); + + /** + * Get the {@link Action} entity for given actionId with all lazy attributes + * (i.e. distributionSet, target, target.assignedDs). + * + * @param actionId + * to be id of the action + * @return the corresponding {@link Action} + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + Action findActionWithDetails(@NotNull Long actionId); + + /** + * Retrieves all active {@link Action}s of a specific target ordered by + * action ID. + * + * @param pageable + * the pagination parameter + * @param target + * the target associated with the actions + * @return a paged list of actions associated with the given target + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + Page findActiveActionsByTarget(@NotNull Pageable pageable, @NotNull Target target); + + /** + * Retrieves all active {@link Action}s of a specific target ordered by + * action ID. + * + * @param target + * the target associated with the actions + * @return a list of actions associated with the given target + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + List findActiveActionsByTarget(@NotNull Target target); + + /** + * Retrieves all inactive {@link Action}s of a specific target ordered by + * action ID. + * + * @param pageable + * the pagination parameter + * @param target + * the target associated with the actions + * @return a paged list of actions associated with the given target + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + Page findInActiveActionsByTarget(@NotNull Pageable pageable, @NotNull Target target); + + /** + * Retrieves all inactive {@link Action}s of a specific target ordered by + * action ID. + * + * @param target + * the target associated with the actions + * @return a list of actions associated with the given target + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) + List findInActiveActionsByTarget(@NotNull Target target); + + /** + * Force cancels given {@link Action} for given {@link Target}. Force + * canceling means that the action is marked as canceled on the SP server + * and a cancel request is sent to the target. But however it's not tracked, + * if the targets handles the cancel request or not. + * + * @param action + * to be canceled + * @param target + * for which the action needs cancellation + * + * @return generated {@link CancelAction} or null if not in + * {@link Target#getActiveActions()}. + * @throws CancelActionNotAllowedException + * in case the given action is not active + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) + Action forceQuitAction(@NotNull Action action); + + /** + * Updates a {@link TargetAction} and forces the {@link TargetAction} if + * it's not already forced. + * + * @param targetId + * the ID of the target + * @param actionId + * the ID of the action + * @return the updated or the found {@link TargetAction} + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) + Action forceTargetAction(@NotNull Long actionId); + + /** + * Starting an action which is scheduled, e.g. in case of roll out a + * scheduled action must be started now. + * + * @param action + * the action to start now. + * @return the action which has been started + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR + + SpringEvalExpressions.IS_SYSTEM_CODE) + Action startScheduledAction(@NotNull Action action); + +} \ No newline at end of file diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetAssignmentResult.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetAssignmentResult.java index 85e707652..c79fb669d 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetAssignmentResult.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetAssignmentResult.java @@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository; import java.util.Collections; import java.util.List; +import org.eclipse.hawkbit.repository.jpa.TargetManagement; import org.eclipse.hawkbit.repository.model.AssignmentResult; import org.eclipse.hawkbit.repository.model.Target; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java index db1ade60e..965ecd9f6 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java @@ -8,235 +8,163 @@ */ package org.eclipse.hawkbit.repository; -import static com.google.common.base.Preconditions.checkNotNull; - -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import javax.persistence.Entity; -import javax.persistence.EntityManager; import javax.validation.constraints.NotNull; -import org.eclipse.hawkbit.eventbus.event.DistributionSetTagAssigmentResultEvent; -import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; import org.eclipse.hawkbit.repository.DistributionSetFilter.DistributionSetFilterBuilder; import org.eclipse.hawkbit.repository.exception.DistributionSetCreationFailedMissingMandatoryModuleException; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; -import org.eclipse.hawkbit.repository.exception.EntityLockedException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetMetadata; -import org.eclipse.hawkbit.repository.model.DistributionSetMetadata_; import org.eclipse.hawkbit.repository.model.DistributionSetTag; import org.eclipse.hawkbit.repository.model.DistributionSetTagAssignmentResult; import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.DistributionSetTypeElement; -import org.eclipse.hawkbit.repository.model.DistributionSet_; import org.eclipse.hawkbit.repository.model.DsMetadataCompositeKey; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.Tag; import org.eclipse.hawkbit.repository.model.Target; -import org.eclipse.hawkbit.repository.specifications.DistributionSetSpecification; -import org.eclipse.hawkbit.repository.specifications.DistributionSetTypeSpecification; -import org.eclipse.hawkbit.repository.specifications.SpecificationsBuilder; import org.hibernate.validator.constraints.NotEmpty; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; -import org.springframework.data.jpa.repository.Modifying; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; -import com.google.common.base.Strings; -import com.google.common.eventbus.EventBus; +public interface DistributionSetManagement { -/** - * Business facade for managing the {@link DistributionSet}s. - * - */ -@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) -@Validated -@Service -public class DistributionSetManagement { - - @Autowired - private EntityManager entityManager; - - @Autowired - private DistributionSetRepository distributionSetRepository; - - @Autowired - private TagManagement tagManagement; - - @Autowired - private SystemManagement systemManagement; - - @Autowired - private DistributionSetTypeRepository distributionSetTypeRepository; - - @Autowired - private DistributionSetMetadataRepository distributionSetMetadataRepository; - - @Autowired - private ActionRepository actionRepository; - - @Autowired - private EventBus eventBus; - - @Autowired - private AfterTransactionCommitExecutor afterCommit; + // TODO rename/document the whole with details thing (document what the + // details are and maybe find a better name, e.g. with dependencies?) /** - * Find {@link DistributionSet} based on given ID including (lazy loaded) - * details, e.g. {@link DistributionSet#getAgentHub()}. - * - * Note: for performance reasons it is recommended to use - * {@link #findDistributionSetById(Long)} if details are not necessary. - * - * @param distid - * to look for. - * @return {@link DistributionSet} or null if it does not exist - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public DistributionSet findDistributionSetByIdWithDetails(@NotNull final Long distid) { - return distributionSetRepository.findOne(DistributionSetSpecification.byId(distid)); - } - - /** - * Find {@link DistributionSet} based on given ID without details, e.g. - * {@link DistributionSet#getAgentHub()}. - * - * @param distid - * to look for. - * @return {@link DistributionSet} or null if it does not exist - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public DistributionSet findDistributionSetById(@NotNull final Long distid) { - return distributionSetRepository.findOne(distid); - } - - /** - * {@link Entity} based method call for - * {@link #toggleTagAssignment(Collection, String)}. - * - * @param sets - * to toggle for - * @param tag - * to toggle - * @return {@link DistributionSetTagAssignmentResult} with all meta data of - * the assignment outcome. - */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @NotNull - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) - public DistributionSetTagAssignmentResult toggleTagAssignment(@NotEmpty final List sets, - @NotNull final DistributionSetTag tag) { - return toggleTagAssignment(sets.stream().map(ds -> ds.getId()).collect(Collectors.toList()), tag.getName()); - } - - /** - * Toggles {@link DistributionSetTag} assignment to given - * {@link DistributionSet}s by means that if some (or all) of the targets in - * the list have the {@link Tag} not yet assigned, they will be. If all of - * theme have the tag already assigned they will be removed instead. - * - * @param dsIds - * to toggle for - * @param tagName - * to toggle - * @return {@link DistributionSetTagAssignmentResult} with all meta data of - * the assignment outcome. - */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @NotNull - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) - public DistributionSetTagAssignmentResult toggleTagAssignment(@NotEmpty final Collection dsIds, - @NotNull final String tagName) { - - final Iterable sets = findDistributionSetListWithDetails(dsIds); - final DistributionSetTag myTag = tagManagement.findDistributionSetTag(tagName); - - DistributionSetTagAssignmentResult result; - final List toBeChangedDSs = new ArrayList<>(); - for (final DistributionSet set : sets) { - if (set.getTags().add(myTag)) { - toBeChangedDSs.add(set); - } - } - - // un-assignment case - if (toBeChangedDSs.isEmpty()) { - for (final DistributionSet set : sets) { - if (set.getTags().remove(myTag)) { - toBeChangedDSs.add(set); - } - } - result = new DistributionSetTagAssignmentResult(dsIds.size() - toBeChangedDSs.size(), 0, - toBeChangedDSs.size(), Collections.emptyList(), distributionSetRepository.save(toBeChangedDSs), - myTag); - } else { - result = new DistributionSetTagAssignmentResult(dsIds.size() - toBeChangedDSs.size(), toBeChangedDSs.size(), - 0, distributionSetRepository.save(toBeChangedDSs), Collections.emptyList(), myTag); - } - - final DistributionSetTagAssignmentResult resultAssignment = result; - afterCommit.afterCommit(() -> eventBus.post(new DistributionSetTagAssigmentResultEvent(resultAssignment))); - - // no reason to persist the tag - entityManager.detach(myTag); - return result; - } - - /** - * Retrieves {@link DistributionSet} List including details information, - * i.e. @link BaseSoftwareModule}s and {@link DistributionSetTag}s. - * - * @param distributionIdSet - * List of {@link DistributionSet} IDs to be found - * @return the found {@link DistributionSet}s - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public List findDistributionSetListWithDetails( - @NotEmpty final Collection distributionIdSet) { - return distributionSetRepository.findAll(DistributionSetSpecification.byIds(distributionIdSet)); - } - - /** - * Updates existing {@link DistributionSet}. + * Assigns {@link SoftwareModule} to existing {@link DistributionSet}. * * @param ds - * to update - * @return the saved {@link Entity}. - * @throws NullPointerException - * of {@link DistributionSet#getId()} is null - * @throw DataDependencyViolationException in case of illegal update + * to assign and update + * @param softwareModules + * to get assigned + * @return the updated {@link DistributionSet}. */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) - public DistributionSet updateDistributionSet(@NotNull final DistributionSet ds) { - checkNotNull(ds.getId()); - final DistributionSet persisted = findDistributionSetByIdWithDetails(ds.getId()); - checkDistributionSetSoftwareModulesIsAllowedToModify(ds, persisted.getModules()); - return distributionSetRepository.save(ds); + DistributionSet assignSoftwareModules(@NotNull DistributionSet ds, Set softwareModules); + + /** + * Assign a {@link DistributionSetTag} assignment to given + * {@link DistributionSet}s. + * + * @param dsIds + * to assign for + * @param tag + * to assign + * @return list of assigned ds + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) + List assignTag(@NotEmpty Collection dsIds, @NotNull DistributionSetTag tag); + + /** + * Count all {@link DistributionSet}s in the repository that are not marked + * as deleted. + * + * @return number of {@link DistributionSet}s + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + Long countDistributionSetsAll(); + + /** + * @return number of {@link DistributionSetType}s in the repository. + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + Long countDistributionSetTypesAll(); + + /** + * Creates a new {@link DistributionSet}. + * + * @param dSet + * {@link DistributionSet} to be created + * @return the new persisted {@link DistributionSet} + * + * @throws EntityAlreadyExistsException + * if a given entity already exists + * @throws DistributionSetCreationFailedMissingMandatoryModuleException + * is {@link DistributionSet} does not contain mandatory + * {@link SoftwareModule}s. + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) + DistributionSet createDistributionSet(@NotNull DistributionSet dSet); + + /** + * creates a list of distribution set meta data entries. + * + * @param metadata + * the meta data entries to create or update + * @return the updated or created distribution set meta data entries + * @throws EntityAlreadyExistsException + * in case one of the meta data entry already exists for the + * specific key + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) + List createDistributionSetMetadata(@NotEmpty Collection metadata); + + /** + * creates or updates a single distribution set meta data entry. + * + * @param metadata + * the meta data entry to create or update + * @return the updated or created distribution set meta data entry + * @throws EntityAlreadyExistsException + * in case the meta data entry already exists for the specific + * key + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) + DistributionSetMetadata createDistributionSetMetadata(@NotNull DistributionSetMetadata metadata); + + /** + * Creates multiple {@link DistributionSet}s. + * + * @param distributionSets + * to be created + * @return the new {@link DistributionSet}s + * @throws EntityAlreadyExistsException + * if a given entity already exists + * @throws DistributionSetCreationFailedMissingMandatoryModuleException + * is {@link DistributionSet} does not contain mandatory + * {@link SoftwareModule}s. + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) + List createDistributionSets(@NotNull Collection distributionSets); + + /** + * Creates new {@link DistributionSetType}. + * + * @param type + * to create + * @return created {@link Entity} + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) + DistributionSetType createDistributionSetType(@NotNull DistributionSetType type); + + /** + * Creates multiple {@link DistributionSetType}s. + * + * @param types + * to create + * @return created {@link Entity} + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + default List createDistributionSetTypes(@NotNull final Collection types) { + return types.stream().map(this::createDistributionSetType).collect(Collectors.toList()); } /** @@ -254,10 +182,9 @@ public class DistributionSetManagement { * @param set * to delete */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) - public void deleteDistributionSet(@NotNull final DistributionSet set) { + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + default void deleteDistributionSet(@NotNull final DistributionSet set) { deleteDistributionSet(set.getId()); } @@ -269,228 +196,133 @@ public class DistributionSetManagement { * @param distributionSetIDs * to be deleted */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) - public void deleteDistributionSet(@NotEmpty final Long... distributionSetIDs) { - final List toHardDelete = new ArrayList<>(); - - final List assigned = distributionSetRepository.findAssignedDistributionSetsById(distributionSetIDs); - - // soft delete assigned - if (!assigned.isEmpty()) { - distributionSetRepository.deleteDistributionSet(assigned.toArray(new Long[assigned.size()])); - } - - // mark the rest as hard delete - for (final Long setId : distributionSetIDs) { - if (!assigned.contains(setId)) { - toHardDelete.add(setId); - } - } - - // hard delete the rest if exixts - if (!toHardDelete.isEmpty()) { - // don't give the delete statement an empty list, JPA/Oracle cannot - // handle the empty list - distributionSetRepository.deleteByIdIn(toHardDelete); - } - } + void deleteDistributionSet(@NotEmpty Long... distributionSetIDs); /** - * Creates a new {@link DistributionSet}. + * deletes a distribution set meta data entry. * - * @param dSet - * {@link DistributionSet} to be created - * @return the new persisted {@link DistributionSet} - * - * @throws EntityAlreadyExistsException - * if a given entity already exists - * @throws DistributionSetCreationFailedMissingMandatoryModuleException - * is {@link DistributionSet} does not contain mandatory - * {@link SoftwareModule}s. + * @param id + * the ID of the distribution set meta data to delete */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) - public DistributionSet createDistributionSet(@NotNull final DistributionSet dSet) { - prepareDsSave(dSet); - if (dSet.getType() == null) { - dSet.setType(systemManagement.getTenantMetadata().getDefaultDsType()); - } - return distributionSetRepository.save(dSet); - } - - private void prepareDsSave(final DistributionSet dSet) { - if (dSet.getId() != null) { - throw new EntityAlreadyExistsException("Parameter seems to be an existing, already persisted entity"); - } - - if (distributionSetRepository.countByNameAndVersion(dSet.getName(), dSet.getVersion()) > 0) { - throw new EntityAlreadyExistsException("DistributionSet with that name and version already exists."); - } - - } - - /** - * Creates multiple {@link DistributionSet}s. - * - * @param distributionSets - * to be created - * @return the new {@link DistributionSet}s - * @throws EntityAlreadyExistsException - * if a given entity already exists - * @throws DistributionSetCreationFailedMissingMandatoryModuleException - * is {@link DistributionSet} does not contain mandatory - * {@link SoftwareModule}s. - */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) - public List createDistributionSets(@NotNull final Iterable distributionSets) { - for (final DistributionSet ds : distributionSets) { - prepareDsSave(ds); - if (ds.getType() == null) { - ds.setType(systemManagement.getTenantMetadata().getDefaultDsType()); - } - } - return distributionSetRepository.save(distributionSets); - } - - /** - * Assigns {@link SoftwareModule} to existing {@link DistributionSet}. - * - * @param ds - * to assign and update - * @param softwareModules - * to get assigned - * @return the updated {@link DistributionSet}. - */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) - public DistributionSet assignSoftwareModules(@NotNull final DistributionSet ds, - final Set softwareModules) { - checkDistributionSetSoftwareModulesIsAllowedToModify(ds, softwareModules); - for (final SoftwareModule softwareModule : softwareModules) { - ds.addModule(softwareModule); - } - return distributionSetRepository.save(ds); - } + void deleteDistributionSetMetadata(@NotNull DsMetadataCompositeKey id); /** - * Unassigns a {@link SoftwareModule} form an existing - * {@link DistributionSet}. + * Deletes or mark as delete in case the type is in use. * - * @param ds - * to get unassigned form - * @param softwareModule - * to get unassigned - * @return the updated {@link DistributionSet}. + * @param type + * to delete */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) - public DistributionSet unassignSoftwareModule(@NotNull final DistributionSet ds, - final SoftwareModule softwareModule) { - final Set softwareModules = new HashSet<>(); - softwareModules.add(softwareModule); - ds.removeModule(softwareModule); - checkDistributionSetSoftwareModulesIsAllowedToModify(ds, softwareModules); - return distributionSetRepository.save(ds); - } + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) + void deleteDistributionSetType(@NotNull DistributionSetType type); /** - * Updates existing {@link DistributionSetType}. However, keep in mind that - * is not possible to change the {@link DistributionSetTypeElement}s while - * the DS type is already in use. + * retrieves the distribution set for a given action. * - * @param dsType - * to update - * @return updated {@link Entity} - * - * @throws EntityReadOnlyException - * if use tries to change the {@link DistributionSetTypeElement} - * s while the DS type is already in use. + * @param action + * the action associated with the distribution set + * @return the distribution set which is associated with the action */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) - public DistributionSetType updateDistributionSetType(@NotNull final DistributionSetType dsType) { - checkNotNull(dsType.getId()); + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + DistributionSet findDistributionSetByAction(@NotNull Action action); - final DistributionSetType persisted = distributionSetTypeRepository.findOne(dsType.getId()); - - // throw exception if user tries to update a DS type that is already in - // use - if (!persisted.areModuleEntriesIdentical(dsType) && distributionSetRepository.countByType(persisted) > 0) { - throw new EntityReadOnlyException( - String.format("distribution set type %s set is already assigned to targets and cannot be changed", - dsType.getName())); - } - - return distributionSetTypeRepository.save(dsType); + /** + * Find {@link DistributionSet} based on given ID without details, e.g. + * {@link DistributionSet#getAgentHub()}. + * + * @param distid + * to look for. + * @return {@link DistributionSet} or null if it does not exist + */ + @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + default DistributionSet findDistributionSetById(@NotNull final Long distid) { + return findDistributionSetByIdWithDetails(distid); } /** - * Generic predicate based query for {@link DistributionSetType}. + * Find {@link DistributionSet} based on given ID including (lazy loaded) + * details, e.g. {@link DistributionSet#getAgentHub()}. * + * Note: for performance reasons it is recommended to use + * {@link #findDistributionSetById(Long)} if details are not necessary. + * + * @param distid + * to look for. + * @return {@link DistributionSet} or null if it does not exist + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + DistributionSet findDistributionSetByIdWithDetails(@NotNull Long distid); + + /** + * Find distribution set by name and version. + * + * @param distributionName + * name of {@link DistributionSet}; case insensitive + * @param version + * version of {@link DistributionSet} + * @return the page with the found {@link DistributionSet} + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + DistributionSet findDistributionSetByNameAndVersion(@NotEmpty String distributionName, @NotEmpty String version); + + /** + * Retrieves {@link DistributionSet} List for overview purposes (no + * {@link SoftwareModule}s and {@link DistributionSetTag}s). + * + * Please use {@link #findDistributionSetListWithDetails(Iterable)} if + * details are required. + * + * @param dist + * List of {@link DistributionSet} IDs to be found + * @return the found {@link DistributionSet}s + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + Iterable findDistributionSetList(Collection dist); + + /** + * Retrieves {@link DistributionSet} List including details information, + * i.e. @link BaseSoftwareModule}s and {@link DistributionSetTag}s. + * + * @param distributionIdSet + * List of {@link DistributionSet} IDs to be found + * @return the found {@link DistributionSet}s + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + List findDistributionSetListWithDetails(Collection distributionIdSet); + + /** + * finds all meta data by the given distribution set id. + * + * @param distributionSetId + * the distribution set id to retrieve the meta data from + * @param pageable + * the page request to page the result + * @return a paged result of all meta data entries for a given distribution + * set id + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + Page findDistributionSetMetadataByDistributionSetId(@NotNull Long distributionSetId, + @NotNull Pageable pageable); + + /** + * finds all meta data by the given distribution set id. + * + * @param distributionSetId + * the distribution set id to retrieve the meta data from * @param spec - * of the search + * the specification to filter the result * @param pageable - * parameter for paging - * - * @return the found {@link SoftwareModuleType}s + * the page request to page the result + * @return a paged result of all meta data entries for a given distribution + * set id */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public Page findDistributionSetTypesByPredicate( - @NotNull final Specification spec, @NotNull final Pageable pageable) { - return distributionSetTypeRepository.findAll(spec, pageable); - } - - /** - * @param pageable - * parameter - * @return all {@link DistributionSetType}s in the repository. - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public Page findDistributionSetTypesAll(@NotNull final Pageable pageable) { - return distributionSetTypeRepository.findByDeleted(pageable, false); - } - - /** - * retrieves {@link DistributionSet}s by filtering on the given parameters. - * - * @param pageable - * page parameter - * @param distributionSetFilter - * has details of filters to be applied. - * @return the page of found {@link DistributionSet} - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public Page findDistributionSetsByFilters(@NotNull final Pageable pageable, - final DistributionSetFilter distributionSetFilter) { - final List> specList = buildDistributionSetSpecifications(distributionSetFilter); - return findByCriteriaAPI(pageable, specList); - } - - /** - * - * @param distributionSetFilter - * had details of filters to be applied - * @return a single DistributionSet which is either installed or assigned to - * a specific target or {@code null}. - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - private DistributionSet findDistributionSetsByFiltersAndInstalledOrAssignedTarget( - final DistributionSetFilter distributionSetFilter) { - final List> specList = buildDistributionSetSpecifications(distributionSetFilter); - if (specList == null || specList.isEmpty()) { - return null; - } - return distributionSetRepository.findOne(SpecificationsBuilder.combineWithAnd(specList)); - } + Page findDistributionSetMetadataByDistributionSetId(@NotNull Long distributionSetId, + @NotNull Specification spec, @NotNull Pageable pageable); + // TODO discuss: use enum instead of the true,false,null switch ? /** * finds all {@link DistributionSet}s. * @@ -504,31 +336,16 @@ public class DistributionSetManagement { * @param complete * to true for returning only completed distribution * sets or false for only incomplete ones nor - * null to return both.. + * null to return both. * @param complete - * set to if false uncomplete DS should also be + * set to if false incomplete DS should also be * shown. * * * @return all found {@link DistributionSet}s */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public Page findDistributionSetsAll(@NotNull final Pageable pageReq, final Boolean deleted, - final Boolean complete) { - final List> specList = new ArrayList<>(); - - if (deleted != null) { - final Specification spec = DistributionSetSpecification.isDeleted(deleted); - specList.add(spec); - } - - if (complete != null) { - final Specification spec = DistributionSetSpecification.isCompleted(complete); - specList.add(spec); - } - - return findByCriteriaAPI(pageReq, specList); - } + Page findDistributionSetsAll(@NotNull Pageable pageReq, Boolean deleted, Boolean complete); /** * finds all {@link DistributionSet}s. @@ -545,18 +362,11 @@ public class DistributionSetManagement { * @return all found {@link DistributionSet}s */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public Page findDistributionSetsAll(@NotNull final Specification spec, - @NotNull final Pageable pageReq, final Boolean deleted) { - final List> specList = new ArrayList<>(); - if (deleted != null) { - specList.add(DistributionSetSpecification.isDeleted(deleted)); - } - specList.add(spec); - return findByCriteriaAPI(pageReq, specList); - } + Page findDistributionSetsAll(@NotNull Specification spec, + @NotNull Pageable pageReq, Boolean deleted); /** - * method retrieves all {@link DistributionSet}s from the repo in the + * method retrieves all {@link DistributionSet}s from the repository in the * following order: *

* 1) {@link DistributionSet}s which have the given {@link Target} as @@ -573,120 +383,25 @@ public class DistributionSetManagement { * @param distributionSetFilterBuilder * has details of filters to be applied * @param assignedOrInstalled - * the ID of the Target to be ordered by + * the controllerID of the Target to be ordered by * @return */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public Page findDistributionSetsAllOrderedByLinkTarget(@NotNull final Pageable pageable, - @NotNull final DistributionSetFilterBuilder distributionSetFilterBuilder, - @NotNull final String assignedOrInstalled) { - - final DistributionSetFilter filterWithInstalledTargets = distributionSetFilterBuilder - .setInstalledTargetId(assignedOrInstalled).setAssignedTargetId(null).build(); - final DistributionSet installedDS = findDistributionSetsByFiltersAndInstalledOrAssignedTarget( - filterWithInstalledTargets); - - final DistributionSetFilter filterWithAssignedTargets = distributionSetFilterBuilder.setInstalledTargetId(null) - .setAssignedTargetId(assignedOrInstalled).build(); - final DistributionSet assignedDS = findDistributionSetsByFiltersAndInstalledOrAssignedTarget( - filterWithAssignedTargets); - - final DistributionSetFilter dsFilterWithNoTargetLinked = distributionSetFilterBuilder.setInstalledTargetId(null) - .setAssignedTargetId(null).build(); - // first fine the distribution sets filtered by the given filter - // parameters - final Page findDistributionSetsByFilters = findDistributionSetsByFilters(pageable, - dsFilterWithNoTargetLinked); - - final List resultSet = new LinkedList<>(findDistributionSetsByFilters.getContent()); - int orderIndex = 0; - if (installedDS != null) { - final boolean remove = resultSet.remove(installedDS); - if (!remove) { - resultSet.remove(resultSet.size() - 1); - } - resultSet.add(orderIndex, installedDS); - orderIndex++; - } - if (assignedDS != null && !assignedDS.equals(installedDS)) { - final boolean remove = resultSet.remove(assignedDS); - if (!remove) { - resultSet.remove(resultSet.size() - 1); - } - resultSet.add(orderIndex, assignedDS); - } - - return new PageImpl<>(resultSet, pageable, findDistributionSetsByFilters.getTotalElements()); - } + Page findDistributionSetsAllOrderedByLinkTarget(@NotNull Pageable pageable, + @NotNull DistributionSetFilterBuilder distributionSetFilterBuilder, @NotEmpty String assignedOrInstalled); /** - * Find distribution set by name and version. + * retrieves {@link DistributionSet}s by filtering on the given parameters. * - * @param distributionName - * name of {@link DistributionSet}; case insensitive - * @param version - * version of {@link DistributionSet} - * @return the page with the found {@link DistributionSet} + * @param pageable + * page parameter + * @param distributionSetFilter + * has details of filters to be applied. + * @return the page of found {@link DistributionSet} */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public DistributionSet findDistributionSetByNameAndVersion(@NotEmpty final String distributionName, - @NotEmpty final String version) { - final Specification spec = DistributionSetSpecification - .equalsNameAndVersionIgnoreCase(distributionName, version); - return distributionSetRepository.findOne(spec); - - } - - /** - * Retrieves {@link DistributionSet} List for overview purposes (no - * {@link SoftwareModule}s and {@link DistributionSetTag}s). - * - * Please use {@link #findDistributionSetListWithDetails(Iterable)} if - * details are required. - * - * @param dist - * List of {@link DistributionSet} IDs to be found - * @return the found {@link DistributionSet}s - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public Iterable findDistributionSetList(@NotEmpty final Collection dist) { - return distributionSetRepository.findAll(dist); - } - - /** - * Count all {@link DistributionSet}s in the repository that are not marked - * as deleted. - * - * @return number of {@link DistributionSet}s - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public Long countDistributionSetsAll() { - - final List> specList = new ArrayList<>(); - - final Specification spec = DistributionSetSpecification.isDeleted(Boolean.FALSE); - specList.add(spec); - - return distributionSetRepository.count(SpecificationsBuilder.combineWithAnd(specList)); - } - - /** - * @return number of {@link DistributionSetType}s in the repository. - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public Long countDistributionSetTypesAll() { - return distributionSetTypeRepository.countByDeleted(false); - } - - /** - * @param name - * as {@link DistributionSetType#getName()} - * @return {@link DistributionSetType} if found or null if not - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public DistributionSetType findDistributionSetTypeByName(@NotNull final String name) { - return distributionSetTypeRepository.findOne(DistributionSetTypeSpecification.byName(name)); - } + Page findDistributionSetsByFilters(@NotNull Pageable pageable, + @NotNull DistributionSetFilter distributionSetFilter); /** * @param id @@ -694,186 +409,46 @@ public class DistributionSetManagement { * @return {@link DistributionSetType} if found or null if not */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public DistributionSetType findDistributionSetTypeById(@NotNull final Long id) { - return distributionSetTypeRepository.findOne(DistributionSetTypeSpecification.byId(id)); - } + DistributionSetType findDistributionSetTypeById(@NotNull Long id); /** * @param key * as {@link DistributionSetType#getKey()} * @return {@link DistributionSetType} if found or null if not */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public DistributionSetType findDistributionSetTypeByKey(@NotNull final String key) { - return distributionSetTypeRepository.findOne(DistributionSetTypeSpecification.byKey(key)); - } + DistributionSetType findDistributionSetTypeByKey(@NotNull String key); /** - * Creates new {@link DistributionSetType}. - * - * @param type - * to create - * @return created {@link Entity} + * @param name + * as {@link DistributionSetType#getName()} + * @return {@link DistributionSetType} if found or null if not */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) - public DistributionSetType createDistributionSetType(@NotNull final DistributionSetType type) { - if (type.getId() != null) { - throw new EntityAlreadyExistsException("Given type contains an Id!"); - } - - return distributionSetTypeRepository.save(type); - } + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + DistributionSetType findDistributionSetTypeByName(@NotEmpty String name); /** - * Deletes or markes as delete in case the type is in use. - * - * @param type - * to delete - */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) - public void deleteDistributionSetType(@NotNull final DistributionSetType type) { - - if (distributionSetRepository.countByType(type) > 0) { - final DistributionSetType toDelete = entityManager.merge(type); - toDelete.setDeleted(true); - distributionSetTypeRepository.save(toDelete); - } else { - distributionSetTypeRepository.delete(type.getId()); - } - } - - /** - * creates or updates a single distribution set meta data entry. - * - * @param metadata - * the meta data entry to create or update - * @return the updated or created distribution set meta data entry - * @throws EntityAlreadyExistsException - * in case the meta data entry already exists for the specific - * key - */ - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @Modifying - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) - public DistributionSetMetadata createDistributionSetMetadata(@NotNull final DistributionSetMetadata metadata) { - if (distributionSetMetadataRepository.exists(metadata.getId())) { - throwMetadataKeyAlreadyExists(metadata.getId().getKey()); - } - // merge base distribution set so optLockRevision gets updated and audit - // log written because - // modifying metadata is modifying the base distribution set itself for - // auditing purposes. - entityManager.merge(metadata.getDistributionSet()).setLastModifiedAt(0L); - return distributionSetMetadataRepository.save(metadata); - } - - /** - * creates a list of distribution set meta data entries. - * - * @param metadata - * the meta data entries to create or update - * @return the updated or created distribution set meta data entries - * @throws EntityAlreadyExistsException - * in case one of the meta data entry already exists for the - * specific key - */ - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @Modifying - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) - public List createDistributionSetMetadata( - @NotEmpty final Collection metadata) { - for (final DistributionSetMetadata distributionSetMetadata : metadata) { - checkAndThrowAlreadyIfDistributionSetMetadataExists(distributionSetMetadata.getId()); - } - metadata.forEach(m -> entityManager.merge(m.getDistributionSet()).setLastModifiedAt(-1L)); - return (List) distributionSetMetadataRepository.save(metadata); - } - - /** - * updates a distribution set meta data value if corresponding entry exists. - * - * @param metadata - * the meta data entry to be updated - * @return the updated meta data entry - * @throws EntityNotFoundException - * in case the meta data entry does not exists and cannot be - * updated - */ - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @Modifying - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) - public DistributionSetMetadata updateDistributionSetMetadata(@NotNull final DistributionSetMetadata metadata) { - // check if exists otherwise throw entity not found exception - findOne(metadata.getId()); - // touch it to update the lock revision because we are modifying the - // DS indirectly - entityManager.merge(metadata.getDistributionSet()).setLastModifiedAt(0L); - return distributionSetMetadataRepository.save(metadata); - } - - /** - * deletes a distribution set meta data entry. - * - * @param id - * the ID of the distribution set meta data to delete - */ - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @Modifying - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) - public void deleteDistributionSetMetadata(@NotNull final DsMetadataCompositeKey id) { - distributionSetMetadataRepository.delete(id); - } - - /** - * finds all meta data by the given distribution set id. - * - * @param distributionSetId - * the distribution set id to retrieve the meta data from * @param pageable - * the page request to page the result - * @return a paged result of all meta data entries for a given distribution - * set id + * parameter + * @return all {@link DistributionSetType}s in the repository. */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public Page findDistributionSetMetadataByDistributionSetId( - @NotNull final Long distributionSetId, @NotNull final Pageable pageable) { - - return distributionSetMetadataRepository.findAll( - (Specification) (root, query, cb) -> cb.equal( - root.get(DistributionSetMetadata_.distributionSet).get(DistributionSet_.id), distributionSetId), - pageable); - - } + Page findDistributionSetTypesAll(@NotNull Pageable pageable); /** - * finds all meta data by the given distribution set id. + * Generic predicate based query for {@link DistributionSetType}. * - * @param distributionSetId - * the distribution set id to retrieve the meta data from * @param spec - * the specification to filter the result + * of the search * @param pageable - * the page request to page the result - * @return a paged result of all meta data entries for a given distribution - * set id + * parameter for paging + * + * @return the found {@link SoftwareModuleType}s */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public Page findDistributionSetMetadataByDistributionSetId( - @NotNull final Long distributionSetId, @NotNull final Specification spec, - @NotNull final Pageable pageable) { - return distributionSetMetadataRepository - .findAll( - (Specification) (root, query, - cb) -> cb.and( - cb.equal(root.get(DistributionSetMetadata_.distributionSet) - .get(DistributionSet_.id), distributionSetId), - spec.toPredicate(root, query, cb)), - pageable); - } + Page findDistributionSetTypesAll(@NotNull Specification spec, + @NotNull Pageable pageable); /** * finds a single distribution set meta data by its id. @@ -886,188 +461,53 @@ public class DistributionSetManagement { * in case the meta data does not exists for the given key */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public DistributionSetMetadata findOne(@NotNull final DsMetadataCompositeKey id) { - final DistributionSetMetadata findOne = distributionSetMetadataRepository.findOne(id); - if (findOne == null) { - throw new EntityNotFoundException("Metadata with key '" + id.getKey() + "' does not exist"); - } - return findOne; - } + DistributionSetMetadata findOne(@NotNull DsMetadataCompositeKey id); /** - * retrieves the distribution set for a given action. - * - * @param action - * the action associated with the distribution set - * @return the distribution set which is associated with the action - */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public DistributionSet findDistributionSetByAction(@NotNull final Action action) { - return distributionSetRepository.findByAction(action); - } - - /** - * Creates multiple {@link DistributionSetType}s. - * - * @param types - * to create - * @return created {@link Entity} - */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) - public List createDistributionSetTypes(@NotNull final Collection types) { - return types.stream().map(this::createDistributionSetType).collect(Collectors.toList()); - } - - /** - * Checking Distribution Set is already using while assign Software module. + * Checks if a {@link DistributionSet} is currently in use by a target in + * the repository. * * @param distributionSet - * @param softwareModules + * to check + * + * @return true if in use */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) - public void checkDistributionSetAlreadyUse(final DistributionSet distributionSet) { - checkDistributionSetSoftwareModulesIsAllowedToModify(distributionSet); - } - - private List> buildDistributionSetSpecifications( - final DistributionSetFilter distributionSetFilter) { - final List> specList = new ArrayList<>(); - - Specification spec; - - if (null != distributionSetFilter.getIsComplete()) { - spec = DistributionSetSpecification.isCompleted(distributionSetFilter.getIsComplete()); - specList.add(spec); - } - - if (null != distributionSetFilter.getIsDeleted()) { - spec = DistributionSetSpecification.isDeleted(distributionSetFilter.getIsDeleted()); - specList.add(spec); - } - - if (distributionSetFilter.getType() != null) { - spec = DistributionSetSpecification.byType(distributionSetFilter.getType()); - specList.add(spec); - } - - if (!Strings.isNullOrEmpty(distributionSetFilter.getSearchText())) { - spec = DistributionSetSpecification.likeNameOrDescriptionOrVersion(distributionSetFilter.getSearchText()); - specList.add(spec); - } - - if (isDSWithNoTagSelected(distributionSetFilter) || isTagsSelected(distributionSetFilter)) { - spec = DistributionSetSpecification.hasTags(distributionSetFilter.getTagNames(), - distributionSetFilter.getSelectDSWithNoTag()); - specList.add(spec); - } - if (distributionSetFilter.getInstalledTargetId() != null) { - spec = DistributionSetSpecification.installedTarget(distributionSetFilter.getInstalledTargetId()); - specList.add(spec); - } - if (distributionSetFilter.getAssignedTargetId() != null) { - spec = DistributionSetSpecification.assignedTarget(distributionSetFilter.getAssignedTargetId()); - specList.add(spec); - } - return specList; - } - - private void checkDistributionSetSoftwareModulesIsAllowedToModify(final DistributionSet distributionSet, - final Set softwareModules) { - if (!new HashSet(distributionSet.getModules()).equals(softwareModules) - && actionRepository.countByDistributionSet(distributionSet) > 0) { - throw new EntityLockedException( - String.format("distribution set %s:%s is already assigned to targets and cannot be changed", - distributionSet.getName(), distributionSet.getVersion())); - } - } - - private void checkDistributionSetSoftwareModulesIsAllowedToModify(final DistributionSet distributionSet) { - if (actionRepository.countByDistributionSet(distributionSet) > 0) { - throw new EntityLockedException( - String.format("distribution set %s:%s is already assigned to targets and cannot be changed", - distributionSet.getName(), distributionSet.getVersion())); - } - } - - private static Boolean isDSWithNoTagSelected(final DistributionSetFilter distributionSetFilter) { - if (distributionSetFilter.getSelectDSWithNoTag() != null && distributionSetFilter.getSelectDSWithNoTag()) { - return true; - } - return false; - } - - private static Boolean isTagsSelected(final DistributionSetFilter distributionSetFilter) { - if (distributionSetFilter.getTagNames() != null && !distributionSetFilter.getTagNames().isEmpty()) { - return true; - } - return false; - } + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + boolean isDistributionSetInUse(@NotNull DistributionSet distributionSet); /** - * executes findAll with the given {@link DistributionSet} - * {@link Specification}s. + * {@link Entity} based method call for + * {@link #toggleTagAssignment(Collection, String)}. * - * @param pageable - * paging parameter - * @param specList - * list of @link {@link Specification} - * @return the page with the found {@link DistributionSet} + * @param sets + * to toggle for + * @param tag + * to toggle + * @return {@link DistributionSetTagAssignmentResult} with all meta data of + * the assignment outcome. */ - private Page findByCriteriaAPI(@NotNull final Pageable pageable, - final List> specList) { - - if (specList == null || specList.isEmpty()) { - return distributionSetRepository.findAll(pageable); - } - - return distributionSetRepository.findAll(SpecificationsBuilder.combineWithAnd(specList), pageable); - } - - private void checkAndThrowAlreadyIfDistributionSetMetadataExists(final DsMetadataCompositeKey metadataId) { - if (distributionSetMetadataRepository.exists(metadataId)) { - throw new EntityAlreadyExistsException( - "Metadata entry with key '" + metadataId.getKey() + "' already exists"); - } - } - - private static void throwMetadataKeyAlreadyExists(final String metadataKey) { - throw new EntityAlreadyExistsException("Metadata entry with key '" + metadataKey + "' already exists"); + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + default DistributionSetTagAssignmentResult toggleTagAssignment(@NotEmpty final Collection sets, + @NotNull final DistributionSetTag tag) { + return toggleTagAssignment(sets.stream().map(ds -> ds.getId()).collect(Collectors.toList()), tag.getName()); } /** - * Assign a {@link DistributionSetTag} assignment to given - * {@link DistributionSet}s. + * Toggles {@link DistributionSetTag} assignment to given + * {@link DistributionSet}s by means that if some (or all) of the targets in + * the list have the {@link Tag} not yet assigned, they will be. If all of + * theme have the tag already assigned they will be removed instead. * * @param dsIds - * to assign for - * @param tag - * to assign - * @return list of assigned ds + * to toggle for + * @param tagName + * to toggle + * @return {@link DistributionSetTagAssignmentResult} with all meta data of + * the assignment outcome. */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @NotNull - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) - public List assignTag(@NotEmpty final Collection dsIds, - @NotNull final DistributionSetTag tag) { - final List allDs = findDistributionSetListWithDetails(dsIds); - - allDs.forEach(ds -> ds.getTags().add(tag)); - final List save = distributionSetRepository.save(allDs); - - afterCommit.afterCommit(() -> { - - final DistributionSetTagAssignmentResult result = new DistributionSetTagAssignmentResult(0, save.size(), 0, - save, Collections.emptyList(), tag); - eventBus.post(new DistributionSetTagAssigmentResultEvent(result)); - }); - - return save; - } + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) + DistributionSetTagAssignmentResult toggleTagAssignment(@NotEmpty Collection dsIds, @NotNull String tagName); /** * Unassign all {@link DistributionSet} from a given @@ -1077,13 +517,21 @@ public class DistributionSetManagement { * to unassign all ds * @return list of unassigned ds */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @NotNull - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) - public List unAssignAllDistributionSetsByTag(@NotNull final DistributionSetTag tag) { - return unAssignTag(tag.getAssignedToDistributionSet(), tag); - } + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) + List unAssignAllDistributionSetsByTag(@NotNull DistributionSetTag tag); + + /** + * Unassigns a {@link SoftwareModule} form an existing + * {@link DistributionSet}. + * + * @param ds + * to get unassigned form + * @param softwareModule + * to get unassigned + * @return the updated {@link DistributionSet}. + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) + DistributionSet unassignSoftwareModule(@NotNull DistributionSet ds, @NotNull SoftwareModule softwareModule); /** * Unassign a {@link DistributionSetTag} assignment to given @@ -1095,18 +543,49 @@ public class DistributionSetManagement { * to unassign * @return the unassigned ds or if no ds is unassigned */ - @Modifying - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) - public DistributionSet unAssignTag(@NotNull final Long dsId, @NotNull final DistributionSetTag distributionSetTag) { - final List allDs = findDistributionSetListWithDetails(Arrays.asList(dsId)); - final List unAssignTag = unAssignTag(allDs, distributionSetTag); - return unAssignTag.isEmpty() ? null : unAssignTag.get(0); - } + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) + DistributionSet unAssignTag(@NotNull Long dsId, @NotNull DistributionSetTag distributionSetTag); - private List unAssignTag(final Collection distributionSets, - final DistributionSetTag tag) { - distributionSets.forEach(ds -> ds.getTags().remove(tag)); - return distributionSetRepository.save(distributionSets); - } -} + /** + * Updates existing {@link DistributionSet}. + * + * @param ds + * to update + * @return the saved {@link Entity}. + * @throws NullPointerException + * of {@link DistributionSet#getId()} is null + * @throw DataDependencyViolationException in case of illegal update + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) + DistributionSet updateDistributionSet(@NotNull DistributionSet ds); + + /** + * updates a distribution set meta data value if corresponding entry exists. + * + * @param metadata + * the meta data entry to be updated + * @return the updated meta data entry + * @throws EntityNotFoundException + * in case the meta data entry does not exists and cannot be + * updated + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) + DistributionSetMetadata updateDistributionSetMetadata(@NotNull DistributionSetMetadata metadata); + + /** + * Updates existing {@link DistributionSetType}. However, keep in mind that + * is not possible to change the {@link DistributionSetTypeElement}s while + * the DS type is already in use. + * + * @param dsType + * to update + * @return updated {@link Entity} + * + * @throws EntityReadOnlyException + * if use tries to change the {@link DistributionSetTypeElement} + * s while the DS type is already in use. + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) + DistributionSetType updateDistributionSetType(@NotNull DistributionSetType dsType); + +} \ No newline at end of file diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ReportManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ReportManagement.java index 326442283..40ee21d9b 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ReportManagement.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ReportManagement.java @@ -12,77 +12,24 @@ import java.io.Serializable; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.YearMonth; -import java.time.ZoneId; import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import javax.persistence.EntityManager; -import javax.persistence.Query; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Expression; -import javax.persistence.criteria.Join; -import javax.persistence.criteria.JoinType; -import javax.persistence.criteria.ListJoin; -import javax.persistence.criteria.Root; +import javax.validation.constraints.NotNull; +import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; import org.eclipse.hawkbit.report.model.DataReportSeries; -import org.eclipse.hawkbit.report.model.DataReportSeriesItem; import org.eclipse.hawkbit.report.model.InnerOuterDataReportSeries; import org.eclipse.hawkbit.report.model.ListReportSeries; import org.eclipse.hawkbit.report.model.SeriesTime; -import org.eclipse.hawkbit.repository.model.DistributionSet; -import org.eclipse.hawkbit.repository.model.DistributionSet_; -import org.eclipse.hawkbit.repository.model.Target; -import org.eclipse.hawkbit.repository.model.TargetInfo; -import org.eclipse.hawkbit.repository.model.TargetInfo_; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; -import org.eclipse.hawkbit.repository.model.Target_; -import org.eclipse.hawkbit.tenancy.TenantAware; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Isolation; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; +import org.springframework.security.access.prepost.PreAuthorize; /** - * Service layer for generating hawkBit reports. + * Service layer for generating hawkBit statistics and reports. * */ -@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) -@Validated -@Service -public class ReportManagement { - - @Value("${spring.jpa.database}") - private String databaseType; - - private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM"); - - private static final String H2_TARGET_CREATED_SQL_TEMPLATE = "SELECT TO_CHAR( DATEADD('second', target0_.created_at / 1000, DATE '1970-01-01'), '%s') AS col_0_0_, count(target0_.controller_id) AS col_1_0_ from sp_target target0_ WHERE TO_CHAR(DATEADD('second', target0_.created_at / 1000, DATE '1970-01-01'),'%s') BETWEEN TO_CHAR('%s', '%s') and TO_CHAR('%s', '%s') AND UPPER(target0_.tenant)=UPPER('%s') GROUP BY TO_CHAR(DATEADD('second', target0_.created_at / 1000, DATE '1970-01-01'), '%s')"; - - private static final String H2_CONTROLLER_FRRDBACK_SQL_TEMPLATE = "SELECT TO_CHAR(DATEADD('second', action_.created_at / 1000, DATE '1970-01-01'), '%s') AS col_0_0_, count(action_.id) AS col_1_0_ FROM sp_action action_ WHERE TO_CHAR(DATEADD('second', action_.created_at / 1000, DATE '1970-01-01'), '%s') BETWEEN TO_CHAR('%s', '%s') AND TO_CHAR('%s', '%s') AND UPPER(action_.tenant)=UPPER('%s') GROUP BY TO_CHAR(DATEADD('second', action_.created_at / 1000, DATE '1970-01-01'), '%s')"; - - private static final String MYSQL_TARGET_CREATED_SQL_TEMPLATE = "SELECT DATE_FORMAT(FROM_UNIXTIME(target0_.created_at / 1000), '%s') AS col_0_0_, COUNT(target0_.controller_id) AS col_1_0_ FROM sp_target target0_ WHERE DATE_FORMAT(FROM_UNIXTIME(target0_.created_at / 1000),'%s') BETWEEN DATE_FORMAT('%s', '%s') AND DATE_FORMAT('%s', '%s') AND UPPER(target0_.tenant)=UPPER('%s') GROUP BY DATE_FORMAT(FROM_UNIXTIME(target0_.created_at / 1000), '%s')"; - - private static final String MYSQL_CONTROLLER_FRRDBACK_SQL_TEMPLATE = "SELECT DATE_FORMAT(FROM_UNIXTIME(action_.created_at / 1000), '%s') AS col_0_0_, COUNT(action_.id) as col_1_0_ FROM sp_action action_ WHERE DATE_FORMAT(FROM_UNIXTIME(action_.created_at / 1000),'%s') BETWEEN DATE_FORMAT('%s', '%s') AND DATE_FORMAT('%s', '%s') AND UPPER(action_.tenant)=UPPER('%s') GROUP BY DATE_FORMAT(FROM_UNIXTIME(action_.created_at / 1000), '%s')"; - - private static final String MYSQL_DB_TYPE = "MYSQL"; - - private static final String H2_DB_TYPE = "H2"; - - @Autowired - private EntityManager entityManager; - - @Autowired - private TenantAware tenantAware; +public interface ReportManagement { /** * Generates a report of all targets of their current update status count. @@ -92,29 +39,8 @@ public class ReportManagement { * @return a data report series which contains the target count for each * target update status */ - @Cacheable("targetStatus") - public DataReportSeries targetStatus() { - - final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); - final CriteriaQuery query = cb.createQuery(Object[].class); - final Root targetRoot = query.from(Target.class); - final Join targetInfo = targetRoot.join(Target_.targetInfo); - final Expression countColumn = cb.count(targetInfo.get(TargetInfo_.targetId)); - final CriteriaQuery multiselect = query - .multiselect(targetInfo.get(TargetInfo_.updateStatus), countColumn) - .groupBy(targetInfo.get(TargetInfo_.updateStatus)) - .orderBy(cb.desc(targetInfo.get(TargetInfo_.updateStatus))); - - // | col1 | col2 | - // | U_STATUS | COUNT | - final List resultList = entityManager.createQuery(multiselect).getResultList(); - - final List> reportSeriesItems = resultList.stream() - .map(r -> new DataReportSeriesItem((TargetUpdateStatus) r[0], (Long) r[1])) - .collect(Collectors.toList()); - - return new DataReportSeries<>("Target Status Overview", reportSeriesItems); - } + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + DataReportSeries targetStatus(); /** * Generates a report of the top x distribution set assigned usage as a list @@ -132,29 +58,8 @@ public class ReportManagement { * set entries are summarized as "misc" * @return a list of inner and outer series of distribution set usage */ - @Cacheable("distributionUsageAssigned") - public List> distributionUsageAssigned(final int topXEntries) { - - // top X entries distribution usage - final CriteriaBuilder cbTopX = entityManager.getCriteriaBuilder(); - final CriteriaQuery queryTopX = cbTopX.createQuery(Object[].class); - final Root rootTopX = queryTopX.from(DistributionSet.class); - final ListJoin joinTopX = rootTopX.join(DistributionSet_.assignedToTargets, - JoinType.LEFT); - final Expression countColumn = cbTopX.count(joinTopX); - // top x usage query - final CriteriaQuery groupBy = queryTopX - .multiselect(rootTopX.get(DistributionSet_.name), rootTopX.get(DistributionSet_.version), countColumn) - .where(cbTopX.equal(rootTopX.get(DistributionSet_.deleted), false)) - .groupBy(rootTopX.get(DistributionSet_.name), rootTopX.get(DistributionSet_.version)) - .orderBy(cbTopX.desc(countColumn), cbTopX.asc(rootTopX.get(DistributionSet_.name))); - // | col1 | col2 | col3 | - // | NAME | VER | COUNT | - final List resultListTop = entityManager.createQuery(groupBy).getResultList(); - // end of top X entries distribution usage - - return mapDistirbutionUsageResultToDataReport(topXEntries, resultListTop); - } + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + List> distributionUsageAssigned(int topXEntries); /** * Generates a report of the top x distribution set installed usage as a @@ -173,28 +78,8 @@ public class ReportManagement { * set entries are summarized as "misc" * @return a list of inner and outer series of distribution set usage */ - @Cacheable("distributionUsageInstalled") - public List> distributionUsageInstalled(final int topXEntries) { - // top X entries distribution usage - final CriteriaBuilder cbTopX = entityManager.getCriteriaBuilder(); - final CriteriaQuery queryTopX = cbTopX.createQuery(Object[].class); - final Root rootTopX = queryTopX.from(DistributionSet.class); - final ListJoin joinTopX = rootTopX.join(DistributionSet_.installedAtTargets, - JoinType.LEFT); - final Expression countColumn = cbTopX.count(joinTopX); - // top x usage query - final CriteriaQuery groupBy = queryTopX - .multiselect(rootTopX.get(DistributionSet_.name), rootTopX.get(DistributionSet_.version), countColumn) - .where(cbTopX.equal(rootTopX.get(DistributionSet_.deleted), false)) - .groupBy(rootTopX.get(DistributionSet_.name), rootTopX.get(DistributionSet_.version)) - .orderBy(cbTopX.desc(countColumn), cbTopX.asc(rootTopX.get(DistributionSet_.name))); - // | col1 | col2 | col3 | - // | NAME | VER | COUNT | - final List resultListTop = entityManager.createQuery(groupBy).getResultList(); - // end of top X entries distribution usage - - return mapDistirbutionUsageResultToDataReport(topXEntries, resultListTop); - } + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + List> distributionUsageInstalled(int topXEntries); /** * Generates report for target created over period. @@ -208,55 +93,9 @@ public class ReportManagement { * @return DataReportSeries ListReportSeries list of target created * count */ - @Cacheable("targetsCreatedOverPeriod") - public DataReportSeries targetsCreatedOverPeriod(final DateType dateType, - final LocalDateTime from, final LocalDateTime to) { - final Query createNativeQuery = entityManager - .createNativeQuery(getTargetsCreatedQueryTemplate(dateType, from, to)); - final List resultList = createNativeQuery.getResultList(); - - final List> reportItems = resultList.stream() - .map(r -> new DataReportSeriesItem<>(dateType.format((String) r[0]), ((Number) r[1]).longValue())) - .collect(Collectors.toList()); - - return new DataReportSeries<>("CreatedTargets", reportItems); - } - - private String getTargetsCreatedQueryTemplate(final DateType dateType, final LocalDateTime from, - final LocalDateTime to) { - switch (databaseType) { - case H2_DB_TYPE: - return String.format(H2_TARGET_CREATED_SQL_TEMPLATE, dateTimeFormatToSqlFormat(dateType), - dateTimeFormatToSqlFormat(dateType), from.format(DATE_FORMAT), dateTimeFormatToSqlFormat(dateType), - to.format(DATE_FORMAT), dateTimeFormatToSqlFormat(dateType), tenantAware.getCurrentTenant(), - dateTimeFormatToSqlFormat(dateType)); - case MYSQL_DB_TYPE: - return String.format(MYSQL_TARGET_CREATED_SQL_TEMPLATE, dateTimeFormatToSqlFormat(dateType), - dateTimeFormatToSqlFormat(dateType), from.toString(), dateTimeFormatToSqlFormat(dateType), - to.toString(), dateTimeFormatToSqlFormat(dateType), tenantAware.getCurrentTenant(), - dateTimeFormatToSqlFormat(dateType)); - default: - return null; - } - } - - private String getFeedbackReceivedQueryTemplate(final DateType dateType, final LocalDateTime from, - final LocalDateTime to) { - switch (databaseType) { - case H2_DB_TYPE: - return String.format(H2_CONTROLLER_FRRDBACK_SQL_TEMPLATE, dateTimeFormatToSqlFormat(dateType), - dateTimeFormatToSqlFormat(dateType), from.format(DATE_FORMAT), dateTimeFormatToSqlFormat(dateType), - to.format(DATE_FORMAT), dateTimeFormatToSqlFormat(dateType), tenantAware.getCurrentTenant(), - dateTimeFormatToSqlFormat(dateType)); - case MYSQL_DB_TYPE: - return String.format(MYSQL_CONTROLLER_FRRDBACK_SQL_TEMPLATE, dateTimeFormatToSqlFormat(dateType), - dateTimeFormatToSqlFormat(dateType), from.toString(), dateTimeFormatToSqlFormat(dateType), - to.toString(), dateTimeFormatToSqlFormat(dateType), tenantAware.getCurrentTenant(), - dateTimeFormatToSqlFormat(dateType)); - default: - return null; - } - } + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + DataReportSeries targetsCreatedOverPeriod(@NotNull DateType dateType, + @NotNull LocalDateTime from, @NotNull LocalDateTime to); /** * Generates report for feedback over period. @@ -270,19 +109,9 @@ public class ReportManagement { * @return DataReportSeries ListReportSeries list of action status * count */ - @Cacheable("feedbackReceivedOverTime") - public DataReportSeries feedbackReceivedOverTime(final DateType dateType, - final LocalDateTime from, final LocalDateTime to) { - final Query createNativeQuery = entityManager - .createNativeQuery(getFeedbackReceivedQueryTemplate(dateType, from, to)); - final List resultList = createNativeQuery.getResultList(); - - final List> reportItems = resultList.stream() - .map(r -> new DataReportSeriesItem<>(dateType.format((String) r[0]), ((Number) r[1]).longValue())) - .collect(Collectors.toList()); - - return new DataReportSeries<>("FeedbackRecieved", reportItems); - } + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + DataReportSeries feedbackReceivedOverTime(@NotNull DateType dateType, + @NotNull LocalDateTime from, @NotNull LocalDateTime to); /** * Generates a report as a {@link ListReportSeries} targets polled based on @@ -297,212 +126,8 @@ public class ReportManagement { * than a year, never. * */ - @Cacheable("targetsLastPoll") - public DataReportSeries targetsLastPoll() { - - final LocalDateTime now = LocalDateTime.now(); - final LocalDateTime beforeHour = now.minusHours(1); - final LocalDateTime beforeDay = now.minusDays(1); - final LocalDateTime beforeWeek = now.minusWeeks(1); - final LocalDateTime beforeMonth = now.minusMonths(1); - final LocalDateTime beforeYear = now.minusYears(1); - - final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); - final List> resultList = new ArrayList<>(); - - // hours - resultList.add(new DataReportSeriesItem(SeriesTime.HOUR, - entityManager.createQuery(createCountSelectTargetsLastPoll(cb, beforeHour, now)).getSingleResult())); - // days - resultList.add(new DataReportSeriesItem(SeriesTime.DAY, entityManager - .createQuery(createCountSelectTargetsLastPoll(cb, beforeDay, beforeHour)).getSingleResult())); - // weeks - resultList.add(new DataReportSeriesItem(SeriesTime.WEEK, entityManager - .createQuery(createCountSelectTargetsLastPoll(cb, beforeWeek, beforeDay)).getSingleResult())); - // months - resultList.add(new DataReportSeriesItem(SeriesTime.MONTH, entityManager - .createQuery(createCountSelectTargetsLastPoll(cb, beforeMonth, beforeWeek)).getSingleResult())); - // years - resultList.add(new DataReportSeriesItem(SeriesTime.YEAR, entityManager - .createQuery(createCountSelectTargetsLastPoll(cb, beforeYear, beforeMonth)).getSingleResult())); - // years - resultList.add(new DataReportSeriesItem(SeriesTime.MORE_THAN_YEAR, - entityManager.createQuery(createCountSelectTargetsLastPoll(cb, null, beforeYear)).getSingleResult())); - // never - resultList.add(new DataReportSeriesItem(SeriesTime.NEVER, - entityManager.createQuery(createCountSelectTargetsLastPoll(cb, null, null)).getSingleResult())); - - return new DataReportSeries<>("TargetLastPoll", resultList); - } - - private CriteriaQuery createCountSelectTargetsLastPoll(final CriteriaBuilder cb, final LocalDateTime from, - final LocalDateTime to) { - - Long start = null; - Long end = null; - if (from != null) { - start = from.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); - } - if (to != null) { - end = to.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); - } - - // count select statement - final CriteriaQuery countSelect = cb.createQuery(Long.class); - final Root countSelectRoot = countSelect.from(Target.class); - final Join targetInfoJoin = countSelectRoot.join(Target_.targetInfo); - countSelect.select(cb.count(countSelectRoot)); - if (start != null && end != null) { - countSelect.where(cb.between(targetInfoJoin.get(TargetInfo_.lastTargetQuery), start, end)); - } else if (from == null && to != null) { - countSelect.where(cb.lessThanOrEqualTo(targetInfoJoin.get(TargetInfo_.lastTargetQuery), end)); - } else { - countSelect.where(cb.isNull(targetInfoJoin.get(TargetInfo_.lastTargetQuery))); - } - return countSelect; - } - - private List> mapDistirbutionUsageResultToDataReport(final int topXEntries, - final List resultListTop) { - final List> innerOuterReport = new ArrayList<>(); - final Map map = new LinkedHashMap<>(); - - int topXCounter = 0; - - for (final Object[] objects : resultListTop) { - - final boolean containsInnerOuter = map.containsKey(new DSName((String) objects[0])); - final String name = containsInnerOuter || topXCounter < topXEntries ? (String) objects[0] : null; - final DSName dsName = new DSName(name); - final String version = containsInnerOuter || topXCounter < topXEntries ? (String) objects[1] : null; - final Long count = (Long) objects[2]; - - InnerOuter innerouter = map.get(dsName); - if (innerouter == null) { - topXCounter++; - innerouter = new InnerOuter(dsName); - map.put(new DSName(name), innerouter); - } - innerouter.addOuter(new DSName(version), count); - } - - for (final InnerOuter inner : map.values()) { - final List> outerReportItems = new ArrayList<>(); - - if (inner.name.getName() != null) { - for (final InnerOuter outer : inner.outer) { - outerReportItems.add(outer.toItem()); - } - } else { - outerReportItems.add(new DataReportSeriesItem("misc", inner.count)); - } - - innerOuterReport.add(new InnerOuterDataReportSeries( - new DataReportSeries<>("DS-Name", Collections.singletonList(inner.toItem())), - new DataReportSeries<>("DS-Version", outerReportItems))); - } - - return innerOuterReport; - } - - private static final class InnerOuter { - final DSName name; - long count; - final List outer; - - private InnerOuter(final DSName idName) { - name = idName; - outer = new ArrayList<>(); - } - - private InnerOuter(final DSName idName, final long count) { - name = idName; - this.count = count; - outer = new ArrayList<>(); - } - - private void addOuter(final DSName idName, final long count) { - outer.add(new InnerOuter(idName, count)); - this.count += count; - } - - private DataReportSeriesItem toItem() { - return new DataReportSeriesItem<>(name.getName() != null ? name.getName() : "misc", count); - } - } - - /** - * Object contains the name and the id of an entity. - * - */ - private static final class DSName { - - private final String name; - - /** - * @param id - * the ID of an entity - * @param name - * the name of an entity - */ - private DSName(final String name) { - this.name = name; - } - - /** - * @return the name - */ - private String getName() { - return name; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (name == null ? 0 : name.hashCode()); - return result; - } - - @Override - public boolean equals(final Object obj) { // NOSONAR - as this is - // generated - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final DSName other = (DSName) obj; - if (name == null) { - if (other.name != null) { - return false; - } - } else if (!name.equals(other.name)) { - return false; - } - return true; - } - - @Override - public String toString() { - return "DSName [name=" + name + "]"; - } - } - - private String dateTimeFormatToSqlFormat(final DateType datatype) { - switch (databaseType) { - case H2_DB_TYPE: - return datatype.h2Format(); - case MYSQL_DB_TYPE: - return datatype.mySqlFormat(); - default: - return null; - } - } + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + DataReportSeries targetsLastPoll(); /** * Return DateTypes. @@ -580,4 +205,5 @@ public class ReportManagement { } } -} + +} \ No newline at end of file diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutGroupManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutGroupManagement.java index d204154d0..7c5503c19 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutGroupManagement.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutGroupManagement.java @@ -8,65 +8,20 @@ */ package org.eclipse.hawkbit.repository; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import javax.persistence.EntityManager; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Join; -import javax.persistence.criteria.JoinType; -import javax.persistence.criteria.ListJoin; -import javax.persistence.criteria.Root; import javax.validation.constraints.NotNull; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; -import org.eclipse.hawkbit.repository.model.Action; -import org.eclipse.hawkbit.repository.model.Action_; import org.eclipse.hawkbit.repository.model.Rollout; -import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus; import org.eclipse.hawkbit.repository.model.RolloutGroup; -import org.eclipse.hawkbit.repository.model.RolloutGroup_; -import org.eclipse.hawkbit.repository.model.RolloutTargetGroup; -import org.eclipse.hawkbit.repository.model.RolloutTargetGroup_; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetWithActionStatus; -import org.eclipse.hawkbit.repository.model.Target_; -import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus; -import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Isolation; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; -/** - * RolloutGroupManagement to control rollout groups. This service secures all - * the functionality based on the {@link PreAuthorize} annotation on methods. - */ -@Validated -@Service -@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) -public class RolloutGroupManagement { - - @Autowired - private RolloutGroupRepository rolloutGroupRepository; - - @Autowired - private ActionRepository actionRepository; - - @Autowired - private TargetRepository targetRepository; - - @Autowired - private EntityManager entityManager; +public interface RolloutGroupManagement { /** * Retrieves a single {@link RolloutGroup} by its ID. @@ -77,9 +32,7 @@ public class RolloutGroupManagement { * does not exists */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) - public RolloutGroup findRolloutGroupById(final Long rolloutGroupId) { - return rolloutGroupRepository.findOne(rolloutGroupId); - } + RolloutGroup findRolloutGroupById(@NotNull Long rolloutGroupId); /** * Retrieves a page of {@link RolloutGroup}s filtered by a given @@ -92,9 +45,7 @@ public class RolloutGroupManagement { * @return a page of found {@link RolloutGroup}s */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) - public Page findRolloutGroupsByRolloutId(final Long rolloutId, final Pageable page) { - return rolloutGroupRepository.findByRolloutId(rolloutId, page); - } + Page findRolloutGroupsByRolloutId(@NotNull Long rolloutId, @NotNull Pageable page); /** * Retrieves a page of {@link RolloutGroup}s filtered by a given @@ -110,12 +61,8 @@ public class RolloutGroupManagement { * @return a page of found {@link RolloutGroup}s */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) - public Page findRolloutGroupsByPredicate(final Rollout rollout, - final Specification specification, final Pageable page) { - return rolloutGroupRepository.findAll((root, query, criteriaBuilder) -> criteriaBuilder.and( - criteriaBuilder.equal(root.get(RolloutGroup_.rollout), rollout), - specification.toPredicate(root, query, criteriaBuilder)), page); - } + Page findRolloutGroupsAll(@NotNull Rollout rollout, + @NotNull Specification specification, @NotNull Pageable page); /** * Retrieves a page of {@link RolloutGroup}s filtered by a given @@ -128,21 +75,7 @@ public class RolloutGroupManagement { * @return a page of found {@link RolloutGroup}s */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) - public Page findAllRolloutGroupsWithDetailedStatus(final Long rolloutId, final Pageable page) { - final Page rolloutGroups = rolloutGroupRepository.findByRolloutId(rolloutId, page); - final List rolloutGroupIds = rolloutGroups.getContent().stream().map(rollout -> rollout.getId()) - .collect(Collectors.toList()); - final Map> allStatesForRollout = getStatusCountItemForRolloutGroup( - rolloutGroupIds); - - for (final RolloutGroup rolloutGroup : rolloutGroups) { - final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus( - allStatesForRollout.get(rolloutGroup.getId()), rolloutGroup.getTotalTargets()); - rolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus); - } - - return rolloutGroups; - } + Page findAllRolloutGroupsWithDetailedStatus(@NotNull Long rolloutId, @NotNull Pageable page); /** * Get count of targets in different status in rollout group. @@ -152,25 +85,9 @@ public class RolloutGroupManagement { * @return rolloutGroup with details of targets count for different statuses */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) - public RolloutGroup findRolloutGroupWithDetailedStatus(final Long rolloutGroupId) { - final RolloutGroup rolloutGroup = findRolloutGroupById(rolloutGroupId); - final List rolloutStatusCountItems = actionRepository - .getStatusCountByRolloutGroupId(rolloutGroupId); - - final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems, - rolloutGroup.getTotalTargets()); - rolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus); - return rolloutGroup; - - } - - private Map> getStatusCountItemForRolloutGroup( - final List rolloutGroupIds) { - final List resultList = actionRepository - .getStatusCountByRolloutGroupId(rolloutGroupIds); - return resultList.stream().collect(Collectors.groupingBy(TotalTargetCountActionStatus::getId)); - } + RolloutGroup findRolloutGroupWithDetailedStatus(@NotNull Long rolloutGroupId); + // TODO discuss: target read perm missing? /** * Get targets of specified rollout group. * @@ -184,15 +101,10 @@ public class RolloutGroupManagement { * @return Page list of targets of a rollout group */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) - public Page findRolloutGroupTargets(final RolloutGroup rolloutGroup, - final Specification specification, final Pageable page) { - return targetRepository.findAll((root, query, criteriaBuilder) -> { - final ListJoin rolloutTargetJoin = root.join(Target_.rolloutTargetGroup); - return criteriaBuilder.and(specification.toPredicate(root, query, criteriaBuilder), - criteriaBuilder.equal(rolloutTargetJoin.get(RolloutTargetGroup_.rolloutGroup), rolloutGroup)); - }, page); - } + Page findRolloutGroupTargets(@NotNull RolloutGroup rolloutGroup, + @NotNull Specification specification, @NotNull Pageable page); + // TODO discuss: target read perm missing? /** * Get targets of specified rollout group. * @@ -204,20 +116,9 @@ public class RolloutGroupManagement { * @return Page list of targets of a rollout group */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) - public Page findRolloutGroupTargets(@NotNull final RolloutGroup rolloutGroup, final Pageable page) { - if (isRolloutStatusReady(rolloutGroup)) { - // in case of status ready the action has not been created yet and - // the relation information between target and rollout-group is - // stored in the #TargetRolloutGroup. - return targetRepository.findByRolloutTargetGroupRolloutGroupId(rolloutGroup.getId(), page); - } - return targetRepository.findByActionsRolloutGroup(rolloutGroup, page); - } - - private static boolean isRolloutStatusReady(final RolloutGroup rolloutGroup) { - return rolloutGroup != null && RolloutStatus.READY.equals(rolloutGroup.getRollout().getStatus()); - } + Page findRolloutGroupTargets(@NotNull RolloutGroup rolloutGroup, @NotNull Pageable page); + // TODO discuss: target read perm missing? /** * * Find all targets with action status by rollout group id. The action @@ -233,31 +134,7 @@ public class RolloutGroupManagement { * @return {@link TargetWithActionStatus} target with action status */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) - public Page findAllTargetsWithActionStatus(final PageRequest pageRequest, - @NotNull final RolloutGroup rolloutGroup) { + Page findAllTargetsWithActionStatus(@NotNull PageRequest pageRequest, + @NotNull RolloutGroup rolloutGroup); - final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); - final CriteriaQuery query = cb.createQuery(Object[].class); - final CriteriaQuery countQuery = cb.createQuery(Long.class); - - final Root targetRoot = query.distinct(true).from(RolloutTargetGroup.class); - final Join targetJoin = targetRoot.join(RolloutTargetGroup_.target); - final ListJoin actionJoin = targetRoot.join(RolloutTargetGroup_.actions, - JoinType.LEFT); - - final Root countQueryFrom = countQuery.distinct(true).from(RolloutTargetGroup.class); - countQueryFrom.join(RolloutTargetGroup_.target); - countQueryFrom.join(RolloutTargetGroup_.actions, JoinType.LEFT); - countQuery.select(cb.count(countQueryFrom)) - .where(cb.equal(countQueryFrom.get(RolloutTargetGroup_.rolloutGroup), rolloutGroup)); - final Long totalCount = entityManager.createQuery(countQuery).getSingleResult(); - - final CriteriaQuery multiselect = query.multiselect(targetJoin, actionJoin.get(Action_.status)) - .where(cb.equal(targetRoot.get(RolloutTargetGroup_.rolloutGroup), rolloutGroup)); - final List targetWithActionStatus = entityManager.createQuery(multiselect) - .setFirstResult(pageRequest.getOffset()).setMaxResults(pageRequest.getPageSize()).getResultList() - .stream().map(o -> new TargetWithActionStatus((Target) o[0], (Action.Status) o[1])) - .collect(Collectors.toList()); - return new PageImpl<>(targetWithActionStatus, pageRequest, totalCount); - } -} +} \ No newline at end of file diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutManagement.java index 09decfc03..bf4924692 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutManagement.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutManagement.java @@ -8,131 +8,29 @@ */ package org.eclipse.hawkbit.repository; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executor; -import java.util.stream.Collectors; - -import javax.persistence.EntityManager; import javax.validation.constraints.NotNull; -import org.eclipse.hawkbit.cache.CacheWriteNotify; import org.eclipse.hawkbit.eventbus.event.RolloutGroupCreatedEvent; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException; -import org.eclipse.hawkbit.repository.model.Action; -import org.eclipse.hawkbit.repository.model.Action.ActionType; -import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus; import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupConditions; -import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorCondition; import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus; -import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition; -import org.eclipse.hawkbit.repository.model.RolloutTargetGroup; -import org.eclipse.hawkbit.repository.model.Rollout_; -import org.eclipse.hawkbit.repository.model.Target; -import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus; -import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus; -import org.eclipse.hawkbit.rollout.condition.RolloutGroupActionEvaluator; -import org.eclipse.hawkbit.rollout.condition.RolloutGroupConditionEvaluator; import org.hibernate.validator.constraints.NotEmpty; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.context.ApplicationContext; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; import org.springframework.data.jpa.domain.Specification; -import org.springframework.data.jpa.repository.Modifying; -import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.stereotype.Service; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.annotation.Isolation; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.support.DefaultTransactionDefinition; -import org.springframework.transaction.support.TransactionTemplate; -import org.springframework.util.Assert; -import org.springframework.validation.annotation.Validated; /** * RolloutManagement to control rollouts e.g. like creating, starting, resuming * and pausing rollouts. This service secures all the functionality based on the * {@link PreAuthorize} annotation on methods. */ -@Validated -@Service -@EnableScheduling -@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) -public class RolloutManagement { - private static final Logger LOGGER = LoggerFactory.getLogger(RolloutManagement.class); - - @Autowired - private EntityManager entityManager; - - @Autowired - private RolloutRepository rolloutRepository; - - @Autowired - private TargetManagement targetManagement; - - @Autowired - private TargetRepository targetRepository; - - @Autowired - private RolloutGroupRepository rolloutGroupRepository; - - @Autowired - private DeploymentManagement deploymentManagement; - - @Autowired - private RolloutTargetGroupRepository rolloutTargetGroupRepository; - - @Autowired - private ActionRepository actionRepository; - - @Autowired - private ApplicationContext context; - - @Autowired - private NoCountPagingRepository criteriaNoCountDao; - - @Autowired - private PlatformTransactionManager txManager; - - @Autowired - private CacheWriteNotify cacheWriteNotify; - - @Autowired - @Qualifier("asyncExecutor") - private Executor executor; - - /* - * set which stores the rollouts which are asynchronously creating. This is - * necessary to verify rollouts which maybe stuck during creationg e.g. - * because of database interruption, failures or even application crash. - * !This is not cluster aware! - */ - private static final Set creatingRollouts = ConcurrentHashMap.newKeySet(); - - /* - * set which stores the rollouts which are asynchronously starting. This is - * necessary to verify rollouts which maybe stuck during starting e.g. - * because of database interruption, failures or even application crash. - * !This is not cluster aware! - */ - private static final Set startingRollouts = ConcurrentHashMap.newKeySet(); +public interface RolloutManagement { /** * Retrieves all rollouts. @@ -142,9 +40,7 @@ public class RolloutManagement { * @return a page of found rollouts */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) - public Page findAll(final Pageable page) { - return rolloutRepository.findAll(page); - } + Page findAll(@NotNull Pageable page); /** * Retrieves all rollouts found by the given specification. @@ -156,12 +52,8 @@ public class RolloutManagement { * @return a page of found rollouts */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) - public Page findAllWithDetailedStatusByPredicate(final Specification specification, - final Pageable page) { - final Page findAll = rolloutRepository.findAll(specification, page); - setRolloutStatusDetails(findAll); - return findAll; - } + Page findAllWithDetailedStatusByPredicate(@NotNull Specification specification, + @NotNull Pageable page); /** * Retrieves a specific rollout by its ID. @@ -172,9 +64,7 @@ public class RolloutManagement { * not exists */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) - public Rollout findRolloutById(final Long rolloutId) { - return rolloutRepository.findOne(rolloutId); - } + Rollout findRolloutById(@NotNull Long rolloutId); /** * Persists a new rollout entity. The filter within the @@ -201,14 +91,8 @@ public class RolloutManagement { * @throws IllegalArgumentException * in case the given groupSize is zero or lower. */ - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @Modifying @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE) - public Rollout createRollout(final Rollout rollout, final int amountGroup, - final RolloutGroupConditions conditions) { - final Rollout savedRollout = createRollout(rollout, amountGroup); - return createRolloutGroups(amountGroup, conditions, savedRollout); - } + Rollout createRollout(@NotNull Rollout rollout, int amountGroup, @NotNull RolloutGroupConditions conditions); /** * Persists a new rollout entity. The filter within the @@ -244,116 +128,8 @@ public class RolloutManagement { * @return the created rollout entity in state * {@link RolloutStatus#CREATING} */ - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @Modifying @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE) - public Rollout createRolloutAsync(final Rollout rollout, final int amountGroup, - final RolloutGroupConditions conditions) { - final Rollout savedRollout = createRollout(rollout, amountGroup); - creatingRollouts.add(savedRollout.getName()); - // need to flush the entity manager here to get the ID of the rollout, - // because entity manager is set to FlushMode#Auto, entitymanager will - // flush the Target entity, due the indirect relationship to the Rollout - // entity without set an ID JPA is throwing a Invalid - // 'org.springframework.dao.InvalidDataAccessApiUsageException: During - // synchronization aect was found through a relationship that was not - // marked cascade PERSIST' - entityManager.flush(); - executor.execute(() -> { - try { - createRolloutGroupsInNewTransaction(amountGroup, conditions, savedRollout); - } finally { - creatingRollouts.remove(savedRollout.getName()); - } - }); - return savedRollout; - } - - private Rollout createRollout(final Rollout rollout, final int amountGroup) { - verifyRolloutGroupParameter(amountGroup); - final Long totalTargets = targetManagement.countTargetByTargetFilterQuery(rollout.getTargetFilterQuery()); - rollout.setTotalTargets(totalTargets.longValue()); - return rolloutRepository.save(rollout); - } - - private static void verifyRolloutGroupParameter(final int amountGroup) { - if (amountGroup <= 0) { - throw new IllegalArgumentException("the amountGroup must be greater than zero"); - } else if (amountGroup > 500) { - throw new IllegalArgumentException("the amountGroup must not be greater than 500"); - } - } - - private Rollout createRolloutGroupsInNewTransaction(final int amountOfGroups, - final RolloutGroupConditions conditions, final Rollout savedRollout) { - final DefaultTransactionDefinition def = new DefaultTransactionDefinition(); - def.setName("creatingRollout"); - def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); - return new TransactionTemplate(txManager, def) - .execute(status -> createRolloutGroups(amountOfGroups, conditions, savedRollout)); - } - - /** - * Method for creating rollout groups and calculating group sizes. Group - * sizes are calculated by dividing the total count of targets through the - * amount of given groups. In same cases this will lead to less rollout - * groups than given by client. - * - * @param amountOfGroups - * the amount of groups - * @param conditions - * the rollout group conditions - * @param savedRollout - * the rollout - * @return the rollout with created groups - */ - private Rollout createRolloutGroups(final int amountOfGroups, final RolloutGroupConditions conditions, - final Rollout savedRollout) { - int pageIndex = 0; - int groupIndex = 0; - final Long totalCount = savedRollout.getTotalTargets(); - final int groupSize = (int) Math.ceil((double) totalCount / (double) amountOfGroups); - // validate if the amount of groups that will be created are the amount - // of groups that the client what's to have created. - int amountGroupValidated = amountOfGroups; - final int amountGroupCreation = (int) (Math.ceil((double) totalCount / (double) groupSize)); - if (amountGroupCreation == (amountOfGroups - 1)) { - amountGroupValidated--; - } - RolloutGroup lastSavedGroup = null; - while (pageIndex < totalCount) { - groupIndex++; - final String nameAndDesc = "group-" + groupIndex; - final RolloutGroup group = new RolloutGroup(); - group.setName(nameAndDesc); - group.setDescription(nameAndDesc); - group.setRollout(savedRollout); - group.setParent(lastSavedGroup); - group.setSuccessCondition(conditions.getSuccessCondition()); - group.setSuccessConditionExp(conditions.getSuccessConditionExp()); - group.setErrorCondition(conditions.getErrorCondition()); - group.setErrorConditionExp(conditions.getErrorConditionExp()); - group.setErrorAction(conditions.getErrorAction()); - group.setErrorActionExp(conditions.getErrorActionExp()); - - final RolloutGroup savedGroup = rolloutGroupRepository.save(group); - - final Slice targetGroup = targetManagement.findTargetsAll(savedRollout.getTargetFilterQuery(), - new OffsetBasedPageRequest(pageIndex, groupSize, new Sort(Direction.ASC, "id"))); - savedGroup.setTotalTargets(targetGroup.getContent().size()); - - lastSavedGroup = savedGroup; - - targetGroup - .forEach(target -> rolloutTargetGroupRepository.save(new RolloutTargetGroup(savedGroup, target))); - cacheWriteNotify.rolloutGroupCreated(groupIndex, savedRollout.getId(), savedGroup.getId(), - amountGroupValidated, groupIndex); - pageIndex += groupSize; - } - - savedRollout.setStatus(RolloutStatus.READY); - return rolloutRepository.save(savedRollout); - } + Rollout createRolloutAsync(@NotNull Rollout rollout, int amountGroup, @NotNull RolloutGroupConditions conditions); /** * Starts a rollout which has been created. The rollout must be in @@ -373,15 +149,9 @@ public class RolloutManagement { * if given rollout is not in {@link RolloutStatus#READY}. Only * ready rollouts can be started. */ - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @Modifying @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR + SpringEvalExpressions.IS_SYSTEM_CODE) - public Rollout startRollout(final Rollout rollout) { - final Rollout mergedRollout = entityManager.merge(rollout); - checkIfRolloutCanStarted(rollout, mergedRollout); - return doStartRollout(mergedRollout); - } + Rollout startRollout(@NotNull Rollout rollout); /** * Starts a rollout asynchronously which has been created. The rollout must @@ -402,61 +172,9 @@ public class RolloutManagement { * if given rollout is not in {@link RolloutStatus#READY}. Only * ready rollouts can be started. */ - @Transactional - @Modifying @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR + SpringEvalExpressions.IS_SYSTEM_CODE) - public Rollout startRolloutAsync(final Rollout rollout) { - final Rollout mergedRollout = entityManager.merge(rollout); - checkIfRolloutCanStarted(rollout, mergedRollout); - mergedRollout.setStatus(RolloutStatus.STARTING); - final Rollout updatedRollout = rolloutRepository.save(mergedRollout); - startingRollouts.add(updatedRollout.getName()); - executor.execute(() -> { - try { - final DefaultTransactionDefinition def = new DefaultTransactionDefinition(); - def.setName("startingRollout"); - def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); - new TransactionTemplate(txManager, def).execute(status -> { - doStartRollout(updatedRollout); - return null; - }); - } finally { - startingRollouts.remove(updatedRollout.getName()); - } - }); - return updatedRollout; - - } - - private Rollout doStartRollout(final Rollout rollout) { - final DistributionSet distributionSet = rollout.getDistributionSet(); - final ActionType actionType = rollout.getActionType(); - final long forceTime = rollout.getForcedTime(); - final List rolloutGroups = rolloutGroupRepository.findByRolloutOrderByIdAsc(rollout); - for (int iGroup = 0; iGroup < rolloutGroups.size(); iGroup++) { - final RolloutGroup rolloutGroup = rolloutGroups.get(iGroup); - final List targetGroup = targetRepository.findByRolloutTargetGroupRolloutGroup(rolloutGroup); - // firstgroup can already be started - if (iGroup == 0) { - final List targetsWithActionType = targetGroup.stream() - .map(t -> new TargetWithActionType(t.getControllerId(), actionType, forceTime)) - .collect(Collectors.toList()); - deploymentManagement.assignDistributionSet(distributionSet.getId(), targetsWithActionType, rollout, - rolloutGroup); - rolloutGroup.setStatus(RolloutGroupStatus.RUNNING); - } else { - // create only not active actions with status scheduled so they - // can be activated later - deploymentManagement.createScheduledAction(targetGroup, distributionSet, actionType, forceTime, rollout, - rolloutGroup); - rolloutGroup.setStatus(RolloutGroupStatus.SCHEDULED); - } - rolloutGroupRepository.save(rolloutGroup); - } - rollout.setStatus(RolloutStatus.RUNNING); - return rolloutRepository.save(rollout); - } + Rollout startRolloutAsync(@NotNull Rollout rollout); /** * Pauses a rollout which is currently running. The Rollout switches @@ -477,24 +195,9 @@ public class RolloutManagement { * if given rollout is not in {@link RolloutStatus#RUNNING}. * Only running rollouts can be paused. */ - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @Modifying @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR + SpringEvalExpressions.IS_SYSTEM_CODE) - public void pauseRollout(final Rollout rollout) { - final Rollout mergedRollout = entityManager.merge(rollout); - if (mergedRollout.getStatus() != RolloutStatus.RUNNING) { - throw new RolloutIllegalStateException("Rollout can only be paused in state running but current state is " - + rollout.getStatus().name().toLowerCase()); - } - // setting the complete rollout only in paused state. This is sufficient - // due the currently running groups will be completed and new groups are - // not started until rollout goes back to running state again. The - // periodically check for running rollouts will skip rollouts in pause - // state. - mergedRollout.setStatus(RolloutStatus.PAUSED); - rolloutRepository.save(mergedRollout); - } + void pauseRollout(@NotNull Rollout rollout); /** * Resumes a paused rollout. The rollout switches back to @@ -507,19 +210,9 @@ public class RolloutManagement { * if given rollout is not in {@link RolloutStatus#PAUSED}. Only * paused rollouts can be resumed. */ - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @Modifying @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR + SpringEvalExpressions.IS_SYSTEM_CODE) - public void resumeRollout(final Rollout rollout) { - final Rollout mergedRollout = entityManager.merge(rollout); - if (!(RolloutStatus.PAUSED.equals(mergedRollout.getStatus()))) { - throw new RolloutIllegalStateException("Rollout can only be resumed in state paused but current state is " - + rollout.getStatus().name().toLowerCase()); - } - mergedRollout.setStatus(RolloutStatus.RUNNING); - rolloutRepository.save(mergedRollout); - } + void resumeRollout(@NotNull Rollout rollout); /** * Checking running rollouts. Rollouts which are checked updating the @@ -549,211 +242,30 @@ public class RolloutManagement { * this check. This check is only applied if the last check is * less than (lastcheck-delay). */ - @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED) - @Modifying @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR + SpringEvalExpressions.IS_SYSTEM_CODE) - public void checkRunningRollouts(final long delayBetweenChecks) { - verifyStuckedRollouts(); - final long lastCheck = System.currentTimeMillis(); - final int updated = rolloutRepository.updateLastCheck(lastCheck, delayBetweenChecks, RolloutStatus.RUNNING); - - if (updated == 0) { - // nothing to check, maybe another instance already checked in - // between - LOGGER.debug("No rolloutcheck necessary for current scheduled check {}, next check at {}", lastCheck, - lastCheck + delayBetweenChecks); - return; - } - - final List rolloutsToCheck = rolloutRepository.findByLastCheckAndStatus(lastCheck, - RolloutStatus.RUNNING); - LOGGER.info("Found {} running rollouts to check", rolloutsToCheck.size()); - - for (final Rollout rollout : rolloutsToCheck) { - LOGGER.debug("Checking rollout {}", rollout); - final List rolloutGroups = rolloutGroupRepository.findByRolloutAndStatus(rollout, - RolloutGroupStatus.RUNNING); - - if (rolloutGroups.isEmpty()) { - // no running rollouts, probably there was an error - // somewhere at the latest group. And the latest group has - // been switched from running into error state. So we need - // to find the latest group which - executeLatestRolloutGroup(rollout); - } else { - LOGGER.debug("Rollout {} has {} running groups", rollout.getId(), rolloutGroups.size()); - executeRolloutGroups(rollout, rolloutGroups); - } - - if (isRolloutComplete(rollout)) { - LOGGER.info("Rollout {} is finished, setting finished status", rollout); - rollout.setStatus(RolloutStatus.FINISHED); - rolloutRepository.save(rollout); - } - } - } + void checkRunningRollouts(long delayBetweenChecks); /** - * Verifies and handles stucked rollouts in asynchronous creation or - * starting state. If rollouts are created or started asynchronously it - * might be that they keep in state {@link RolloutStatus#CREATING} or - * {@link RolloutStatus#STARTING} due database or application interruption. - * In case this happens, set the rollout to error state. - */ - private void verifyStuckedRollouts() { - final List rolloutsInCreatingState = rolloutRepository.findByStatus(RolloutStatus.CREATING); - rolloutsInCreatingState.stream().filter(rollout -> !creatingRollouts.contains(rollout.getName())) - .forEach(rollout -> { - LOGGER.warn( - "Determined error during rollout creation of rollout {}, stucking in creating state, setting to status", - rollout, RolloutStatus.ERROR_CREATING); - rollout.setStatus(RolloutStatus.ERROR_CREATING); - rolloutRepository.save(rollout); - }); - - final List rolloutsInStartingState = rolloutRepository.findByStatus(RolloutStatus.STARTING); - rolloutsInStartingState.stream().filter(rollout -> !startingRollouts.contains(rollout.getName())) - .forEach(rollout -> { - LOGGER.warn( - "Determined error during rollout starting of rollout {}, stucking in starting state, setting to status", - rollout, RolloutStatus.ERROR_STARTING); - rollout.setStatus(RolloutStatus.ERROR_STARTING); - rolloutRepository.save(rollout); - }); - - } - - private void executeRolloutGroups(final Rollout rollout, final List rolloutGroups) { - for (final RolloutGroup rolloutGroup : rolloutGroups) { - // error state check, do we need to stop the whole - // rollout because of error? - final RolloutGroupErrorCondition errorCondition = rolloutGroup.getErrorCondition(); - final boolean isError = checkErrorState(rollout, rolloutGroup, errorCondition); - if (isError) { - LOGGER.info("Rollout {} {} has error, calling error action", rollout.getName(), rollout.getId()); - callErrorAction(rollout, rolloutGroup); - } else { - // not in error so check finished state, do we need to - // start the next group? - final RolloutGroupSuccessCondition finishedCondition = rolloutGroup.getSuccessCondition(); - checkFinishCondition(rollout, rolloutGroup, finishedCondition); - if (isRolloutGroupComplete(rollout, rolloutGroup)) { - rolloutGroup.setStatus(RolloutGroupStatus.FINISHED); - rolloutGroupRepository.save(rolloutGroup); - } - } - } - } - - private void executeLatestRolloutGroup(final Rollout rollout) { - final List latestRolloutGroup = rolloutGroupRepository - .findByRolloutAndStatusNotOrderByIdDesc(rollout, RolloutGroupStatus.SCHEDULED); - if (latestRolloutGroup.isEmpty()) { - return; - } - executeRolloutGroupSuccessAction(rollout, latestRolloutGroup.get(0)); - } - - private void callErrorAction(final Rollout rollout, final RolloutGroup rolloutGroup) { - try { - context.getBean(rolloutGroup.getErrorAction().getBeanName(), RolloutGroupActionEvaluator.class) - .eval(rollout, rolloutGroup, rolloutGroup.getErrorActionExp()); - } catch (final BeansException e) { - LOGGER.error("Something bad happend when accessing the error action bean {}", - rolloutGroup.getErrorAction().getBeanName(), e); - } - } - - private boolean isRolloutComplete(final Rollout rollout) { - final Long groupsActiveLeft = rolloutGroupRepository.countByRolloutAndStatusOrStatus(rollout, - RolloutGroupStatus.RUNNING, RolloutGroupStatus.SCHEDULED); - return groupsActiveLeft == 0; - } - - private boolean isRolloutGroupComplete(final Rollout rollout, final RolloutGroup rolloutGroup) { - final Long actionsLeftForRollout = actionRepository - .countByRolloutAndRolloutGroupAndStatusNotAndStatusNotAndStatusNot(rollout, rolloutGroup, - Action.Status.ERROR, Action.Status.FINISHED, Action.Status.CANCELED); - return actionsLeftForRollout == 0; - } - - private boolean checkErrorState(final Rollout rollout, final RolloutGroup rolloutGroup, - final RolloutGroupErrorCondition errorCondition) { - if (errorCondition == null) { - // there is no error condition, so return false, don't have error. - return false; - } - try { - return context.getBean(errorCondition.getBeanName(), RolloutGroupConditionEvaluator.class).eval(rollout, - rolloutGroup, rolloutGroup.getErrorConditionExp()); - } catch (final BeansException e) { - LOGGER.error("Something bad happend when accessing the error condition bean {}", - errorCondition.getBeanName(), e); - return false; - } - } - - private boolean checkFinishCondition(final Rollout rollout, final RolloutGroup rolloutGroup, - final RolloutGroupSuccessCondition finishCondition) { - LOGGER.trace("Checking finish condition {} on rolloutgroup {}", finishCondition, rolloutGroup); - try { - final boolean isFinished = context - .getBean(finishCondition.getBeanName(), RolloutGroupConditionEvaluator.class) - .eval(rollout, rolloutGroup, rolloutGroup.getSuccessConditionExp()); - if (isFinished) { - LOGGER.info("Rolloutgroup {} is finished, starting next group", rolloutGroup); - executeRolloutGroupSuccessAction(rollout, rolloutGroup); - } else { - LOGGER.debug("Rolloutgroup {} is still running", rolloutGroup); - } - return isFinished; - } catch (final BeansException e) { - LOGGER.error("Something bad happend when accessing the finish condition bean {}", - finishCondition.getBeanName(), e); - return false; - } - } - - private void executeRolloutGroupSuccessAction(final Rollout rollout, final RolloutGroup rolloutGroup) { - context.getBean(rolloutGroup.getSuccessAction().getBeanName(), RolloutGroupActionEvaluator.class).eval(rollout, - rolloutGroup, rolloutGroup.getSuccessActionExp()); - } - - /** - * Counts all {@link Target}s in the repository. + * Counts all {@link Rollout}s in the repository. * - * @return number of targets + * @return number of roll outs */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) - public Long countRolloutsAll() { - return rolloutRepository.count(); - } + Long countRolloutsAll(); /** - * Count rollouts by specified filter text. + * Count rollouts by given text in name or description. * * @param searchText * name or description * @return total count rollouts for specified filter text. */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) - public Long countRolloutsAllByFilters(final String searchText) { - return rolloutRepository.count(likeNameOrDescription(searchText)); - } - - private static Specification likeNameOrDescription(final String searchText) { - return (rolloutRoot, query, criteriaBuilder) -> { - final String searchTextToLower = searchText.toLowerCase(); - return criteriaBuilder.or( - criteriaBuilder.like(criteriaBuilder.lower(rolloutRoot.get(Rollout_.name)), searchTextToLower), - criteriaBuilder.like(criteriaBuilder.lower(rolloutRoot.get(Rollout_.description)), - searchTextToLower)); - }; - } + Long countRolloutsAllByFilters(@NotEmpty String searchText); /** - * * Retrieves a specific rollout by its ID. + * Finds rollouts by given text in name or description. * * @param pageable * the page request to sort and limit the result @@ -763,12 +275,7 @@ public class RolloutManagement { * not exists */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) - public Slice findRolloutByFilters(final Pageable pageable, @NotEmpty final String searchText) { - final Specification specs = likeNameOrDescription(searchText); - final Slice findAll = criteriaNoCountDao.findAll(specs, pageable, Rollout.class); - setRolloutStatusDetails(findAll); - return findAll; - } + Slice findRolloutByFilters(@NotNull Pageable pageable, @NotEmpty String searchText); /** * Retrieves a specific rollout by its name. @@ -779,9 +286,7 @@ public class RolloutManagement { * does not exists */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) - public Rollout findRolloutByName(final String rolloutName) { - return rolloutRepository.findByName(rolloutName); - } + Rollout findRolloutByName(@NotNull String rolloutName); /** * Update rollout details. @@ -791,14 +296,8 @@ public class RolloutManagement { * * @return Rollout updated rollout */ - @NotNull - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @Modifying @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE) - public Rollout updateRollout(@NotNull final Rollout rollout) { - Assert.notNull(rollout.getId()); - return rolloutRepository.save(rollout); - } + Rollout updateRollout(@NotNull Rollout rollout); /** * Get count of targets in different status in rollout. @@ -810,12 +309,7 @@ public class RolloutManagement { * */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) - public Page findAllRolloutsWithDetailedStatus(final Pageable page) { - final Page rollouts = findAll(page); - setRolloutStatusDetails(rollouts); - return rollouts; - - } + Page findAllRolloutsWithDetailedStatus(@NotNull Pageable page); /** * Get count of targets in different status in rollout. @@ -826,40 +320,7 @@ public class RolloutManagement { * */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) - public Rollout findRolloutWithDetailedStatus(final Long rolloutId) { - final Rollout rollout = findRolloutById(rolloutId); - final List rolloutStatusCountItems = actionRepository - .getStatusCountByRolloutId(rolloutId); - final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems, - rollout.getTotalTargets()); - rollout.setTotalTargetCountStatus(totalTargetCountStatus); - return rollout; - } - - private Map> getStatusCountItemForRollout(final List rolloutIds) { - final List resultList = actionRepository.getStatusCountByRolloutId(rolloutIds); - return resultList.stream().collect(Collectors.groupingBy(TotalTargetCountActionStatus::getId)); - } - - private void setRolloutStatusDetails(final Slice rollouts) { - final List rolloutIds = rollouts.getContent().stream().map(rollout -> rollout.getId()) - .collect(Collectors.toList()); - final Map> allStatesForRollout = getStatusCountItemForRollout( - rolloutIds); - - for (final Rollout rollout : rollouts) { - final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus( - allStatesForRollout.get(rollout.getId()), rollout.getTotalTargets()); - rollout.setTotalTargetCountStatus(totalTargetCountStatus); - } - } - - private static void checkIfRolloutCanStarted(final Rollout rollout, final Rollout mergedRollout) { - if (!(RolloutStatus.READY.equals(mergedRollout.getStatus()))) { - throw new RolloutIllegalStateException("Rollout can only be started in state ready but current state is " - + rollout.getStatus().name().toLowerCase()); - } - } + Rollout findRolloutWithDetailedStatus(@NotNull Long rolloutId); /*** * Get finished percentage details for a specified group which is in running @@ -871,16 +332,7 @@ public class RolloutManagement { * the ID of the {@link RolloutGroup} * @return percentage finished */ - public float getFinishedPercentForRunningGroup(final Long rolloutId, final RolloutGroup rolloutGroup) { - final Long totalGroup = rolloutGroup.getTotalTargets(); - final Long finished = actionRepository.countByRolloutIdAndRolloutGroupIdAndStatus(rolloutId, - rolloutGroup.getId(), Action.Status.FINISHED); - if (totalGroup == 0) { - // in case e.g. targets has been deleted we don't have any actions - // left for this group, so the group is finished - return 100; - } - // calculate threshold - return ((float) finished / (float) totalGroup) * 100; - } -} + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) + float getFinishedPercentForRunningGroup(@NotNull Long rolloutId, @NotNull RolloutGroup rolloutGroup); + +} \ No newline at end of file diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutScheduler.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutScheduler.java index 6671ad896..03ece28ee 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutScheduler.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutScheduler.java @@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository; import java.util.List; +import org.eclipse.hawkbit.repository.jpa.SystemManagement; import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.tenancy.TenantAware; import org.slf4j.Logger; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TenantConfigurationManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TenantConfigurationManagement.java index c292b39e3..10a88b232 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TenantConfigurationManagement.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TenantConfigurationManagement.java @@ -1,11 +1,3 @@ -/** - * 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; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; @@ -13,39 +5,11 @@ import org.eclipse.hawkbit.repository.model.TenantConfiguration; import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationValidatorException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.context.ApplicationContext; -import org.springframework.context.EnvironmentAware; import org.springframework.core.convert.ConversionFailedException; -import org.springframework.core.convert.support.ConfigurableConversionService; -import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.env.Environment; -import org.springframework.data.jpa.repository.Modifying; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Isolation; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; -/** - * Central tenant configuration management operations of the SP server. - */ -@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) -@Validated -@Service -public class TenantConfigurationManagement implements EnvironmentAware { - - @Autowired - private TenantConfigurationRepository tenantConfigurationRepository; - - @Autowired - private ApplicationContext applicationContext; - - private final ConfigurableConversionService conversionService = new DefaultConversionService(); - - private Environment environment; +public interface TenantConfigurationManagement { /** * Retrieves a configuration value from the e.g. tenant overwritten @@ -70,37 +34,10 @@ public class TenantConfigurationManagement implements EnvironmentAware { * if the property cannot be converted to the given * {@code propertyType} */ - - @Cacheable(value = "tenantConfiguration", key = "#configurationKey.getKeyName()") @PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION + SpringEvalExpressions.HAS_AUTH_OR + SpringEvalExpressions.IS_SYSTEM_CODE) - public TenantConfigurationValue getConfigurationValue(final TenantConfigurationKey configurationKey, - final Class propertyType) { - validateTenantConfigurationDataType(configurationKey, propertyType); - - final TenantConfiguration tenantConfiguration = tenantConfigurationRepository - .findByKey(configurationKey.getKeyName()); - - return buildTenantConfigurationValueByKey(configurationKey, propertyType, tenantConfiguration); - } - - /** - * Validates the data type of the tenant configuration. If it is possible to - * cast to the given data type. - * - * @param configurationKey - * the key - * @param propertyType - * the class - */ - protected void validateTenantConfigurationDataType(final TenantConfigurationKey configurationKey, - final Class propertyType) { - if (!configurationKey.getDataType().isAssignableFrom(propertyType)) { - throw new TenantConfigurationValidatorException( - String.format("Cannot parse the database value of type %s into the type %s.", - configurationKey.getDataType(), propertyType)); - } - } + TenantConfigurationValue getConfigurationValue(TenantConfigurationKey configurationKey, + Class propertyType); /** * Build the tenant configuration by the given key @@ -114,24 +51,10 @@ public class TenantConfigurationManagement implements EnvironmentAware { * @return if no default value is set and no database value available * or returns the tenant configuration value */ - protected TenantConfigurationValue buildTenantConfigurationValueByKey( - final TenantConfigurationKey configurationKey, final Class propertyType, - final TenantConfiguration tenantConfiguration) { - if (tenantConfiguration != null) { - return TenantConfigurationValue. builder().isGlobal(false).createdBy(tenantConfiguration.getCreatedBy()) - .createdAt(tenantConfiguration.getCreatedAt()) - .lastModifiedAt(tenantConfiguration.getLastModifiedAt()) - .lastModifiedBy(tenantConfiguration.getLastModifiedBy()) - .value(conversionService.convert(tenantConfiguration.getValue(), propertyType)).build(); - - } else if (configurationKey.getDefaultKeyName() != null) { - - return TenantConfigurationValue. builder().isGlobal(true).createdBy(null).createdAt(null) - .lastModifiedAt(null).lastModifiedBy(null) - .value(getGlobalConfigurationValue(configurationKey, propertyType)).build(); - } - return null; - } + @PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION + SpringEvalExpressions.HAS_AUTH_OR + + SpringEvalExpressions.IS_SYSTEM_CODE) + TenantConfigurationValue buildTenantConfigurationValueByKey(TenantConfigurationKey configurationKey, + Class propertyType, TenantConfiguration tenantConfiguration); /** * Retrieves a configuration value from the e.g. tenant overwritten @@ -153,9 +76,7 @@ public class TenantConfigurationManagement implements EnvironmentAware { */ @PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION + SpringEvalExpressions.HAS_AUTH_OR + SpringEvalExpressions.IS_SYSTEM_CODE) - public TenantConfigurationValue getConfigurationValue(final TenantConfigurationKey configurationKey) { - return getConfigurationValue(configurationKey, configurationKey.getDataType()); - } + TenantConfigurationValue getConfigurationValue(TenantConfigurationKey configurationKey); /** * returns the global configuration property either defined in the property @@ -179,23 +100,7 @@ public class TenantConfigurationManagement implements EnvironmentAware { */ @PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION + SpringEvalExpressions.HAS_AUTH_OR + SpringEvalExpressions.IS_SYSTEM_CODE) - public T getGlobalConfigurationValue(final TenantConfigurationKey configurationKey, - final Class propertyType) { - - if (!configurationKey.getDataType().isAssignableFrom(propertyType)) { - throw new TenantConfigurationValidatorException( - String.format("Cannot parse the database value of type %s into the type %s.", - configurationKey.getDataType(), propertyType)); - } - - final T valueInProperties = environment.getProperty(configurationKey.getDefaultKeyName(), propertyType); - - if (valueInProperties == null) { - return conversionService.convert(configurationKey.getDefaultValue(), propertyType); - } - - return valueInProperties; - } + T getGlobalConfigurationValue(TenantConfigurationKey configurationKey, Class propertyType); /** * Adds or updates a specific configuration for a specific tenant. @@ -213,41 +118,8 @@ public class TenantConfigurationManagement implements EnvironmentAware { * @throws ConversionFailedException * if the property cannot be converted to the given */ - @CacheEvict(value = "tenantConfiguration", key = "#configurationKey.getKeyName()") - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @Modifying @PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION) - public TenantConfigurationValue addOrUpdateConfiguration(final TenantConfigurationKey configurationKey, - final T value) { - - if (!configurationKey.getDataType().isAssignableFrom(value.getClass())) { - throw new TenantConfigurationValidatorException(String.format( - "Cannot parse the value %s of type %s into the type %s defined by the configuration key.", value, - value.getClass(), configurationKey.getDataType())); - } - - configurationKey.validate(applicationContext, value); - - TenantConfiguration tenantConfiguration = tenantConfigurationRepository - .findByKey(configurationKey.getKeyName()); - - if (tenantConfiguration == null) { - tenantConfiguration = new TenantConfiguration(configurationKey.getKeyName(), value.toString()); - } else { - tenantConfiguration.setValue(value.toString()); - } - - final TenantConfiguration updatedTenantConfiguration = tenantConfigurationRepository.save(tenantConfiguration); - - final Class clazzT = (Class) value.getClass(); - - return TenantConfigurationValue. builder().isGlobal(false) - .createdBy(updatedTenantConfiguration.getCreatedBy()) - .createdAt(updatedTenantConfiguration.getCreatedAt()) - .lastModifiedAt(updatedTenantConfiguration.getLastModifiedAt()) - .lastModifiedBy(updatedTenantConfiguration.getLastModifiedBy()) - .value(conversionService.convert(updatedTenantConfiguration.getValue(), clazzT)).build(); - } + TenantConfigurationValue addOrUpdateConfiguration(TenantConfigurationKey configurationKey, T value); /** * Deletes a specific configuration for the current tenant. Does nothing in @@ -256,16 +128,7 @@ public class TenantConfigurationManagement implements EnvironmentAware { * @param configurationKey * the configuration key to be deleted */ - @CacheEvict(value = "tenantConfiguration", key = "#configurationKey.getKeyName()") - @Transactional(isolation = Isolation.READ_UNCOMMITTED) - @Modifying @PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION) - public void deleteConfiguration(final TenantConfigurationKey configurationKey) { - tenantConfigurationRepository.deleteByKey(configurationKey.getKeyName()); - } + void deleteConfiguration(TenantConfigurationKey configurationKey); - @Override - public void setEnvironment(final Environment environment) { - this.environment = environment; - } -} +} \ No newline at end of file diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ActionRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/ActionRepository.java similarity index 99% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ActionRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/ActionRepository.java index 742a7d731..56ade85ff 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ActionRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/ActionRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import java.util.Collection; import java.util.List; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ActionStatusRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/ActionStatusRepository.java similarity index 98% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ActionStatusRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/ActionStatusRepository.java index 2a0e8cfb3..61d95e15e 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ActionStatusRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/ActionStatusRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.Status; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/BaseEntityRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/BaseEntityRepository.java similarity index 97% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/BaseEntityRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/BaseEntityRepository.java index 16f80596d..693827803 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/BaseEntityRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/BaseEntityRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import java.io.Serializable; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetMetadataRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/DistributionSetMetadataRepository.java similarity index 96% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetMetadataRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/DistributionSetMetadataRepository.java index c042a32d0..68cd380e5 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetMetadataRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/DistributionSetMetadataRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import org.eclipse.hawkbit.repository.model.DistributionSetMetadata; import org.eclipse.hawkbit.repository.model.DsMetadataCompositeKey; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/DistributionSetRepository.java similarity index 99% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/DistributionSetRepository.java index f2138bb2d..c7ab44145 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/DistributionSetRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import java.util.Collection; import java.util.List; @@ -27,8 +27,6 @@ import org.springframework.transaction.annotation.Transactional; /** * {@link DistributionSet} repository. * - * - * */ @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) public interface DistributionSetRepository diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetTagRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/DistributionSetTagRepository.java similarity index 97% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetTagRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/DistributionSetTagRepository.java index 8521a96b9..9e55a305b 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetTagRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/DistributionSetTagRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import java.util.List; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetTypeRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/DistributionSetTypeRepository.java similarity index 98% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetTypeRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/DistributionSetTypeRepository.java index 289dd6c09..7c0d39b58 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DistributionSetTypeRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/DistributionSetTypeRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/EclipseLinkTargetInfoRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/EclipseLinkTargetInfoRepository.java similarity index 98% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/EclipseLinkTargetInfoRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/EclipseLinkTargetInfoRepository.java index e37a51d6c..ca124f81c 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/EclipseLinkTargetInfoRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/EclipseLinkTargetInfoRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import java.util.Collection; import java.util.List; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ExternalArtifactProviderRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/ExternalArtifactProviderRepository.java similarity index 94% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ExternalArtifactProviderRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/ExternalArtifactProviderRepository.java index d0802d242..4a29afc4b 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ExternalArtifactProviderRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/ExternalArtifactProviderRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import org.eclipse.hawkbit.repository.model.ExternalArtifactProvider; import org.springframework.transaction.annotation.Isolation; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ExternalArtifactRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/ExternalArtifactRepository.java similarity index 96% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ExternalArtifactRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/ExternalArtifactRepository.java index d20da8013..dfc8105dd 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/ExternalArtifactRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/ExternalArtifactRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import org.eclipse.hawkbit.repository.model.ExternalArtifact; import org.springframework.data.domain.Page; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaArtifactManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaArtifactManagement.java new file mode 100644 index 000000000..c6d30208c --- /dev/null +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaArtifactManagement.java @@ -0,0 +1,270 @@ +/** + * 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 java.io.InputStream; +import java.util.List; + +import org.eclipse.hawkbit.artifact.repository.ArtifactRepository; +import org.eclipse.hawkbit.artifact.repository.ArtifactStoreException; +import org.eclipse.hawkbit.artifact.repository.HashNotMatchException; +import org.eclipse.hawkbit.artifact.repository.model.DbArtifact; +import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash; +import org.eclipse.hawkbit.repository.ArtifactManagement; +import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException; +import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException; +import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; +import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.exception.GridFSDBFileNotFoundException; +import org.eclipse.hawkbit.repository.exception.InvalidMD5HashException; +import org.eclipse.hawkbit.repository.exception.InvalidSHA1HashException; +import org.eclipse.hawkbit.repository.model.Artifact; +import org.eclipse.hawkbit.repository.model.ExternalArtifact; +import org.eclipse.hawkbit.repository.model.ExternalArtifactProvider; +import org.eclipse.hawkbit.repository.model.LocalArtifact; +import org.eclipse.hawkbit.repository.model.SoftwareModule; +import org.eclipse.hawkbit.repository.specifications.SoftwareModuleSpecification; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +/** + * JPA based {@link ArtifactManagement} implementation. + * + */ +@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) +@Validated +@Service +public class JpaArtifactManagement implements ArtifactManagement { + + private static final Logger LOG = LoggerFactory.getLogger(JpaArtifactManagement.class); + + @Autowired + private LocalArtifactRepository localArtifactRepository; + + @Autowired + private ExternalArtifactRepository externalArtifactRepository; + + @Autowired + private SoftwareModuleRepository softwareModuleRepository; + + @Autowired + private ExternalArtifactProviderRepository externalArtifactProviderRepository; + + @Autowired + private ArtifactRepository artifactRepository; + + private static LocalArtifact checkForExistingArtifact(final String filename, final boolean overrideExisting, + final SoftwareModule softwareModule) { + if (softwareModule.getLocalArtifactByFilename(filename).isPresent()) { + if (overrideExisting) { + LOG.debug("overriding existing artifact with new filename {}", filename); + return softwareModule.getLocalArtifactByFilename(filename).get(); + } else { + throw new EntityAlreadyExistsException("File with that name already exists in the Software Module"); + } + } + return null; + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public ExternalArtifact createExternalArtifact(final ExternalArtifactProvider externalRepository, + final String urlSuffix, final Long moduleId) { + + final SoftwareModule module = getModuleAndThrowExceptionIfThatFails(moduleId); + return externalArtifactRepository.save(new ExternalArtifact(externalRepository, urlSuffix, module)); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public ExternalArtifactProvider createExternalArtifactProvider(final String name, final String description, + final String basePath, final String defaultUrlSuffix) { + return externalArtifactProviderRepository + .save(new ExternalArtifactProvider(name, description, basePath, defaultUrlSuffix)); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public LocalArtifact createLocalArtifact(final InputStream stream, final Long moduleId, final String filename, + final String providedMd5Sum, final String providedSha1Sum, final boolean overrideExisting, + final String contentType) { + DbArtifact result = null; + + final SoftwareModule softwareModule = getModuleAndThrowExceptionIfThatFails(moduleId); + + final LocalArtifact existing = checkForExistingArtifact(filename, overrideExisting, softwareModule); + + try { + result = artifactRepository.store(stream, filename, contentType, + new DbArtifactHash(providedSha1Sum, providedMd5Sum)); + } catch (final ArtifactStoreException e) { + throw new ArtifactUploadFailedException(e); + } catch (final HashNotMatchException e) { + if (e.getHashFunction().equals(HashNotMatchException.SHA1)) { + throw new InvalidSHA1HashException(e.getMessage(), e); + } else { + throw new InvalidMD5HashException(e.getMessage(), e); + } + } + if (result == null) { + return null; + } + + return storeArtifactMetadata(softwareModule, filename, result, existing); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public void deleteExternalArtifact(final Long id) { + final ExternalArtifact existing = externalArtifactRepository.findOne(id); + + if (null == existing) { + return; + } + + existing.getSoftwareModule().removeArtifact(existing); + softwareModuleRepository.save(existing.getSoftwareModule()); + externalArtifactRepository.delete(id); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public void deleteLocalArtifact(final LocalArtifact existing) { + if (existing == null) { + return; + } + + boolean artifactIsOnlyUsedByOneSoftwareModule = true; + for (final LocalArtifact lArtifact : localArtifactRepository + .findByGridFsFileName(existing.getGridFsFileName())) { + if (!lArtifact.getSoftwareModule().isDeleted() + && Long.compare(lArtifact.getSoftwareModule().getId(), existing.getSoftwareModule().getId()) != 0) { + artifactIsOnlyUsedByOneSoftwareModule = false; + break; + } + } + + if (artifactIsOnlyUsedByOneSoftwareModule) { + try { + LOG.debug("deleting artifact from repository {}", existing.getGridFsFileName()); + artifactRepository.deleteBySha1(existing.getGridFsFileName()); + } catch (final ArtifactStoreException e) { + throw new ArtifactDeleteFailedException(e); + } + } + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public void deleteLocalArtifact(final Long id) { + final LocalArtifact existing = localArtifactRepository.findOne(id); + + if (null == existing) { + return; + } + + deleteLocalArtifact(existing); + + existing.getSoftwareModule().removeArtifact(existing); + softwareModuleRepository.save(existing.getSoftwareModule()); + localArtifactRepository.delete(id); + } + + @Override + public Artifact findArtifact(final Long id) { + return localArtifactRepository.findOne(id); + } + + @Override + public List findByFilenameAndSoftwareModule(final String filename, final Long softwareModuleId) { + return localArtifactRepository.findByFilenameAndSoftwareModuleId(filename, softwareModuleId); + } + + @Override + public LocalArtifact findFirstLocalArtifactsBySHA1(final String sha1) { + return localArtifactRepository.findFirstByGridFsFileName(sha1); + } + + @Override + public List findLocalArtifactByFilename(final String filename) { + return localArtifactRepository.findByFilename(filename); + } + + @Override + public Page findLocalArtifactBySoftwareModule(final Pageable pageReq, final Long swId) { + return localArtifactRepository.findBySoftwareModuleId(pageReq, swId); + } + + @Override + public SoftwareModule findSoftwareModuleById(final Long id) { + + final Specification spec = SoftwareModuleSpecification.byId(id); + + return softwareModuleRepository.findOne(spec); + } + + @Override + public SoftwareModule findSoftwareModuleWithDetails(final Long id) { + final SoftwareModule result = findSoftwareModuleById(id); + if (result != null) { + result.getArtifacts().size(); + } + + return result; + } + + private SoftwareModule getModuleAndThrowExceptionIfThatFails(final Long moduleId) { + final SoftwareModule softwareModule = findSoftwareModuleWithDetails(moduleId); + + if (softwareModule == null) { + LOG.debug("no software module with ID {} exists", moduleId); + throw new EntityNotFoundException("Software Module: " + moduleId); + } + return softwareModule; + } + + @Override + public DbArtifact loadLocalArtifactBinary(final LocalArtifact artifact) { + final DbArtifact result = artifactRepository.getArtifactBySha1(artifact.getGridFsFileName()); + if (result == null) { + throw new GridFSDBFileNotFoundException(artifact.getGridFsFileName()); + } + + return result; + } + + private LocalArtifact storeArtifactMetadata(final SoftwareModule softwareModule, final String providedFilename, + final DbArtifact result, final LocalArtifact existing) { + LocalArtifact artifact = existing; + if (existing == null) { + artifact = new LocalArtifact(result.getHashes().getSha1(), providedFilename, softwareModule); + } + artifact.setMd5Hash(result.getHashes().getMd5()); + artifact.setSha1Hash(result.getHashes().getSha1()); + artifact.setSize(result.getSize()); + + LOG.debug("storing new artifact into repository {}", artifact); + return localArtifactRepository.save(artifact); + } +} diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java new file mode 100644 index 000000000..76363cca9 --- /dev/null +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java @@ -0,0 +1,417 @@ +/** + * 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 java.net.URI; +import java.util.List; +import java.util.Map; + +import javax.persistence.EntityManager; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; +import javax.validation.constraints.NotNull; + +import org.eclipse.hawkbit.repository.ControllerManagement; +import org.eclipse.hawkbit.repository.TenantConfigurationManagement; +import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.exception.ToManyAttributeEntriesException; +import org.eclipse.hawkbit.repository.exception.ToManyStatusEntriesException; +import org.eclipse.hawkbit.repository.model.Action; +import org.eclipse.hawkbit.repository.model.Action.Status; +import org.eclipse.hawkbit.repository.model.ActionStatus; +import org.eclipse.hawkbit.repository.model.ActionStatus_; +import org.eclipse.hawkbit.repository.model.DistributionSet; +import org.eclipse.hawkbit.repository.model.LocalArtifact; +import org.eclipse.hawkbit.repository.model.SoftwareModule; +import org.eclipse.hawkbit.repository.model.Target; +import org.eclipse.hawkbit.repository.model.TargetInfo; +import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; +import org.eclipse.hawkbit.repository.model.Target_; +import org.eclipse.hawkbit.repository.model.TenantConfiguration; +import org.eclipse.hawkbit.repository.specifications.ActionSpecifications; +import org.eclipse.hawkbit.security.HawkbitSecurityProperties; +import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +/** + * JPA based {@link ControllerManagement} implementation. + * + */ +@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) +@Validated +@Service +public class JpaControllerManagement implements ControllerManagement { + private static final Logger LOG = LoggerFactory.getLogger(ControllerManagement.class); + private static final Logger LOG_DOS = LoggerFactory.getLogger("server-security.dos"); + + @Autowired + private EntityManager entityManager; + + @Autowired + private ActionRepository actionRepository; + + @Autowired + private TargetRepository targetRepository; + + @Autowired + private TargetManagement targetManagement; + + @Autowired + private TargetInfoRepository targetInfoRepository; + + @Autowired + private SoftwareModuleRepository softwareModuleRepository; + + @Autowired + private ActionStatusRepository actionStatusRepository; + + @Autowired + private HawkbitSecurityProperties securityProperties; + + @Autowired + private TenantConfigurationRepository tenantConfigurationRepository; + + @Autowired + private TenantConfigurationManagement tenantConfigurationManagement; + + @Override + public String getPollingTime() { + final TenantConfigurationKey configurationKey = TenantConfigurationKey.POLLING_TIME_INTERVAL; + final Class propertyType = String.class; + JpaTenantConfigurationManagement.validateTenantConfigurationDataType(configurationKey, propertyType); + final TenantConfiguration tenantConfiguration = tenantConfigurationRepository + .findByKey(configurationKey.getKeyName()); + return tenantConfigurationManagement + .buildTenantConfigurationValueByKey(configurationKey, propertyType, tenantConfiguration).getValue(); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public Target updateLastTargetQuery(final String controllerId, final URI address) { + final Target target = targetRepository.findByControllerId(controllerId); + if (target == null) { + throw new EntityNotFoundException(controllerId); + } + + return updateLastTargetQuery(target.getTargetInfo(), address).getTarget(); + } + + @Override + public Action getActionForDownloadByTargetAndSoftwareModule(final String controllerId, + final SoftwareModule module) { + final List action = actionRepository.findActionByTargetAndSoftwareModule(controllerId, module); + + if (action.isEmpty() || action.get(0).isCancelingOrCanceled()) { + throw new EntityNotFoundException( + "No assigment found for module " + module.getId() + " to target " + controllerId); + } + + return action.get(0); + } + + @Override + public boolean hasTargetArtifactAssigned(final String controllerId, final LocalArtifact localArtifact) { + final Target target = targetRepository.findByControllerId(controllerId); + if (target == null) { + return false; + } + return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(target, localArtifact)) > 0; + } + + @Override + public List findActionByTargetAndActive(final Target target) { + return actionRepository.findByTargetAndActiveOrderByIdAsc(target, true); + } + + @Override + public List findSoftwareModulesByDistributionSet(final DistributionSet distributionSet) { + return softwareModuleRepository.findByAssignedTo(distributionSet); + } + + @Override + public Action findActionWithDetails(final Long actionId) { + return actionRepository.findById(actionId); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public Target findOrRegisterTargetIfItDoesNotexist(final String controllerId, final URI address) { + final Specification spec = (targetRoot, query, cb) -> cb.equal(targetRoot.get(Target_.controllerId), + controllerId); + + Target target = targetRepository.findOne(spec); + + if (target == null) { + target = new Target(controllerId); + target.setDescription("Plug and Play target: " + controllerId); + target.setName(controllerId); + return targetManagement.createTarget(target, TargetUpdateStatus.REGISTERED, System.currentTimeMillis(), + address); + } + + return updateLastTargetQuery(target.getTargetInfo(), address).getTarget(); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public TargetInfo updateTargetStatus(final TargetInfo targetInfo, final TargetUpdateStatus status, + final Long lastTargetQuery, final URI address) { + final TargetInfo mtargetInfo = entityManager.merge(targetInfo); + if (status != null) { + mtargetInfo.setUpdateStatus(status); + } + if (lastTargetQuery != null) { + mtargetInfo.setLastTargetQuery(lastTargetQuery); + } + if (address != null) { + mtargetInfo.setAddress(address.toString()); + } + return targetInfoRepository.save(mtargetInfo); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public Action addCancelActionStatus(final ActionStatus actionStatus) { + final Action action = actionStatus.getAction(); + + checkForToManyStatusEntries(action); + action.setStatus(actionStatus.getStatus()); + + switch (actionStatus.getStatus()) { + case WARNING: + case ERROR: + case RUNNING: + break; + case CANCELED: + case FINISHED: + // in case of successful cancellation we also report the success at + // the canceled action itself. + actionStatus.addMessage( + ControllerManagement.SERVER_MESSAGE_PREFIX + "Cancellation completion is finished sucessfully."); + DeploymentHelper.successCancellation(action, actionRepository, targetManagement, targetInfoRepository, + entityManager); + break; + case RETRIEVED: + actionStatus.addMessage(ControllerManagement.SERVER_MESSAGE_PREFIX + "Cancellation request retrieved."); + break; + default: + } + actionRepository.save(action); + actionStatusRepository.save(actionStatus); + + return action; + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public Action addUpdateActionStatus(@NotNull final ActionStatus actionStatus) { + final Action action = actionStatus.getAction(); + + if (!action.isActive()) { + LOG.debug("Update of actionStatus {} for action {} not possible since action not active anymore.", + actionStatus.getId(), action.getId()); + return action; + } + return handleAddUpdateActionStatus(actionStatus, action); + } + + /** + * Sets {@link TargetUpdateStatus} based on given {@link ActionStatus}. + * + * @param actionStatus + * @param action + * @return + */ + private Action handleAddUpdateActionStatus(final ActionStatus actionStatus, final Action action) { + LOG.debug("addUpdateActionStatus for action {}", action.getId()); + + final Action mergedAction = entityManager.merge(action); + Target mergedTarget = mergedAction.getTarget(); + // check for a potential DOS attack + checkForToManyStatusEntries(action); + + switch (actionStatus.getStatus()) { + case ERROR: + mergedTarget = DeploymentHelper.updateTargetInfo(mergedTarget, TargetUpdateStatus.ERROR, false, + targetInfoRepository, entityManager); + handleErrorOnAction(mergedAction, mergedTarget); + break; + case FINISHED: + handleFinishedAndStoreInTargetStatus(mergedTarget, mergedAction); + break; + case CANCELED: + case WARNING: + case RUNNING: + DeploymentHelper.updateTargetInfo(mergedTarget, TargetUpdateStatus.PENDING, false, targetInfoRepository, + entityManager); + break; + default: + break; + } + + actionStatusRepository.save(actionStatus); + + LOG.debug("addUpdateActionStatus {} for target {} is finished.", action.getId(), mergedTarget.getId()); + + return actionRepository.save(mergedAction); + } + + private void handleErrorOnAction(final Action mergedAction, final Target mergedTarget) { + mergedAction.setActive(false); + mergedAction.setStatus(Status.ERROR); + mergedTarget.setAssignedDistributionSet(null); + targetManagement.updateTarget(mergedTarget); + } + + private void checkForToManyStatusEntries(final Action action) { + if (securityProperties.getDos().getMaxStatusEntriesPerAction() > 0) { + + final Long statusCount = actionStatusRepository.countByAction(action); + + if (statusCount >= securityProperties.getDos().getMaxStatusEntriesPerAction()) { + LOG_DOS.error( + "Potential denial of service (DOS) attack identfied. More status entries in the system than permitted ({})!", + securityProperties.getDos().getMaxStatusEntriesPerAction()); + throw new ToManyStatusEntriesException( + String.valueOf(securityProperties.getDos().getMaxStatusEntriesPerAction())); + } + } + } + + private void handleFinishedAndStoreInTargetStatus(final Target target, final Action action) { + action.setActive(false); + action.setStatus(Status.FINISHED); + final TargetInfo targetInfo = target.getTargetInfo(); + final DistributionSet ds = entityManager.merge(action.getDistributionSet()); + targetInfo.setInstalledDistributionSet(ds); + if (target.getAssignedDistributionSet() != null && targetInfo.getInstalledDistributionSet() != null && target + .getAssignedDistributionSet().getId().equals(targetInfo.getInstalledDistributionSet().getId())) { + targetInfo.setUpdateStatus(TargetUpdateStatus.IN_SYNC); + targetInfo.setInstallationDate(System.currentTimeMillis()); + } else { + targetInfo.setUpdateStatus(TargetUpdateStatus.PENDING); + targetInfo.setInstallationDate(System.currentTimeMillis()); + } + targetInfoRepository.save(targetInfo); + entityManager.detach(ds); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public Target updateControllerAttributes(final String controllerId, final Map data) { + final Target target = targetRepository.findByControllerId(controllerId); + + if (target == null) { + throw new EntityNotFoundException(controllerId); + } + + target.getTargetInfo().getControllerAttributes().putAll(data); + + if (target.getTargetInfo().getControllerAttributes().size() > securityProperties.getDos() + .getMaxAttributeEntriesPerTarget()) { + LOG_DOS.info("Target tries to insert more than the allowed number of entries ({}). DOS attack anticipated!", + securityProperties.getDos().getMaxAttributeEntriesPerTarget()); + throw new ToManyAttributeEntriesException( + String.valueOf(securityProperties.getDos().getMaxAttributeEntriesPerTarget())); + } + + target.getTargetInfo().setLastTargetQuery(System.currentTimeMillis()); + target.getTargetInfo().setRequestControllerAttributes(false); + return targetRepository.save(target); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public Action registerRetrieved(final Action action, final String message) { + return handleRegisterRetrieved(action, message); + } + + /** + * Registers retrieved status for given {@link Target} and {@link Action} if + * it does not exist yet. + * + * @param action + * to the handle status for + * @param message + * for the status + * @return the updated action in case the status has been changed to + * {@link Status#RETRIEVED} + */ + private Action handleRegisterRetrieved(final Action action, final String message) { + // do a manual query with CriteriaBuilder to avoid unnecessary field + // queries and an extra + // count query made by spring-data when using pageable requests, we + // don't need an extra count + // query, we just want to check if the last action status is a retrieved + // or not. + final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + final CriteriaQuery queryActionStatus = cb.createQuery(Object[].class); + final Root actionStatusRoot = queryActionStatus.from(ActionStatus.class); + final CriteriaQuery query = queryActionStatus + .multiselect(actionStatusRoot.get(ActionStatus_.id), actionStatusRoot.get(ActionStatus_.status)) + .where(cb.equal(actionStatusRoot.get(ActionStatus_.action), action)) + .orderBy(cb.desc(actionStatusRoot.get(ActionStatus_.id))); + final List resultList = entityManager.createQuery(query).setFirstResult(0).setMaxResults(1) + .getResultList(); + + // if the latest status is not in retrieve state then we add a retrieved + // state again, we want + // to document a deployment retrieved status and a cancel retrieved + // status, but multiple + // retrieves after the other we don't want to store to protect to + // overflood action status in + // case controller retrieves a action multiple times. + if (resultList.isEmpty() || resultList.get(0)[1] != Status.RETRIEVED) { + // document that the status has been retrieved + actionStatusRepository + .save(new ActionStatus(action, Status.RETRIEVED, System.currentTimeMillis(), message)); + + // don't change the action status itself in case the action is in + // canceling state otherwise + // we modify the action status and the controller won't get the + // cancel job anymore. + if (!action.isCancelingOrCanceled()) { + final Action actionMerge = entityManager.merge(action); + actionMerge.setStatus(Status.RETRIEVED); + return actionRepository.save(actionMerge); + } + } + return action; + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public void addInformationalActionStatus(final ActionStatus statusMessage) { + actionStatusRepository.save(statusMessage); + } + + @Override + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public String getSecurityTokenByControllerId(final String controllerId) { + final Target target = targetRepository.findByControllerId(controllerId); + return target != null ? target.getSecurityToken() : null; + } +} diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java new file mode 100644 index 000000000..50bbd2d31 --- /dev/null +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -0,0 +1,652 @@ +/** + * 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 java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +import javax.persistence.EntityManager; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.JoinType; +import javax.persistence.criteria.ListJoin; +import javax.persistence.criteria.Root; +import javax.validation.constraints.NotNull; + +import org.eclipse.hawkbit.Constants; +import org.eclipse.hawkbit.eventbus.event.CancelTargetAssignmentEvent; +import org.eclipse.hawkbit.eventbus.event.TargetAssignDistributionSetEvent; +import org.eclipse.hawkbit.eventbus.event.TargetInfoUpdateEvent; +import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor; +import org.eclipse.hawkbit.repository.DeploymentManagement; +import org.eclipse.hawkbit.repository.DistributionSetAssignmentResult; +import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException; +import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.exception.ForceQuitActionNotAllowedException; +import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException; +import org.eclipse.hawkbit.repository.model.Action; +import org.eclipse.hawkbit.repository.model.Action.ActionType; +import org.eclipse.hawkbit.repository.model.Action.Status; +import org.eclipse.hawkbit.repository.model.ActionStatus; +import org.eclipse.hawkbit.repository.model.ActionWithStatusCount; +import org.eclipse.hawkbit.repository.model.Action_; +import org.eclipse.hawkbit.repository.model.DistributionSet; +import org.eclipse.hawkbit.repository.model.DistributionSetType; +import org.eclipse.hawkbit.repository.model.DistributionSet_; +import org.eclipse.hawkbit.repository.model.Rollout; +import org.eclipse.hawkbit.repository.model.RolloutGroup; +import org.eclipse.hawkbit.repository.model.Rollout_; +import org.eclipse.hawkbit.repository.model.SoftwareModule; +import org.eclipse.hawkbit.repository.model.SoftwareModuleType; +import org.eclipse.hawkbit.repository.model.Target; +import org.eclipse.hawkbit.repository.model.TargetInfo; +import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; +import org.eclipse.hawkbit.repository.specifications.TargetSpecifications; +import org.hibernate.validator.constraints.NotEmpty; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.data.domain.AuditorAware; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import com.google.common.collect.Lists; +import com.google.common.eventbus.EventBus; + +/** + * JPA implementation for DeploymentManagement. + * + */ +@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) +@Validated +@Service +public class JpaDeploymentManagement implements DeploymentManagement { + private static final Logger LOG = LoggerFactory.getLogger(JpaDeploymentManagement.class); + + @Autowired + private EntityManager entityManager; + + @Autowired + private ActionRepository actionRepository; + + @Autowired + private DistributionSetRepository distributoinSetRepository; + + @Autowired + private SoftwareModuleRepository softwareModuleRepository; + + @Autowired + private TargetRepository targetRepository; + + @Autowired + private ActionStatusRepository actionStatusRepository; + + @Autowired + private TargetManagement targetManagement; + + @Autowired + private TargetInfoRepository targetInfoRepository; + + @Autowired + private AuditorAware auditorProvider; + + @Autowired + private EventBus eventBus; + + @Autowired + private AfterTransactionCommitExecutor afterCommit; + + @Override + @Transactional(isolation = Isolation.READ_COMMITTED) + @Modifying + @CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true) + public DistributionSetAssignmentResult assignDistributionSet(final DistributionSet pset, + final List targets) { + + return assignDistributionSetByTargetId(pset, + targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList()), + ActionType.FORCED, Action.NO_FORCE_TIME); + + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_COMMITTED) + + @CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true) + public DistributionSetAssignmentResult assignDistributionSet(final Long dsID, final String... targetIDs) { + return assignDistributionSet(dsID, ActionType.FORCED, Action.NO_FORCE_TIME, targetIDs); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + @CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true) + // Exception squid:S2095: see + // https://jira.sonarsource.com/browse/SONARJAVA-1478 + @SuppressWarnings({ "squid:S2095" }) + public DistributionSetAssignmentResult assignDistributionSet(final Long dsID, final ActionType actionType, + final long forcedTimestamp, final String... targetIDs) { + return assignDistributionSet(dsID, Arrays.stream(targetIDs) + .map(t -> new TargetWithActionType(t, actionType, forcedTimestamp)).collect(Collectors.toList())); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_COMMITTED) + @CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true) + public DistributionSetAssignmentResult assignDistributionSet(final Long dsID, + final List targets) { + final DistributionSet set = distributoinSetRepository.findOne(dsID); + if (set == null) { + throw new EntityNotFoundException( + String.format("no %s with id %d found", DistributionSet.class.getSimpleName(), dsID)); + } + + return assignDistributionSetToTargets(set, targets, null, null); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_COMMITTED) + @CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true) + public DistributionSetAssignmentResult assignDistributionSet(final Long dsID, + final List targets, final Rollout rollout, final RolloutGroup rolloutGroup) { + final DistributionSet set = distributoinSetRepository.findOne(dsID); + if (set == null) { + throw new EntityNotFoundException( + String.format("no %s with id %d found", DistributionSet.class.getSimpleName(), dsID)); + } + + return assignDistributionSetToTargets(set, targets, rollout, rolloutGroup); + } + + /** + * method assigns the {@link DistributionSet} to all {@link Target}s by + * their IDs with a specific {@link ActionType} and {@code forcetime}. + * + * @param dsID + * the ID of the distribution set to assign + * @param targets + * a list of all targets and their action type + * @param rollout + * the rollout for this assignment + * @param rolloutGroup + * the rollout group for this assignment + * @return the assignment result + * + * @throw IncompleteDistributionSetException if mandatory + * {@link SoftwareModuleType} are not assigned as define by the + * {@link DistributionSetType}. + */ + private DistributionSetAssignmentResult assignDistributionSetToTargets(@NotNull final DistributionSet set, + final List targetsWithActionType, final Rollout rollout, + final RolloutGroup rolloutGroup) { + + if (!set.isComplete()) { + throw new IncompleteDistributionSetException( + "Distribution set of type " + set.getType().getKey() + " is incomplete: " + set.getId()); + } + + final List controllerIDs = targetsWithActionType.stream().map(TargetWithActionType::getTargetId) + .collect(Collectors.toList()); + + LOG.debug("assignDistribution({}) to {} targets", set, controllerIDs.size()); + + final Map targetsWithActionMap = targetsWithActionType.stream() + .collect(Collectors.toMap(TargetWithActionType::getTargetId, Function.identity())); + + // split tIDs length into max entries in-statement because many database + // have constraint of max entries in in-statements e.g. Oracle with + // maximum 1000 elements, so we need to split the entries here and + // execute multiple statements we take the target only into account if + // the requested operation is no duplicate of a previous one + final List targets = Lists.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT).stream() + .map(ids -> targetRepository + .findAll(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(ids, set.getId()))) + .flatMap(t -> t.stream()).collect(Collectors.toList()); + + if (targets.isEmpty()) { + // detaching as it is not necessary to persist the set itself + entityManager.detach(set); + // return with nothing as all targets had the DS already assigned + return new DistributionSetAssignmentResult(Collections.emptyList(), 0, targetsWithActionType.size(), + Collections.emptyList(), targetManagement); + } + + final List> targetIds = Lists.partition( + targets.stream().map(Target::getId).collect(Collectors.toList()), Constants.MAX_ENTRIES_IN_STATEMENT); + + // override all active actions and set them into canceling state, we + // need to remember which one we have been switched to canceling state + // because for targets which we have changed to canceling we don't want + // to publish the new action update event. + final Set targetIdsCancellList = new HashSet<>(); + targetIds.forEach(ids -> targetIdsCancellList.addAll(overrideObsoleteUpdateActions(ids))); + + // cancel all scheduled actions which are in-active, these actions were + // not active before and the manual assignment which has been done + // cancels the + targetIds.forEach(tIds -> actionRepository.switchStatus(Status.CANCELED, tIds, false, Status.SCHEDULED)); + + // set assigned distribution set and TargetUpdateStatus + final String currentUser; + if (auditorProvider != null) { + currentUser = auditorProvider.getCurrentAuditor(); + } else { + currentUser = null; + } + + targetIds.forEach(tIds -> targetRepository.setAssignedDistributionSet(set, System.currentTimeMillis(), + currentUser, tIds)); + targetIds.forEach(tIds -> targetInfoRepository.setTargetUpdateStatus(TargetUpdateStatus.PENDING, tIds)); + final Map targetIdsToActions = actionRepository + .save(targets.stream().map(t -> createTargetAction(targetsWithActionMap, t, set, rollout, rolloutGroup)) + .collect(Collectors.toList())) + .stream().collect(Collectors.toMap(a -> a.getTarget().getControllerId(), Function.identity())); + + // create initial action status when action is created so we remember + // the initial running status because we will change the status + // of the action itself and with this action status we have a nicer + // action history. + targetIdsToActions.values().forEach(action -> { + final ActionStatus actionStatus = new ActionStatus(); + actionStatus.setAction(action); + actionStatus.setOccurredAt(action.getCreatedAt()); + actionStatus.setStatus(Status.RUNNING); + actionStatusRepository.save(actionStatus); + }); + + // flush to get action IDs + entityManager.flush(); + // collect updated target and actions IDs in order to return them + final DistributionSetAssignmentResult result = new DistributionSetAssignmentResult( + targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList()), targets.size(), + controllerIDs.size() - targets.size(), + targetIdsToActions.values().stream().map(Action::getId).collect(Collectors.toList()), targetManagement); + + LOG.debug("assignDistribution({}) finished {}", set, result); + + final List softwareModules = softwareModuleRepository.findByAssignedTo(set); + + // detaching as it is not necessary to persist the set itself + entityManager.detach(set); + + sendDistributionSetAssignmentEvent(targets, targetIdsCancellList, targetIdsToActions, softwareModules); + + return result; + } + + private void sendDistributionSetAssignmentEvent(final List targets, final Set targetIdsCancellList, + final Map targetIdsToActions, final List softwareModules) { + targets.stream().filter(t -> !!!targetIdsCancellList.contains(t.getId())) + .forEach(t -> assignDistributionSetEvent(t, targetIdsToActions.get(t.getControllerId()).getId(), + softwareModules)); + } + + private static Action createTargetAction(final Map targetsWithActionMap, + final Target target, final DistributionSet set, final Rollout rollout, final RolloutGroup rolloutGroup) { + final Action actionForTarget = new Action(); + final TargetWithActionType targetWithActionType = targetsWithActionMap.get(target.getControllerId()); + actionForTarget.setActionType(targetWithActionType.getActionType()); + actionForTarget.setForcedTime(targetWithActionType.getForceTime()); + actionForTarget.setActive(true); + actionForTarget.setStatus(Status.RUNNING); + actionForTarget.setTarget(target); + actionForTarget.setDistributionSet(set); + actionForTarget.setRollout(rollout); + actionForTarget.setRolloutGroup(rolloutGroup); + return actionForTarget; + } + + /** + * Sends the {@link TargetAssignDistributionSetEvent} for a specific target + * to the {@link EventBus}. + * + * @param target + * the Target which has been assigned to a distribution set + * @param actionId + * the action id of the assignment + * @param softwareModules + * the software modules which have been assigned + */ + private void assignDistributionSetEvent(final Target target, final Long actionId, + final List softwareModules) { + target.getTargetInfo().setUpdateStatus(TargetUpdateStatus.PENDING); + afterCommit.afterCommit(() -> { + eventBus.post(new TargetInfoUpdateEvent(target.getTargetInfo())); + eventBus.post(new TargetAssignDistributionSetEvent(target.getOptLockRevision(), target.getTenant(), + target.getControllerId(), actionId, softwareModules, target.getTargetInfo().getAddress(), + target.getSecurityToken())); + }); + } + + /** + * Removes {@link UpdateAction}s that are no longer necessary and sends + * cancellations to the controller. + * + * @param myTarget + * to override {@link UpdateAction}s + */ + private Set overrideObsoleteUpdateActions(final List targetsIds) { + + final Set cancelledTargetIds = new HashSet<>(); + + // Figure out if there are potential target/action combinations that + // need to be considered + // for cancelation + final List activeActions = actionRepository + .findByActiveAndTargetIdInAndActionStatusNotEqualToAndDistributionSetRequiredMigrationStep(targetsIds, + Action.Status.CANCELING); + activeActions.forEach(action -> { + action.setStatus(Status.CANCELING); + // document that the status has been retrieved + + actionStatusRepository.save(new ActionStatus(action, Status.CANCELING, System.currentTimeMillis(), + "manual cancelation requested")); + + cancelAssignDistributionSetEvent(action.getTarget(), action.getId()); + + cancelledTargetIds.add(action.getTarget().getId()); + }); + + actionRepository.save(activeActions); + + return cancelledTargetIds; + + } + + private DistributionSetAssignmentResult assignDistributionSetByTargetId(@NotNull final DistributionSet set, + @NotEmpty final List tIDs, final ActionType actionType, final long forcedTime) { + + return assignDistributionSetToTargets(set, tIDs.stream() + .map(t -> new TargetWithActionType(t, actionType, forcedTime)).collect(Collectors.toList()), null, + null); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_COMMITTED) + public Action cancelAction(final Action action, final Target target) { + LOG.debug("cancelAction({}, {})", action, target); + if (action.isCancelingOrCanceled()) { + throw new CancelActionNotAllowedException("Actions in canceling or canceled state cannot be canceled"); + } + final Action myAction = entityManager.merge(action); + + if (myAction.isActive()) { + LOG.debug("action ({}) was still active. Change to {}.", action, Status.CANCELING); + myAction.setStatus(Status.CANCELING); + + // document that the status has been retrieved + actionStatusRepository.save(new ActionStatus(myAction, Status.CANCELING, System.currentTimeMillis(), + "manual cancelation requested")); + final Action saveAction = actionRepository.save(myAction); + cancelAssignDistributionSetEvent(target, myAction.getId()); + + return saveAction; + } else { + throw new CancelActionNotAllowedException( + "Action [id: " + action.getId() + "] is not active and cannot be canceled"); + } + } + + /** + * Sends the {@link CancelTargetAssignmentEvent} for a specific target to + * the {@link EventBus}. + * + * @param target + * the Target which has been assigned to a distribution set + * @param actionId + * the action id of the assignment + */ + private void cancelAssignDistributionSetEvent(final Target target, final Long actionId) { + afterCommit.afterCommit(() -> eventBus.post(new CancelTargetAssignmentEvent(target.getOptLockRevision(), + target.getTenant(), target.getControllerId(), actionId, target.getTargetInfo().getAddress()))); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_COMMITTED) + public Action forceQuitAction(final Action action) { + final Action mergedAction = entityManager.merge(action); + + if (!mergedAction.isCancelingOrCanceled()) { + throw new ForceQuitActionNotAllowedException( + "Action [id: " + action.getId() + "] is not canceled yet and cannot be force quit"); + } + + if (!mergedAction.isActive()) { + throw new ForceQuitActionNotAllowedException( + "Action [id: " + action.getId() + "] is not active and cannot be force quit"); + } + + LOG.warn("action ({}) was still activ and has been force quite.", action); + + // document that the status has been retrieved + actionStatusRepository.save(new ActionStatus(mergedAction, Status.CANCELED, System.currentTimeMillis(), + "A force quit has been performed.")); + + DeploymentHelper.successCancellation(mergedAction, actionRepository, targetManagement, targetInfoRepository, + entityManager); + + return actionRepository.save(mergedAction); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_COMMITTED) + public void createScheduledAction(final List targets, final DistributionSet distributionSet, + final ActionType actionType, final long forcedTime, final Rollout rollout, + final RolloutGroup rolloutGroup) { + // cancel all current scheduled actions for this target. E.g. an action + // is already scheduled and a next action is created then cancel the + // current scheduled action to cancel. E.g. a new scheduled action is + // created. + final List targetIds = targets.stream().map(t -> t.getId()).collect(Collectors.toList()); + actionRepository.switchStatus(Action.Status.CANCELED, targetIds, false, Action.Status.SCHEDULED); + targets.forEach(target -> { + final Action action = new Action(); + action.setTarget(target); + action.setActive(false); + action.setDistributionSet(distributionSet); + action.setActionType(actionType); + action.setForcedTime(forcedTime); + action.setStatus(Status.SCHEDULED); + action.setRollout(rollout); + action.setRolloutGroup(rolloutGroup); + actionRepository.save(action); + }); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_COMMITTED) + public Action startScheduledAction(final Action action) { + + final Action mergedAction = entityManager.merge(action); + final Target mergedTarget = entityManager.merge(action.getTarget()); + + // check if we need to override running update actions + final Set overrideObsoleteUpdateActions = overrideObsoleteUpdateActions( + Collections.singletonList(action.getTarget().getId())); + + final boolean hasDistributionSetAlreadyAssigned = targetRepository + .count(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot( + Collections.singletonList(mergedTarget.getControllerId()), + action.getDistributionSet().getId())) == 0; + if (hasDistributionSetAlreadyAssigned) { + // the target has already the distribution set assigned, we don't + // need to start the scheduled action, just finished it. + mergedAction.setStatus(Status.FINISHED); + mergedAction.setActive(false); + return actionRepository.save(mergedAction); + } + + mergedAction.setActive(true); + mergedAction.setStatus(Status.RUNNING); + final Action savedAction = actionRepository.save(mergedAction); + + final ActionStatus actionStatus = new ActionStatus(); + actionStatus.setAction(action); + actionStatus.setOccurredAt(action.getCreatedAt()); + actionStatus.setStatus(Status.RUNNING); + actionStatusRepository.save(actionStatus); + + mergedTarget.setAssignedDistributionSet(action.getDistributionSet()); + final TargetInfo targetInfo = mergedTarget.getTargetInfo(); + targetInfo.setUpdateStatus(TargetUpdateStatus.PENDING); + targetRepository.save(mergedTarget); + targetInfoRepository.save(targetInfo); + + // in case we canceled an action before for this target, then don't fire + // assignment event + if (!overrideObsoleteUpdateActions.contains(savedAction.getId())) { + final List softwareModules = softwareModuleRepository + .findByAssignedTo(action.getDistributionSet()); + // send distribution set assignment event + + assignDistributionSetEvent(mergedAction.getTarget(), mergedAction.getId(), softwareModules); + } + return savedAction; + } + + @Override + public Action findAction(final Long actionId) { + return actionRepository.findOne(actionId); + } + + @Override + public Action findActionWithDetails(final Long actionId) { + return actionRepository.findById(actionId); + } + + @Override + public Slice findActionsByTarget(final Pageable pageable, final Target target) { + return actionRepository.findByTarget(pageable, target); + } + + @Override + public List findActionsByTarget(final Target target) { + return actionRepository.findByTarget(target); + } + + @Override + public List findActionsWithStatusCountByTargetOrderByIdDesc(final Target target) { + final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + final CriteriaQuery query = cb.createQuery(ActionWithStatusCount.class); + final Root actionRoot = query.from(Action.class); + final ListJoin actionStatusJoin = actionRoot.join(Action_.actionStatus, JoinType.LEFT); + final Join actionDsJoin = actionRoot.join(Action_.distributionSet); + final Join actionRolloutJoin = actionRoot.join(Action_.rollout, JoinType.LEFT); + + final CriteriaQuery multiselect = query.distinct(true).multiselect( + actionRoot.get(Action_.id), actionRoot.get(Action_.actionType), actionRoot.get(Action_.active), + actionRoot.get(Action_.forcedTime), actionRoot.get(Action_.status), actionRoot.get(Action_.createdAt), + actionRoot.get(Action_.lastModifiedAt), actionDsJoin.get(DistributionSet_.id), + actionDsJoin.get(DistributionSet_.name), actionDsJoin.get(DistributionSet_.version), + cb.count(actionStatusJoin), actionRolloutJoin.get(Rollout_.name)); + multiselect.where(cb.equal(actionRoot.get(Action_.target), target)); + multiselect.orderBy(cb.desc(actionRoot.get(Action_.id))); + multiselect.groupBy(actionRoot.get(Action_.id)); + return entityManager.createQuery(multiselect).getResultList(); + } + + @Override + public Slice findActionsByTarget(final Specification specifiction, final Target target, + final Pageable pageable) { + + return actionRepository.findAll((Specification) (root, query, cb) -> cb + .and(specifiction.toPredicate(root, query, cb), cb.equal(root.get(Action_.target), target)), pageable); + } + + @Override + public Slice findActionsByTarget(final Target foundTarget, final Pageable pageable) { + return actionRepository.findByTarget(pageable, foundTarget); + } + + @Override + public Page findActiveActionsByTarget(final Pageable pageable, final Target target) { + return actionRepository.findByActiveAndTarget(pageable, target, true); + } + + @Override + public List findActiveActionsByTarget(final Target target) { + return actionRepository.findByActiveAndTarget(target, true); + } + + @Override + public List findInActiveActionsByTarget(final Target target) { + return actionRepository.findByActiveAndTarget(target, false); + } + + @Override + public Page findInActiveActionsByTarget(final Pageable pageable, final Target target) { + return actionRepository.findByActiveAndTarget(pageable, target, false); + } + + @Override + public Long countActionsByTarget(final Target target) { + return actionRepository.countByTarget(target); + } + + @Override + public Long countActionsByTarget(final Specification spec, final Target target) { + return actionRepository.count((root, query, cb) -> cb.and(spec.toPredicate(root, query, cb), + cb.equal(root.get(Action_.target), target))); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public Action forceTargetAction(final Long actionId) { + final Action action = actionRepository.findOne(actionId); + if (action != null && !action.isForced()) { + action.setActionType(ActionType.FORCED); + return actionRepository.save(action); + } + return action; + } + + @Override + public Page findActionStatusByAction(final Pageable pageReq, final Action action, + final boolean withMessages) { + if (withMessages) { + return actionStatusRepository.getByAction(pageReq, action); + } else { + return actionStatusRepository.findByAction(pageReq, action); + } + } + + @Override + public List findActionsByRolloutGroupParentAndStatus(final Rollout rollout, + final RolloutGroup rolloutGroupParent, final Action.Status actionStatus) { + return actionRepository.findByRolloutAndRolloutGroupParentAndStatus(rollout, rolloutGroupParent, actionStatus); + } + + @Override + public List findActionsByRolloutAndStatus(final Rollout rollout, final Action.Status actionStatus) { + return actionRepository.findByRolloutAndStatus(rollout, actionStatus); + } +} diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java new file mode 100644 index 000000000..160cf613c --- /dev/null +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java @@ -0,0 +1,663 @@ +/** + * 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 com.google.common.base.Preconditions.checkNotNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import javax.persistence.EntityManager; + +import org.eclipse.hawkbit.eventbus.event.DistributionSetTagAssigmentResultEvent; +import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor; +import org.eclipse.hawkbit.repository.DistributionSetFilter; +import org.eclipse.hawkbit.repository.DistributionSetManagement; +import org.eclipse.hawkbit.repository.DistributionSetFilter.DistributionSetFilterBuilder; +import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; +import org.eclipse.hawkbit.repository.exception.EntityLockedException; +import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException; +import org.eclipse.hawkbit.repository.model.Action; +import org.eclipse.hawkbit.repository.model.DistributionSet; +import org.eclipse.hawkbit.repository.model.DistributionSetMetadata; +import org.eclipse.hawkbit.repository.model.DistributionSetMetadata_; +import org.eclipse.hawkbit.repository.model.DistributionSetTag; +import org.eclipse.hawkbit.repository.model.DistributionSetTagAssignmentResult; +import org.eclipse.hawkbit.repository.model.DistributionSetType; +import org.eclipse.hawkbit.repository.model.DistributionSet_; +import org.eclipse.hawkbit.repository.model.DsMetadataCompositeKey; +import org.eclipse.hawkbit.repository.model.SoftwareModule; +import org.eclipse.hawkbit.repository.specifications.DistributionSetSpecification; +import org.eclipse.hawkbit.repository.specifications.DistributionSetTypeSpecification; +import org.eclipse.hawkbit.repository.specifications.SpecificationsBuilder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import com.google.common.base.Strings; +import com.google.common.eventbus.EventBus; + +/** + * Business facade for managing the {@link DistributionSet}s. + * + */ +@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) +@Validated +@Service +public class JpaDistributionSetManagement implements DistributionSetManagement { + + @Autowired + private EntityManager entityManager; + + @Autowired + private DistributionSetRepository distributionSetRepository; + + @Autowired + private TagManagement tagManagement; + + @Autowired + private SystemManagement systemManagement; + + @Autowired + private DistributionSetTypeRepository distributionSetTypeRepository; + + @Autowired + private DistributionSetMetadataRepository distributionSetMetadataRepository; + + @Autowired + private ActionRepository actionRepository; + + @Autowired + private EventBus eventBus; + + @Autowired + private AfterTransactionCommitExecutor afterCommit; + + @Override + public DistributionSet findDistributionSetByIdWithDetails(final Long distid) { + return distributionSetRepository.findOne(DistributionSetSpecification.byId(distid)); + } + + @Override + public DistributionSet findDistributionSetById(final Long distid) { + return distributionSetRepository.findOne(distid); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public DistributionSetTagAssignmentResult toggleTagAssignment(final Collection dsIds, final String tagName) { + + final Iterable sets = findDistributionSetListWithDetails(dsIds); + final DistributionSetTag myTag = tagManagement.findDistributionSetTag(tagName); + + DistributionSetTagAssignmentResult result; + final List toBeChangedDSs = new ArrayList<>(); + for (final DistributionSet set : sets) { + if (set.getTags().add(myTag)) { + toBeChangedDSs.add(set); + } + } + + // un-assignment case + if (toBeChangedDSs.isEmpty()) { + for (final DistributionSet set : sets) { + if (set.getTags().remove(myTag)) { + toBeChangedDSs.add(set); + } + } + result = new DistributionSetTagAssignmentResult(dsIds.size() - toBeChangedDSs.size(), 0, + toBeChangedDSs.size(), Collections.emptyList(), distributionSetRepository.save(toBeChangedDSs), + myTag); + } else { + result = new DistributionSetTagAssignmentResult(dsIds.size() - toBeChangedDSs.size(), toBeChangedDSs.size(), + 0, distributionSetRepository.save(toBeChangedDSs), Collections.emptyList(), myTag); + } + + final DistributionSetTagAssignmentResult resultAssignment = result; + afterCommit.afterCommit(() -> eventBus.post(new DistributionSetTagAssigmentResultEvent(resultAssignment))); + + // no reason to persist the tag + entityManager.detach(myTag); + return result; + } + + @Override + public List findDistributionSetListWithDetails(final Collection distributionIdSet) { + return distributionSetRepository.findAll(DistributionSetSpecification.byIds(distributionIdSet)); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public DistributionSet updateDistributionSet(final DistributionSet ds) { + checkNotNull(ds.getId()); + final DistributionSet persisted = findDistributionSetByIdWithDetails(ds.getId()); + checkDistributionSetSoftwareModulesIsAllowedToModify(ds, persisted.getModules()); + return distributionSetRepository.save(ds); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public void deleteDistributionSet(final Long... distributionSetIDs) { + final List toHardDelete = new ArrayList<>(); + + final List assigned = distributionSetRepository.findAssignedDistributionSetsById(distributionSetIDs); + + // soft delete assigned + if (!assigned.isEmpty()) { + distributionSetRepository.deleteDistributionSet(assigned.toArray(new Long[assigned.size()])); + } + + // mark the rest as hard delete + for (final Long setId : distributionSetIDs) { + if (!assigned.contains(setId)) { + toHardDelete.add(setId); + } + } + + // hard delete the rest if exixts + if (!toHardDelete.isEmpty()) { + // don't give the delete statement an empty list, JPA/Oracle cannot + // handle the empty list + distributionSetRepository.deleteByIdIn(toHardDelete); + } + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public DistributionSet createDistributionSet(final DistributionSet dSet) { + prepareDsSave(dSet); + if (dSet.getType() == null) { + dSet.setType(systemManagement.getTenantMetadata().getDefaultDsType()); + } + return distributionSetRepository.save(dSet); + } + + private void prepareDsSave(final DistributionSet dSet) { + if (dSet.getId() != null) { + throw new EntityAlreadyExistsException("Parameter seems to be an existing, already persisted entity"); + } + + if (distributionSetRepository.countByNameAndVersion(dSet.getName(), dSet.getVersion()) > 0) { + throw new EntityAlreadyExistsException("DistributionSet with that name and version already exists."); + } + + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public List createDistributionSets(final Collection distributionSets) { + for (final DistributionSet ds : distributionSets) { + prepareDsSave(ds); + if (ds.getType() == null) { + ds.setType(systemManagement.getTenantMetadata().getDefaultDsType()); + } + } + return distributionSetRepository.save(distributionSets); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public DistributionSet assignSoftwareModules(final DistributionSet ds, final Set softwareModules) { + checkDistributionSetSoftwareModulesIsAllowedToModify(ds, softwareModules); + for (final SoftwareModule softwareModule : softwareModules) { + ds.addModule(softwareModule); + } + return distributionSetRepository.save(ds); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public DistributionSet unassignSoftwareModule(final DistributionSet ds, final SoftwareModule softwareModule) { + final Set softwareModules = new HashSet<>(); + softwareModules.add(softwareModule); + ds.removeModule(softwareModule); + checkDistributionSetSoftwareModulesIsAllowedToModify(ds, softwareModules); + return distributionSetRepository.save(ds); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public DistributionSetType updateDistributionSetType(final DistributionSetType dsType) { + checkNotNull(dsType.getId()); + + final DistributionSetType persisted = distributionSetTypeRepository.findOne(dsType.getId()); + + // throw exception if user tries to update a DS type that is already in + // use + if (!persisted.areModuleEntriesIdentical(dsType) && distributionSetRepository.countByType(persisted) > 0) { + throw new EntityReadOnlyException( + String.format("distribution set type %s set is already assigned to targets and cannot be changed", + dsType.getName())); + } + + return distributionSetTypeRepository.save(dsType); + } + + @Override + public Page findDistributionSetTypesAll(final Specification spec, + final Pageable pageable) { + return distributionSetTypeRepository.findAll(spec, pageable); + } + + @Override + public Page findDistributionSetTypesAll(final Pageable pageable) { + return distributionSetTypeRepository.findByDeleted(pageable, false); + } + + @Override + public Page findDistributionSetsByFilters(final Pageable pageable, + final DistributionSetFilter distributionSetFilter) { + final List> specList = buildDistributionSetSpecifications(distributionSetFilter); + return findByCriteriaAPI(pageable, specList); + } + + /** + * + * @param distributionSetFilter + * had details of filters to be applied + * @return a single DistributionSet which is either installed or assigned to + * a specific target or {@code null}. + */ + private DistributionSet findDistributionSetsByFiltersAndInstalledOrAssignedTarget( + final DistributionSetFilter distributionSetFilter) { + final List> specList = buildDistributionSetSpecifications(distributionSetFilter); + if (specList == null || specList.isEmpty()) { + return null; + } + return distributionSetRepository.findOne(SpecificationsBuilder.combineWithAnd(specList)); + } + + @Override + public Page findDistributionSetsAll(final Pageable pageReq, final Boolean deleted, + final Boolean complete) { + final List> specList = new ArrayList<>(); + + if (deleted != null) { + final Specification spec = DistributionSetSpecification.isDeleted(deleted); + specList.add(spec); + } + + if (complete != null) { + final Specification spec = DistributionSetSpecification.isCompleted(complete); + specList.add(spec); + } + + return findByCriteriaAPI(pageReq, specList); + } + + @Override + public Page findDistributionSetsAll(final Specification spec, + final Pageable pageReq, final Boolean deleted) { + final List> specList = new ArrayList<>(); + if (deleted != null) { + specList.add(DistributionSetSpecification.isDeleted(deleted)); + } + specList.add(spec); + return findByCriteriaAPI(pageReq, specList); + } + + @Override + public Page findDistributionSetsAllOrderedByLinkTarget(final Pageable pageable, + final DistributionSetFilterBuilder distributionSetFilterBuilder, final String assignedOrInstalled) { + + final DistributionSetFilter filterWithInstalledTargets = distributionSetFilterBuilder + .setInstalledTargetId(assignedOrInstalled).setAssignedTargetId(null).build(); + final DistributionSet installedDS = findDistributionSetsByFiltersAndInstalledOrAssignedTarget( + filterWithInstalledTargets); + + final DistributionSetFilter filterWithAssignedTargets = distributionSetFilterBuilder.setInstalledTargetId(null) + .setAssignedTargetId(assignedOrInstalled).build(); + final DistributionSet assignedDS = findDistributionSetsByFiltersAndInstalledOrAssignedTarget( + filterWithAssignedTargets); + + final DistributionSetFilter dsFilterWithNoTargetLinked = distributionSetFilterBuilder.setInstalledTargetId(null) + .setAssignedTargetId(null).build(); + // first fine the distribution sets filtered by the given filter + // parameters + final Page findDistributionSetsByFilters = findDistributionSetsByFilters(pageable, + dsFilterWithNoTargetLinked); + + final List resultSet = new LinkedList<>(findDistributionSetsByFilters.getContent()); + int orderIndex = 0; + if (installedDS != null) { + final boolean remove = resultSet.remove(installedDS); + if (!remove) { + resultSet.remove(resultSet.size() - 1); + } + resultSet.add(orderIndex, installedDS); + orderIndex++; + } + if (assignedDS != null && !assignedDS.equals(installedDS)) { + final boolean remove = resultSet.remove(assignedDS); + if (!remove) { + resultSet.remove(resultSet.size() - 1); + } + resultSet.add(orderIndex, assignedDS); + } + + return new PageImpl<>(resultSet, pageable, findDistributionSetsByFilters.getTotalElements()); + } + + @Override + public DistributionSet findDistributionSetByNameAndVersion(final String distributionName, final String version) { + final Specification spec = DistributionSetSpecification + .equalsNameAndVersionIgnoreCase(distributionName, version); + return distributionSetRepository.findOne(spec); + + } + + @Override + public Iterable findDistributionSetList(final Collection dist) { + return distributionSetRepository.findAll(dist); + } + + @Override + public Long countDistributionSetsAll() { + + final List> specList = new ArrayList<>(); + + final Specification spec = DistributionSetSpecification.isDeleted(Boolean.FALSE); + specList.add(spec); + + return distributionSetRepository.count(SpecificationsBuilder.combineWithAnd(specList)); + } + + @Override + public Long countDistributionSetTypesAll() { + return distributionSetTypeRepository.countByDeleted(false); + } + + @Override + public DistributionSetType findDistributionSetTypeByName(final String name) { + return distributionSetTypeRepository.findOne(DistributionSetTypeSpecification.byName(name)); + } + + @Override + public DistributionSetType findDistributionSetTypeById(final Long id) { + return distributionSetTypeRepository.findOne(DistributionSetTypeSpecification.byId(id)); + } + + @Override + public DistributionSetType findDistributionSetTypeByKey(final String key) { + return distributionSetTypeRepository.findOne(DistributionSetTypeSpecification.byKey(key)); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public DistributionSetType createDistributionSetType(final DistributionSetType type) { + if (type.getId() != null) { + throw new EntityAlreadyExistsException("Given type contains an Id!"); + } + + return distributionSetTypeRepository.save(type); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public void deleteDistributionSetType(final DistributionSetType type) { + + if (distributionSetRepository.countByType(type) > 0) { + final DistributionSetType toDelete = entityManager.merge(type); + toDelete.setDeleted(true); + distributionSetTypeRepository.save(toDelete); + } else { + distributionSetTypeRepository.delete(type.getId()); + } + } + + @Override + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + @Modifying + public DistributionSetMetadata createDistributionSetMetadata(final DistributionSetMetadata metadata) { + if (distributionSetMetadataRepository.exists(metadata.getId())) { + throwMetadataKeyAlreadyExists(metadata.getId().getKey()); + } + // merge base distribution set so optLockRevision gets updated and audit + // log written because + // modifying metadata is modifying the base distribution set itself for + // auditing purposes. + entityManager.merge(metadata.getDistributionSet()).setLastModifiedAt(0L); + return distributionSetMetadataRepository.save(metadata); + } + + @Override + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + @Modifying + public List createDistributionSetMetadata( + final Collection metadata) { + for (final DistributionSetMetadata distributionSetMetadata : metadata) { + checkAndThrowAlreadyIfDistributionSetMetadataExists(distributionSetMetadata.getId()); + } + metadata.forEach(m -> entityManager.merge(m.getDistributionSet()).setLastModifiedAt(-1L)); + return (List) distributionSetMetadataRepository.save(metadata); + } + + @Override + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + @Modifying + public DistributionSetMetadata updateDistributionSetMetadata(final DistributionSetMetadata metadata) { + // check if exists otherwise throw entity not found exception + findOne(metadata.getId()); + // touch it to update the lock revision because we are modifying the + // DS indirectly + entityManager.merge(metadata.getDistributionSet()).setLastModifiedAt(0L); + return distributionSetMetadataRepository.save(metadata); + } + + @Override + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + @Modifying + public void deleteDistributionSetMetadata(final DsMetadataCompositeKey id) { + distributionSetMetadataRepository.delete(id); + } + + @Override + public Page findDistributionSetMetadataByDistributionSetId(final Long distributionSetId, + final Pageable pageable) { + + return distributionSetMetadataRepository.findAll( + (Specification) (root, query, cb) -> cb.equal( + root.get(DistributionSetMetadata_.distributionSet).get(DistributionSet_.id), distributionSetId), + pageable); + } + + @Override + public Page findDistributionSetMetadataByDistributionSetId(final Long distributionSetId, + final Specification spec, final Pageable pageable) { + return distributionSetMetadataRepository + .findAll( + (Specification) (root, query, + cb) -> cb.and( + cb.equal(root.get(DistributionSetMetadata_.distributionSet) + .get(DistributionSet_.id), distributionSetId), + spec.toPredicate(root, query, cb)), + pageable); + } + + @Override + public DistributionSetMetadata findOne(final DsMetadataCompositeKey id) { + final DistributionSetMetadata findOne = distributionSetMetadataRepository.findOne(id); + if (findOne == null) { + throw new EntityNotFoundException("Metadata with key '" + id.getKey() + "' does not exist"); + } + return findOne; + } + + @Override + public DistributionSet findDistributionSetByAction(final Action action) { + return distributionSetRepository.findByAction(action); + } + + @Override + public boolean isDistributionSetInUse(final DistributionSet distributionSet) { + return actionRepository.countByDistributionSet(distributionSet) > 0; + } + + private static List> buildDistributionSetSpecifications( + final DistributionSetFilter distributionSetFilter) { + final List> specList = new ArrayList<>(); + + Specification spec; + + if (null != distributionSetFilter.getIsComplete()) { + spec = DistributionSetSpecification.isCompleted(distributionSetFilter.getIsComplete()); + specList.add(spec); + } + + if (null != distributionSetFilter.getIsDeleted()) { + spec = DistributionSetSpecification.isDeleted(distributionSetFilter.getIsDeleted()); + specList.add(spec); + } + + if (distributionSetFilter.getType() != null) { + spec = DistributionSetSpecification.byType(distributionSetFilter.getType()); + specList.add(spec); + } + + if (!Strings.isNullOrEmpty(distributionSetFilter.getSearchText())) { + spec = DistributionSetSpecification.likeNameOrDescriptionOrVersion(distributionSetFilter.getSearchText()); + specList.add(spec); + } + + if (isDSWithNoTagSelected(distributionSetFilter) || isTagsSelected(distributionSetFilter)) { + spec = DistributionSetSpecification.hasTags(distributionSetFilter.getTagNames(), + distributionSetFilter.getSelectDSWithNoTag()); + specList.add(spec); + } + if (distributionSetFilter.getInstalledTargetId() != null) { + spec = DistributionSetSpecification.installedTarget(distributionSetFilter.getInstalledTargetId()); + specList.add(spec); + } + if (distributionSetFilter.getAssignedTargetId() != null) { + spec = DistributionSetSpecification.assignedTarget(distributionSetFilter.getAssignedTargetId()); + specList.add(spec); + } + return specList; + } + + private void checkDistributionSetSoftwareModulesIsAllowedToModify(final DistributionSet distributionSet, + final Set softwareModules) { + if (!new HashSet(distributionSet.getModules()).equals(softwareModules) + && actionRepository.countByDistributionSet(distributionSet) > 0) { + throw new EntityLockedException( + String.format("distribution set %s:%s is already assigned to targets and cannot be changed", + distributionSet.getName(), distributionSet.getVersion())); + } + } + + private static Boolean isDSWithNoTagSelected(final DistributionSetFilter distributionSetFilter) { + if (distributionSetFilter.getSelectDSWithNoTag() != null && distributionSetFilter.getSelectDSWithNoTag()) { + return true; + } + return false; + } + + private static Boolean isTagsSelected(final DistributionSetFilter distributionSetFilter) { + if (distributionSetFilter.getTagNames() != null && !distributionSetFilter.getTagNames().isEmpty()) { + return true; + } + return false; + } + + /** + * executes findAll with the given {@link DistributionSet} + * {@link Specification}s. + * + * @param pageable + * paging parameter + * @param specList + * list of @link {@link Specification} + * @return the page with the found {@link DistributionSet} + */ + private Page findByCriteriaAPI(final Pageable pageable, + final List> specList) { + + if (specList == null || specList.isEmpty()) { + return distributionSetRepository.findAll(pageable); + } + + return distributionSetRepository.findAll(SpecificationsBuilder.combineWithAnd(specList), pageable); + } + + private void checkAndThrowAlreadyIfDistributionSetMetadataExists(final DsMetadataCompositeKey metadataId) { + if (distributionSetMetadataRepository.exists(metadataId)) { + throw new EntityAlreadyExistsException( + "Metadata entry with key '" + metadataId.getKey() + "' already exists"); + } + } + + private static void throwMetadataKeyAlreadyExists(final String metadataKey) { + throw new EntityAlreadyExistsException("Metadata entry with key '" + metadataKey + "' already exists"); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public List assignTag(final Collection dsIds, final DistributionSetTag tag) { + final List allDs = findDistributionSetListWithDetails(dsIds); + + allDs.forEach(ds -> ds.getTags().add(tag)); + final List save = distributionSetRepository.save(allDs); + + afterCommit.afterCommit(() -> { + + final DistributionSetTagAssignmentResult result = new DistributionSetTagAssignmentResult(0, save.size(), 0, + save, Collections.emptyList(), tag); + eventBus.post(new DistributionSetTagAssigmentResultEvent(result)); + }); + + return save; + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public List unAssignAllDistributionSetsByTag(final DistributionSetTag tag) { + return unAssignTag(tag.getAssignedToDistributionSet(), tag); + } + + @Override + @Modifying + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public DistributionSet unAssignTag(final Long dsId, final DistributionSetTag distributionSetTag) { + final List allDs = findDistributionSetListWithDetails(Arrays.asList(dsId)); + final List unAssignTag = unAssignTag(allDs, distributionSetTag); + return unAssignTag.isEmpty() ? null : unAssignTag.get(0); + } + + private List unAssignTag(final Collection distributionSets, + final DistributionSetTag tag) { + distributionSets.forEach(ds -> ds.getTags().remove(tag)); + return distributionSetRepository.save(distributionSets); + } +} diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaReportManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaReportManagement.java new file mode 100644 index 000000000..f0f704352 --- /dev/null +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaReportManagement.java @@ -0,0 +1,433 @@ +/** + * 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 java.io.Serializable; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.persistence.EntityManager; +import javax.persistence.Query; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Expression; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.JoinType; +import javax.persistence.criteria.ListJoin; +import javax.persistence.criteria.Root; + +import org.eclipse.hawkbit.report.model.DataReportSeries; +import org.eclipse.hawkbit.report.model.DataReportSeriesItem; +import org.eclipse.hawkbit.report.model.InnerOuterDataReportSeries; +import org.eclipse.hawkbit.report.model.SeriesTime; +import org.eclipse.hawkbit.repository.ReportManagement; +import org.eclipse.hawkbit.repository.model.DistributionSet; +import org.eclipse.hawkbit.repository.model.DistributionSet_; +import org.eclipse.hawkbit.repository.model.Target; +import org.eclipse.hawkbit.repository.model.TargetInfo; +import org.eclipse.hawkbit.repository.model.TargetInfo_; +import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; +import org.eclipse.hawkbit.repository.model.Target_; +import org.eclipse.hawkbit.tenancy.TenantAware; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +/** + * Service layer for generating hawkBit reports. + * + */ +@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) +@Validated +@Service +public class JpaReportManagement implements ReportManagement { + + @Value("${spring.jpa.database}") + private String databaseType; + + private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM"); + + private static final String H2_TARGET_CREATED_SQL_TEMPLATE = "SELECT TO_CHAR( DATEADD('second', target0_.created_at / 1000, DATE '1970-01-01'), '%s') AS col_0_0_, count(target0_.controller_id) AS col_1_0_ from sp_target target0_ WHERE TO_CHAR(DATEADD('second', target0_.created_at / 1000, DATE '1970-01-01'),'%s') BETWEEN TO_CHAR('%s', '%s') and TO_CHAR('%s', '%s') AND UPPER(target0_.tenant)=UPPER('%s') GROUP BY TO_CHAR(DATEADD('second', target0_.created_at / 1000, DATE '1970-01-01'), '%s')"; + + private static final String H2_CONTROLLER_FRRDBACK_SQL_TEMPLATE = "SELECT TO_CHAR(DATEADD('second', action_.created_at / 1000, DATE '1970-01-01'), '%s') AS col_0_0_, count(action_.id) AS col_1_0_ FROM sp_action action_ WHERE TO_CHAR(DATEADD('second', action_.created_at / 1000, DATE '1970-01-01'), '%s') BETWEEN TO_CHAR('%s', '%s') AND TO_CHAR('%s', '%s') AND UPPER(action_.tenant)=UPPER('%s') GROUP BY TO_CHAR(DATEADD('second', action_.created_at / 1000, DATE '1970-01-01'), '%s')"; + + private static final String MYSQL_TARGET_CREATED_SQL_TEMPLATE = "SELECT DATE_FORMAT(FROM_UNIXTIME(target0_.created_at / 1000), '%s') AS col_0_0_, COUNT(target0_.controller_id) AS col_1_0_ FROM sp_target target0_ WHERE DATE_FORMAT(FROM_UNIXTIME(target0_.created_at / 1000),'%s') BETWEEN DATE_FORMAT('%s', '%s') AND DATE_FORMAT('%s', '%s') AND UPPER(target0_.tenant)=UPPER('%s') GROUP BY DATE_FORMAT(FROM_UNIXTIME(target0_.created_at / 1000), '%s')"; + + private static final String MYSQL_CONTROLLER_FRRDBACK_SQL_TEMPLATE = "SELECT DATE_FORMAT(FROM_UNIXTIME(action_.created_at / 1000), '%s') AS col_0_0_, COUNT(action_.id) as col_1_0_ FROM sp_action action_ WHERE DATE_FORMAT(FROM_UNIXTIME(action_.created_at / 1000),'%s') BETWEEN DATE_FORMAT('%s', '%s') AND DATE_FORMAT('%s', '%s') AND UPPER(action_.tenant)=UPPER('%s') GROUP BY DATE_FORMAT(FROM_UNIXTIME(action_.created_at / 1000), '%s')"; + + private static final String MYSQL_DB_TYPE = "MYSQL"; + + private static final String H2_DB_TYPE = "H2"; + + @Autowired + private EntityManager entityManager; + + @Autowired + private TenantAware tenantAware; + + @Override + @Cacheable("targetStatus") + public DataReportSeries targetStatus() { + + final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + final CriteriaQuery query = cb.createQuery(Object[].class); + final Root targetRoot = query.from(Target.class); + final Join targetInfo = targetRoot.join(Target_.targetInfo); + final Expression countColumn = cb.count(targetInfo.get(TargetInfo_.targetId)); + final CriteriaQuery multiselect = query + .multiselect(targetInfo.get(TargetInfo_.updateStatus), countColumn) + .groupBy(targetInfo.get(TargetInfo_.updateStatus)) + .orderBy(cb.desc(targetInfo.get(TargetInfo_.updateStatus))); + + // | col1 | col2 | + // | U_STATUS | COUNT | + final List resultList = entityManager.createQuery(multiselect).getResultList(); + + final List> reportSeriesItems = resultList.stream() + .map(r -> new DataReportSeriesItem((TargetUpdateStatus) r[0], (Long) r[1])) + .collect(Collectors.toList()); + + return new DataReportSeries<>("Target Status Overview", reportSeriesItems); + } + + @Override + @Cacheable("distributionUsageAssigned") + public List> distributionUsageAssigned(final int topXEntries) { + + // top X entries distribution usage + final CriteriaBuilder cbTopX = entityManager.getCriteriaBuilder(); + final CriteriaQuery queryTopX = cbTopX.createQuery(Object[].class); + final Root rootTopX = queryTopX.from(DistributionSet.class); + final ListJoin joinTopX = rootTopX.join(DistributionSet_.assignedToTargets, + JoinType.LEFT); + final Expression countColumn = cbTopX.count(joinTopX); + // top x usage query + final CriteriaQuery groupBy = queryTopX + .multiselect(rootTopX.get(DistributionSet_.name), rootTopX.get(DistributionSet_.version), countColumn) + .where(cbTopX.equal(rootTopX.get(DistributionSet_.deleted), false)) + .groupBy(rootTopX.get(DistributionSet_.name), rootTopX.get(DistributionSet_.version)) + .orderBy(cbTopX.desc(countColumn), cbTopX.asc(rootTopX.get(DistributionSet_.name))); + // | col1 | col2 | col3 | + // | NAME | VER | COUNT | + final List resultListTop = entityManager.createQuery(groupBy).getResultList(); + // end of top X entries distribution usage + + return mapDistirbutionUsageResultToDataReport(topXEntries, resultListTop); + } + + @Override + @Cacheable("distributionUsageInstalled") + public List> distributionUsageInstalled(final int topXEntries) { + // top X entries distribution usage + final CriteriaBuilder cbTopX = entityManager.getCriteriaBuilder(); + final CriteriaQuery queryTopX = cbTopX.createQuery(Object[].class); + final Root rootTopX = queryTopX.from(DistributionSet.class); + final ListJoin joinTopX = rootTopX.join(DistributionSet_.installedAtTargets, + JoinType.LEFT); + final Expression countColumn = cbTopX.count(joinTopX); + // top x usage query + final CriteriaQuery groupBy = queryTopX + .multiselect(rootTopX.get(DistributionSet_.name), rootTopX.get(DistributionSet_.version), countColumn) + .where(cbTopX.equal(rootTopX.get(DistributionSet_.deleted), false)) + .groupBy(rootTopX.get(DistributionSet_.name), rootTopX.get(DistributionSet_.version)) + .orderBy(cbTopX.desc(countColumn), cbTopX.asc(rootTopX.get(DistributionSet_.name))); + // | col1 | col2 | col3 | + // | NAME | VER | COUNT | + final List resultListTop = entityManager.createQuery(groupBy).getResultList(); + // end of top X entries distribution usage + + return mapDistirbutionUsageResultToDataReport(topXEntries, resultListTop); + } + + @Override + @Cacheable("targetsCreatedOverPeriod") + public DataReportSeries targetsCreatedOverPeriod(final DateType dateType, + final LocalDateTime from, final LocalDateTime to) { + final Query createNativeQuery = entityManager + .createNativeQuery(getTargetsCreatedQueryTemplate(dateType, from, to)); + final List resultList = createNativeQuery.getResultList(); + + final List> reportItems = resultList.stream() + .map(r -> new DataReportSeriesItem<>(dateType.format((String) r[0]), ((Number) r[1]).longValue())) + .collect(Collectors.toList()); + + return new DataReportSeries<>("CreatedTargets", reportItems); + } + + private String getTargetsCreatedQueryTemplate(final DateType dateType, final LocalDateTime from, + final LocalDateTime to) { + switch (databaseType) { + case H2_DB_TYPE: + return String.format(H2_TARGET_CREATED_SQL_TEMPLATE, dateTimeFormatToSqlFormat(dateType), + dateTimeFormatToSqlFormat(dateType), from.format(DATE_FORMAT), dateTimeFormatToSqlFormat(dateType), + to.format(DATE_FORMAT), dateTimeFormatToSqlFormat(dateType), tenantAware.getCurrentTenant(), + dateTimeFormatToSqlFormat(dateType)); + case MYSQL_DB_TYPE: + return String.format(MYSQL_TARGET_CREATED_SQL_TEMPLATE, dateTimeFormatToSqlFormat(dateType), + dateTimeFormatToSqlFormat(dateType), from.toString(), dateTimeFormatToSqlFormat(dateType), + to.toString(), dateTimeFormatToSqlFormat(dateType), tenantAware.getCurrentTenant(), + dateTimeFormatToSqlFormat(dateType)); + default: + return null; + } + } + + private String getFeedbackReceivedQueryTemplate(final DateType dateType, final LocalDateTime from, + final LocalDateTime to) { + switch (databaseType) { + case H2_DB_TYPE: + return String.format(H2_CONTROLLER_FRRDBACK_SQL_TEMPLATE, dateTimeFormatToSqlFormat(dateType), + dateTimeFormatToSqlFormat(dateType), from.format(DATE_FORMAT), dateTimeFormatToSqlFormat(dateType), + to.format(DATE_FORMAT), dateTimeFormatToSqlFormat(dateType), tenantAware.getCurrentTenant(), + dateTimeFormatToSqlFormat(dateType)); + case MYSQL_DB_TYPE: + return String.format(MYSQL_CONTROLLER_FRRDBACK_SQL_TEMPLATE, dateTimeFormatToSqlFormat(dateType), + dateTimeFormatToSqlFormat(dateType), from.toString(), dateTimeFormatToSqlFormat(dateType), + to.toString(), dateTimeFormatToSqlFormat(dateType), tenantAware.getCurrentTenant(), + dateTimeFormatToSqlFormat(dateType)); + default: + return null; + } + } + + @Override + @Cacheable("feedbackReceivedOverTime") + public DataReportSeries feedbackReceivedOverTime(final DateType dateType, + final LocalDateTime from, final LocalDateTime to) { + final Query createNativeQuery = entityManager + .createNativeQuery(getFeedbackReceivedQueryTemplate(dateType, from, to)); + final List resultList = createNativeQuery.getResultList(); + + final List> reportItems = resultList.stream() + .map(r -> new DataReportSeriesItem<>(dateType.format((String) r[0]), ((Number) r[1]).longValue())) + .collect(Collectors.toList()); + + return new DataReportSeries<>("FeedbackRecieved", reportItems); + } + + @Override + @Cacheable("targetsLastPoll") + public DataReportSeries targetsLastPoll() { + + final LocalDateTime now = LocalDateTime.now(); + final LocalDateTime beforeHour = now.minusHours(1); + final LocalDateTime beforeDay = now.minusDays(1); + final LocalDateTime beforeWeek = now.minusWeeks(1); + final LocalDateTime beforeMonth = now.minusMonths(1); + final LocalDateTime beforeYear = now.minusYears(1); + + final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + final List> resultList = new ArrayList<>(); + + // hours + resultList.add(new DataReportSeriesItem(SeriesTime.HOUR, + entityManager.createQuery(createCountSelectTargetsLastPoll(cb, beforeHour, now)).getSingleResult())); + // days + resultList.add(new DataReportSeriesItem(SeriesTime.DAY, entityManager + .createQuery(createCountSelectTargetsLastPoll(cb, beforeDay, beforeHour)).getSingleResult())); + // weeks + resultList.add(new DataReportSeriesItem(SeriesTime.WEEK, entityManager + .createQuery(createCountSelectTargetsLastPoll(cb, beforeWeek, beforeDay)).getSingleResult())); + // months + resultList.add(new DataReportSeriesItem(SeriesTime.MONTH, entityManager + .createQuery(createCountSelectTargetsLastPoll(cb, beforeMonth, beforeWeek)).getSingleResult())); + // years + resultList.add(new DataReportSeriesItem(SeriesTime.YEAR, entityManager + .createQuery(createCountSelectTargetsLastPoll(cb, beforeYear, beforeMonth)).getSingleResult())); + // years + resultList.add(new DataReportSeriesItem(SeriesTime.MORE_THAN_YEAR, + entityManager.createQuery(createCountSelectTargetsLastPoll(cb, null, beforeYear)).getSingleResult())); + // never + resultList.add(new DataReportSeriesItem(SeriesTime.NEVER, + entityManager.createQuery(createCountSelectTargetsLastPoll(cb, null, null)).getSingleResult())); + + return new DataReportSeries<>("TargetLastPoll", resultList); + } + + private CriteriaQuery createCountSelectTargetsLastPoll(final CriteriaBuilder cb, final LocalDateTime from, + final LocalDateTime to) { + + Long start = null; + Long end = null; + if (from != null) { + start = from.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + if (to != null) { + end = to.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + + // count select statement + final CriteriaQuery countSelect = cb.createQuery(Long.class); + final Root countSelectRoot = countSelect.from(Target.class); + final Join targetInfoJoin = countSelectRoot.join(Target_.targetInfo); + countSelect.select(cb.count(countSelectRoot)); + if (start != null && end != null) { + countSelect.where(cb.between(targetInfoJoin.get(TargetInfo_.lastTargetQuery), start, end)); + } else if (from == null && to != null) { + countSelect.where(cb.lessThanOrEqualTo(targetInfoJoin.get(TargetInfo_.lastTargetQuery), end)); + } else { + countSelect.where(cb.isNull(targetInfoJoin.get(TargetInfo_.lastTargetQuery))); + } + return countSelect; + } + + private List> mapDistirbutionUsageResultToDataReport(final int topXEntries, + final List resultListTop) { + final List> innerOuterReport = new ArrayList<>(); + final Map map = new LinkedHashMap<>(); + + int topXCounter = 0; + + for (final Object[] objects : resultListTop) { + + final boolean containsInnerOuter = map.containsKey(new DSName((String) objects[0])); + final String name = containsInnerOuter || topXCounter < topXEntries ? (String) objects[0] : null; + final DSName dsName = new DSName(name); + final String version = containsInnerOuter || topXCounter < topXEntries ? (String) objects[1] : null; + final Long count = (Long) objects[2]; + + InnerOuter innerouter = map.get(dsName); + if (innerouter == null) { + topXCounter++; + innerouter = new InnerOuter(dsName); + map.put(new DSName(name), innerouter); + } + innerouter.addOuter(new DSName(version), count); + } + + for (final InnerOuter inner : map.values()) { + final List> outerReportItems = new ArrayList<>(); + + if (inner.name.getName() != null) { + for (final InnerOuter outer : inner.outer) { + outerReportItems.add(outer.toItem()); + } + } else { + outerReportItems.add(new DataReportSeriesItem("misc", inner.count)); + } + + innerOuterReport.add(new InnerOuterDataReportSeries( + new DataReportSeries<>("DS-Name", Collections.singletonList(inner.toItem())), + new DataReportSeries<>("DS-Version", outerReportItems))); + } + + return innerOuterReport; + } + + private static final class InnerOuter { + final DSName name; + long count; + final List outer; + + private InnerOuter(final DSName idName) { + name = idName; + outer = new ArrayList<>(); + } + + private InnerOuter(final DSName idName, final long count) { + name = idName; + this.count = count; + outer = new ArrayList<>(); + } + + private void addOuter(final DSName idName, final long count) { + outer.add(new InnerOuter(idName, count)); + this.count += count; + } + + private DataReportSeriesItem toItem() { + return new DataReportSeriesItem<>(name.getName() != null ? name.getName() : "misc", count); + } + } + + /** + * Object contains the name and the id of an entity. + * + */ + private static final class DSName { + + private final String name; + + /** + * @param id + * the ID of an entity + * @param name + * the name of an entity + */ + private DSName(final String name) { + this.name = name; + } + + /** + * @return the name + */ + private String getName() { + return name; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (name == null ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { // NOSONAR - as this is + // generated + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final DSName other = (DSName) obj; + if (name == null) { + if (other.name != null) { + return false; + } + } else if (!name.equals(other.name)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "DSName [name=" + name + "]"; + } + } + + private String dateTimeFormatToSqlFormat(final DateType datatype) { + switch (databaseType) { + case H2_DB_TYPE: + return datatype.h2Format(); + case MYSQL_DB_TYPE: + return datatype.mySqlFormat(); + default: + return null; + } + } + +} diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutGroupManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutGroupManagement.java new file mode 100644 index 000000000..3de83d381 --- /dev/null +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutGroupManagement.java @@ -0,0 +1,178 @@ +/** + * 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 java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.persistence.EntityManager; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.JoinType; +import javax.persistence.criteria.ListJoin; +import javax.persistence.criteria.Root; + +import org.eclipse.hawkbit.repository.RolloutGroupManagement; +import org.eclipse.hawkbit.repository.model.Action; +import org.eclipse.hawkbit.repository.model.Action_; +import org.eclipse.hawkbit.repository.model.Rollout; +import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus; +import org.eclipse.hawkbit.repository.model.RolloutGroup; +import org.eclipse.hawkbit.repository.model.RolloutGroup_; +import org.eclipse.hawkbit.repository.model.RolloutTargetGroup; +import org.eclipse.hawkbit.repository.model.RolloutTargetGroup_; +import org.eclipse.hawkbit.repository.model.Target; +import org.eclipse.hawkbit.repository.model.TargetWithActionStatus; +import org.eclipse.hawkbit.repository.model.Target_; +import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus; +import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +/** + * RolloutGroupManagement to control rollout groups. This service secures all + * the functionality based on the {@link PreAuthorize} annotation on methods. + */ +@Validated +@Service +@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) +public class JpaRolloutGroupManagement implements RolloutGroupManagement { + + @Autowired + private RolloutGroupRepository rolloutGroupRepository; + + @Autowired + private ActionRepository actionRepository; + + @Autowired + private TargetRepository targetRepository; + + @Autowired + private EntityManager entityManager; + + @Override + public RolloutGroup findRolloutGroupById(final Long rolloutGroupId) { + return rolloutGroupRepository.findOne(rolloutGroupId); + } + + @Override + public Page findRolloutGroupsByRolloutId(final Long rolloutId, final Pageable page) { + return rolloutGroupRepository.findByRolloutId(rolloutId, page); + } + + @Override + public Page findRolloutGroupsAll(final Rollout rollout, + final Specification specification, final Pageable page) { + return rolloutGroupRepository.findAll((root, query, criteriaBuilder) -> criteriaBuilder.and( + criteriaBuilder.equal(root.get(RolloutGroup_.rollout), rollout), + specification.toPredicate(root, query, criteriaBuilder)), page); + } + + @Override + public Page findAllRolloutGroupsWithDetailedStatus(final Long rolloutId, final Pageable page) { + final Page rolloutGroups = rolloutGroupRepository.findByRolloutId(rolloutId, page); + final List rolloutGroupIds = rolloutGroups.getContent().stream().map(rollout -> rollout.getId()) + .collect(Collectors.toList()); + final Map> allStatesForRollout = getStatusCountItemForRolloutGroup( + rolloutGroupIds); + + for (final RolloutGroup rolloutGroup : rolloutGroups) { + final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus( + allStatesForRollout.get(rolloutGroup.getId()), rolloutGroup.getTotalTargets()); + rolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus); + } + + return rolloutGroups; + } + + @Override + public RolloutGroup findRolloutGroupWithDetailedStatus(final Long rolloutGroupId) { + final RolloutGroup rolloutGroup = findRolloutGroupById(rolloutGroupId); + final List rolloutStatusCountItems = actionRepository + .getStatusCountByRolloutGroupId(rolloutGroupId); + + final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems, + rolloutGroup.getTotalTargets()); + rolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus); + return rolloutGroup; + + } + + private Map> getStatusCountItemForRolloutGroup( + final List rolloutGroupIds) { + final List resultList = actionRepository + .getStatusCountByRolloutGroupId(rolloutGroupIds); + return resultList.stream().collect(Collectors.groupingBy(TotalTargetCountActionStatus::getId)); + } + + @Override + public Page findRolloutGroupTargets(final RolloutGroup rolloutGroup, + final Specification specification, final Pageable page) { + return targetRepository.findAll((root, query, criteriaBuilder) -> { + final ListJoin rolloutTargetJoin = root.join(Target_.rolloutTargetGroup); + return criteriaBuilder.and(specification.toPredicate(root, query, criteriaBuilder), + criteriaBuilder.equal(rolloutTargetJoin.get(RolloutTargetGroup_.rolloutGroup), rolloutGroup)); + }, page); + } + + @Override + public Page findRolloutGroupTargets(final RolloutGroup rolloutGroup, final Pageable page) { + if (isRolloutStatusReady(rolloutGroup)) { + // in case of status ready the action has not been created yet and + // the relation information between target and rollout-group is + // stored in the #TargetRolloutGroup. + return targetRepository.findByRolloutTargetGroupRolloutGroupId(rolloutGroup.getId(), page); + } + return targetRepository.findByActionsRolloutGroup(rolloutGroup, page); + } + + private static boolean isRolloutStatusReady(final RolloutGroup rolloutGroup) { + return rolloutGroup != null && RolloutStatus.READY.equals(rolloutGroup.getRollout().getStatus()); + } + + @Override + public Page findAllTargetsWithActionStatus(final PageRequest pageRequest, + final RolloutGroup rolloutGroup) { + + final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + final CriteriaQuery query = cb.createQuery(Object[].class); + final CriteriaQuery countQuery = cb.createQuery(Long.class); + + final Root targetRoot = query.distinct(true).from(RolloutTargetGroup.class); + final Join targetJoin = targetRoot.join(RolloutTargetGroup_.target); + final ListJoin actionJoin = targetRoot.join(RolloutTargetGroup_.actions, + JoinType.LEFT); + + final Root countQueryFrom = countQuery.distinct(true).from(RolloutTargetGroup.class); + countQueryFrom.join(RolloutTargetGroup_.target); + countQueryFrom.join(RolloutTargetGroup_.actions, JoinType.LEFT); + countQuery.select(cb.count(countQueryFrom)) + .where(cb.equal(countQueryFrom.get(RolloutTargetGroup_.rolloutGroup), rolloutGroup)); + final Long totalCount = entityManager.createQuery(countQuery).getSingleResult(); + + final CriteriaQuery multiselect = query.multiselect(targetJoin, actionJoin.get(Action_.status)) + .where(cb.equal(targetRoot.get(RolloutTargetGroup_.rolloutGroup), rolloutGroup)); + final List targetWithActionStatus = entityManager.createQuery(multiselect) + .setFirstResult(pageRequest.getOffset()).setMaxResults(pageRequest.getPageSize()).getResultList() + .stream().map(o -> new TargetWithActionStatus((Target) o[0], (Action.Status) o[1])) + .collect(Collectors.toList()); + return new PageImpl<>(targetWithActionStatus, pageRequest, totalCount); + } +} diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java new file mode 100644 index 000000000..1f6e9b9a4 --- /dev/null +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java @@ -0,0 +1,653 @@ +/** + * 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 java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; +import java.util.stream.Collectors; + +import javax.persistence.EntityManager; + +import org.eclipse.hawkbit.cache.CacheWriteNotify; +import org.eclipse.hawkbit.repository.DeploymentManagement; +import org.eclipse.hawkbit.repository.RolloutManagement; +import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException; +import org.eclipse.hawkbit.repository.model.Action; +import org.eclipse.hawkbit.repository.model.Action.ActionType; +import org.eclipse.hawkbit.repository.model.DistributionSet; +import org.eclipse.hawkbit.repository.model.Rollout; +import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus; +import org.eclipse.hawkbit.repository.model.RolloutGroup; +import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupConditions; +import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorCondition; +import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus; +import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition; +import org.eclipse.hawkbit.repository.model.RolloutTargetGroup; +import org.eclipse.hawkbit.repository.model.Rollout_; +import org.eclipse.hawkbit.repository.model.Target; +import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus; +import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus; +import org.eclipse.hawkbit.rollout.condition.RolloutGroupActionEvaluator; +import org.eclipse.hawkbit.rollout.condition.RolloutGroupConditionEvaluator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.ApplicationContext; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Direction; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Service; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.DefaultTransactionDefinition; +import org.springframework.transaction.support.TransactionTemplate; +import org.springframework.util.Assert; +import org.springframework.validation.annotation.Validated; + +/** + * RolloutManagement to control rollouts e.g. like creating, starting, resuming + * and pausing rollouts. This service secures all the functionality based on the + * {@link PreAuthorize} annotation on methods. + */ +@Validated +@Service +@EnableScheduling +@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) +public class JpaRolloutManagement implements RolloutManagement { + private static final Logger LOGGER = LoggerFactory.getLogger(RolloutManagement.class); + + @Autowired + private EntityManager entityManager; + + @Autowired + private RolloutRepository rolloutRepository; + + @Autowired + private TargetManagement targetManagement; + + @Autowired + private TargetRepository targetRepository; + + @Autowired + private RolloutGroupRepository rolloutGroupRepository; + + @Autowired + private DeploymentManagement deploymentManagement; + + @Autowired + private RolloutTargetGroupRepository rolloutTargetGroupRepository; + + @Autowired + private ActionRepository actionRepository; + + @Autowired + private ApplicationContext context; + + @Autowired + private NoCountPagingRepository criteriaNoCountDao; + + @Autowired + private PlatformTransactionManager txManager; + + @Autowired + private CacheWriteNotify cacheWriteNotify; + + @Autowired + @Qualifier("asyncExecutor") + private Executor executor; + + /* + * set which stores the rollouts which are asynchronously creating. This is + * necessary to verify rollouts which maybe stuck during creationg e.g. + * because of database interruption, failures or even application crash. + * !This is not cluster aware! + */ + private static final Set creatingRollouts = ConcurrentHashMap.newKeySet(); + + /* + * set which stores the rollouts which are asynchronously starting. This is + * necessary to verify rollouts which maybe stuck during starting e.g. + * because of database interruption, failures or even application crash. + * !This is not cluster aware! + */ + private static final Set startingRollouts = ConcurrentHashMap.newKeySet(); + + @Override + public Page findAll(final Pageable page) { + return rolloutRepository.findAll(page); + } + + @Override + public Page findAllWithDetailedStatusByPredicate(final Specification specification, + final Pageable page) { + final Page findAll = rolloutRepository.findAll(specification, page); + setRolloutStatusDetails(findAll); + return findAll; + } + + @Override + public Rollout findRolloutById(final Long rolloutId) { + return rolloutRepository.findOne(rolloutId); + } + + @Override + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + @Modifying + public Rollout createRollout(final Rollout rollout, final int amountGroup, + final RolloutGroupConditions conditions) { + final Rollout savedRollout = createRollout(rollout, amountGroup); + return createRolloutGroups(amountGroup, conditions, savedRollout); + } + + @Override + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + @Modifying + public Rollout createRolloutAsync(final Rollout rollout, final int amountGroup, + final RolloutGroupConditions conditions) { + final Rollout savedRollout = createRollout(rollout, amountGroup); + creatingRollouts.add(savedRollout.getName()); + // need to flush the entity manager here to get the ID of the rollout, + // because entity manager is set to FlushMode#Auto, entitymanager will + // flush the Target entity, due the indirect relationship to the Rollout + // entity without set an ID JPA is throwing a Invalid + // 'org.springframework.dao.InvalidDataAccessApiUsageException: During + // synchronization aect was found through a relationship that was not + // marked cascade PERSIST' + entityManager.flush(); + executor.execute(() -> { + try { + createRolloutGroupsInNewTransaction(amountGroup, conditions, savedRollout); + } finally { + creatingRollouts.remove(savedRollout.getName()); + } + }); + return savedRollout; + } + + private Rollout createRollout(final Rollout rollout, final int amountGroup) { + verifyRolloutGroupParameter(amountGroup); + final Long totalTargets = targetManagement.countTargetByTargetFilterQuery(rollout.getTargetFilterQuery()); + rollout.setTotalTargets(totalTargets.longValue()); + return rolloutRepository.save(rollout); + } + + private static void verifyRolloutGroupParameter(final int amountGroup) { + if (amountGroup <= 0) { + throw new IllegalArgumentException("the amountGroup must be greater than zero"); + } else if (amountGroup > 500) { + throw new IllegalArgumentException("the amountGroup must not be greater than 500"); + } + } + + private Rollout createRolloutGroupsInNewTransaction(final int amountOfGroups, + final RolloutGroupConditions conditions, final Rollout savedRollout) { + final DefaultTransactionDefinition def = new DefaultTransactionDefinition(); + def.setName("creatingRollout"); + def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); + return new TransactionTemplate(txManager, def) + .execute(status -> createRolloutGroups(amountOfGroups, conditions, savedRollout)); + } + + /** + * Method for creating rollout groups and calculating group sizes. Group + * sizes are calculated by dividing the total count of targets through the + * amount of given groups. In same cases this will lead to less rollout + * groups than given by client. + * + * @param amountOfGroups + * the amount of groups + * @param conditions + * the rollout group conditions + * @param savedRollout + * the rollout + * @return the rollout with created groups + */ + private Rollout createRolloutGroups(final int amountOfGroups, final RolloutGroupConditions conditions, + final Rollout savedRollout) { + int pageIndex = 0; + int groupIndex = 0; + final Long totalCount = savedRollout.getTotalTargets(); + final int groupSize = (int) Math.ceil((double) totalCount / (double) amountOfGroups); + // validate if the amount of groups that will be created are the amount + // of groups that the client what's to have created. + int amountGroupValidated = amountOfGroups; + final int amountGroupCreation = (int) (Math.ceil((double) totalCount / (double) groupSize)); + if (amountGroupCreation == (amountOfGroups - 1)) { + amountGroupValidated--; + } + RolloutGroup lastSavedGroup = null; + while (pageIndex < totalCount) { + groupIndex++; + final String nameAndDesc = "group-" + groupIndex; + final RolloutGroup group = new RolloutGroup(); + group.setName(nameAndDesc); + group.setDescription(nameAndDesc); + group.setRollout(savedRollout); + group.setParent(lastSavedGroup); + group.setSuccessCondition(conditions.getSuccessCondition()); + group.setSuccessConditionExp(conditions.getSuccessConditionExp()); + group.setErrorCondition(conditions.getErrorCondition()); + group.setErrorConditionExp(conditions.getErrorConditionExp()); + group.setErrorAction(conditions.getErrorAction()); + group.setErrorActionExp(conditions.getErrorActionExp()); + + final RolloutGroup savedGroup = rolloutGroupRepository.save(group); + + final Slice targetGroup = targetManagement.findTargetsAll(savedRollout.getTargetFilterQuery(), + new OffsetBasedPageRequest(pageIndex, groupSize, new Sort(Direction.ASC, "id"))); + savedGroup.setTotalTargets(targetGroup.getContent().size()); + + lastSavedGroup = savedGroup; + + targetGroup + .forEach(target -> rolloutTargetGroupRepository.save(new RolloutTargetGroup(savedGroup, target))); + cacheWriteNotify.rolloutGroupCreated(groupIndex, savedRollout.getId(), savedGroup.getId(), + amountGroupValidated, groupIndex); + pageIndex += groupSize; + } + + savedRollout.setStatus(RolloutStatus.READY); + return rolloutRepository.save(savedRollout); + } + + @Override + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + @Modifying + public Rollout startRollout(final Rollout rollout) { + final Rollout mergedRollout = entityManager.merge(rollout); + checkIfRolloutCanStarted(rollout, mergedRollout); + return doStartRollout(mergedRollout); + } + + @Override + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + @Modifying + public Rollout startRolloutAsync(final Rollout rollout) { + final Rollout mergedRollout = entityManager.merge(rollout); + checkIfRolloutCanStarted(rollout, mergedRollout); + mergedRollout.setStatus(RolloutStatus.STARTING); + final Rollout updatedRollout = rolloutRepository.save(mergedRollout); + startingRollouts.add(updatedRollout.getName()); + executor.execute(() -> { + try { + final DefaultTransactionDefinition def = new DefaultTransactionDefinition(); + def.setName("startingRollout"); + def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); + new TransactionTemplate(txManager, def).execute(status -> { + doStartRollout(updatedRollout); + return null; + }); + } finally { + startingRollouts.remove(updatedRollout.getName()); + } + }); + return updatedRollout; + + } + + private Rollout doStartRollout(final Rollout rollout) { + final DistributionSet distributionSet = rollout.getDistributionSet(); + final ActionType actionType = rollout.getActionType(); + final long forceTime = rollout.getForcedTime(); + final List rolloutGroups = rolloutGroupRepository.findByRolloutOrderByIdAsc(rollout); + for (int iGroup = 0; iGroup < rolloutGroups.size(); iGroup++) { + final RolloutGroup rolloutGroup = rolloutGroups.get(iGroup); + final List targetGroup = targetRepository.findByRolloutTargetGroupRolloutGroup(rolloutGroup); + // firstgroup can already be started + if (iGroup == 0) { + final List targetsWithActionType = targetGroup.stream() + .map(t -> new TargetWithActionType(t.getControllerId(), actionType, forceTime)) + .collect(Collectors.toList()); + deploymentManagement.assignDistributionSet(distributionSet.getId(), targetsWithActionType, rollout, + rolloutGroup); + rolloutGroup.setStatus(RolloutGroupStatus.RUNNING); + } else { + // create only not active actions with status scheduled so they + // can be activated later + deploymentManagement.createScheduledAction(targetGroup, distributionSet, actionType, forceTime, rollout, + rolloutGroup); + rolloutGroup.setStatus(RolloutGroupStatus.SCHEDULED); + } + rolloutGroupRepository.save(rolloutGroup); + } + rollout.setStatus(RolloutStatus.RUNNING); + return rolloutRepository.save(rollout); + } + + @Override + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + @Modifying + public void pauseRollout(final Rollout rollout) { + final Rollout mergedRollout = entityManager.merge(rollout); + if (mergedRollout.getStatus() != RolloutStatus.RUNNING) { + throw new RolloutIllegalStateException("Rollout can only be paused in state running but current state is " + + rollout.getStatus().name().toLowerCase()); + } + // setting the complete rollout only in paused state. This is sufficient + // due the currently running groups will be completed and new groups are + // not started until rollout goes back to running state again. The + // periodically check for running rollouts will skip rollouts in pause + // state. + mergedRollout.setStatus(RolloutStatus.PAUSED); + rolloutRepository.save(mergedRollout); + } + + @Override + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + @Modifying + public void resumeRollout(final Rollout rollout) { + final Rollout mergedRollout = entityManager.merge(rollout); + if (!(RolloutStatus.PAUSED.equals(mergedRollout.getStatus()))) { + throw new RolloutIllegalStateException("Rollout can only be resumed in state paused but current state is " + + rollout.getStatus().name().toLowerCase()); + } + mergedRollout.setStatus(RolloutStatus.RUNNING); + rolloutRepository.save(mergedRollout); + } + + @Override + @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED) + @Modifying + public void checkRunningRollouts(final long delayBetweenChecks) { + verifyStuckedRollouts(); + final long lastCheck = System.currentTimeMillis(); + final int updated = rolloutRepository.updateLastCheck(lastCheck, delayBetweenChecks, RolloutStatus.RUNNING); + + if (updated == 0) { + // nothing to check, maybe another instance already checked in + // between + LOGGER.debug("No rolloutcheck necessary for current scheduled check {}, next check at {}", lastCheck, + lastCheck + delayBetweenChecks); + return; + } + + final List rolloutsToCheck = rolloutRepository.findByLastCheckAndStatus(lastCheck, + RolloutStatus.RUNNING); + LOGGER.info("Found {} running rollouts to check", rolloutsToCheck.size()); + + for (final Rollout rollout : rolloutsToCheck) { + LOGGER.debug("Checking rollout {}", rollout); + final List rolloutGroups = rolloutGroupRepository.findByRolloutAndStatus(rollout, + RolloutGroupStatus.RUNNING); + + if (rolloutGroups.isEmpty()) { + // no running rollouts, probably there was an error + // somewhere at the latest group. And the latest group has + // been switched from running into error state. So we need + // to find the latest group which + executeLatestRolloutGroup(rollout); + } else { + LOGGER.debug("Rollout {} has {} running groups", rollout.getId(), rolloutGroups.size()); + executeRolloutGroups(rollout, rolloutGroups); + } + + if (isRolloutComplete(rollout)) { + LOGGER.info("Rollout {} is finished, setting finished status", rollout); + rollout.setStatus(RolloutStatus.FINISHED); + rolloutRepository.save(rollout); + } + } + } + + /** + * Verifies and handles stucked rollouts in asynchronous creation or + * starting state. If rollouts are created or started asynchronously it + * might be that they keep in state {@link RolloutStatus#CREATING} or + * {@link RolloutStatus#STARTING} due database or application interruption. + * In case this happens, set the rollout to error state. + */ + private void verifyStuckedRollouts() { + final List rolloutsInCreatingState = rolloutRepository.findByStatus(RolloutStatus.CREATING); + rolloutsInCreatingState.stream().filter(rollout -> !creatingRollouts.contains(rollout.getName())) + .forEach(rollout -> { + LOGGER.warn( + "Determined error during rollout creation of rollout {}, stucking in creating state, setting to status", + rollout, RolloutStatus.ERROR_CREATING); + rollout.setStatus(RolloutStatus.ERROR_CREATING); + rolloutRepository.save(rollout); + }); + + final List rolloutsInStartingState = rolloutRepository.findByStatus(RolloutStatus.STARTING); + rolloutsInStartingState.stream().filter(rollout -> !startingRollouts.contains(rollout.getName())) + .forEach(rollout -> { + LOGGER.warn( + "Determined error during rollout starting of rollout {}, stucking in starting state, setting to status", + rollout, RolloutStatus.ERROR_STARTING); + rollout.setStatus(RolloutStatus.ERROR_STARTING); + rolloutRepository.save(rollout); + }); + + } + + private void executeRolloutGroups(final Rollout rollout, final List rolloutGroups) { + for (final RolloutGroup rolloutGroup : rolloutGroups) { + // error state check, do we need to stop the whole + // rollout because of error? + final RolloutGroupErrorCondition errorCondition = rolloutGroup.getErrorCondition(); + final boolean isError = checkErrorState(rollout, rolloutGroup, errorCondition); + if (isError) { + LOGGER.info("Rollout {} {} has error, calling error action", rollout.getName(), rollout.getId()); + callErrorAction(rollout, rolloutGroup); + } else { + // not in error so check finished state, do we need to + // start the next group? + final RolloutGroupSuccessCondition finishedCondition = rolloutGroup.getSuccessCondition(); + checkFinishCondition(rollout, rolloutGroup, finishedCondition); + if (isRolloutGroupComplete(rollout, rolloutGroup)) { + rolloutGroup.setStatus(RolloutGroupStatus.FINISHED); + rolloutGroupRepository.save(rolloutGroup); + } + } + } + } + + private void executeLatestRolloutGroup(final Rollout rollout) { + final List latestRolloutGroup = rolloutGroupRepository + .findByRolloutAndStatusNotOrderByIdDesc(rollout, RolloutGroupStatus.SCHEDULED); + if (latestRolloutGroup.isEmpty()) { + return; + } + executeRolloutGroupSuccessAction(rollout, latestRolloutGroup.get(0)); + } + + private void callErrorAction(final Rollout rollout, final RolloutGroup rolloutGroup) { + try { + context.getBean(rolloutGroup.getErrorAction().getBeanName(), RolloutGroupActionEvaluator.class) + .eval(rollout, rolloutGroup, rolloutGroup.getErrorActionExp()); + } catch (final BeansException e) { + LOGGER.error("Something bad happend when accessing the error action bean {}", + rolloutGroup.getErrorAction().getBeanName(), e); + } + } + + private boolean isRolloutComplete(final Rollout rollout) { + final Long groupsActiveLeft = rolloutGroupRepository.countByRolloutAndStatusOrStatus(rollout, + RolloutGroupStatus.RUNNING, RolloutGroupStatus.SCHEDULED); + return groupsActiveLeft == 0; + } + + private boolean isRolloutGroupComplete(final Rollout rollout, final RolloutGroup rolloutGroup) { + final Long actionsLeftForRollout = actionRepository + .countByRolloutAndRolloutGroupAndStatusNotAndStatusNotAndStatusNot(rollout, rolloutGroup, + Action.Status.ERROR, Action.Status.FINISHED, Action.Status.CANCELED); + return actionsLeftForRollout == 0; + } + + private boolean checkErrorState(final Rollout rollout, final RolloutGroup rolloutGroup, + final RolloutGroupErrorCondition errorCondition) { + if (errorCondition == null) { + // there is no error condition, so return false, don't have error. + return false; + } + try { + return context.getBean(errorCondition.getBeanName(), RolloutGroupConditionEvaluator.class).eval(rollout, + rolloutGroup, rolloutGroup.getErrorConditionExp()); + } catch (final BeansException e) { + LOGGER.error("Something bad happend when accessing the error condition bean {}", + errorCondition.getBeanName(), e); + return false; + } + } + + private boolean checkFinishCondition(final Rollout rollout, final RolloutGroup rolloutGroup, + final RolloutGroupSuccessCondition finishCondition) { + LOGGER.trace("Checking finish condition {} on rolloutgroup {}", finishCondition, rolloutGroup); + try { + final boolean isFinished = context + .getBean(finishCondition.getBeanName(), RolloutGroupConditionEvaluator.class) + .eval(rollout, rolloutGroup, rolloutGroup.getSuccessConditionExp()); + if (isFinished) { + LOGGER.info("Rolloutgroup {} is finished, starting next group", rolloutGroup); + executeRolloutGroupSuccessAction(rollout, rolloutGroup); + } else { + LOGGER.debug("Rolloutgroup {} is still running", rolloutGroup); + } + return isFinished; + } catch (final BeansException e) { + LOGGER.error("Something bad happend when accessing the finish condition bean {}", + finishCondition.getBeanName(), e); + return false; + } + } + + private void executeRolloutGroupSuccessAction(final Rollout rollout, final RolloutGroup rolloutGroup) { + context.getBean(rolloutGroup.getSuccessAction().getBeanName(), RolloutGroupActionEvaluator.class).eval(rollout, + rolloutGroup, rolloutGroup.getSuccessActionExp()); + } + + @Override + public Long countRolloutsAll() { + return rolloutRepository.count(); + } + + @Override + public Long countRolloutsAllByFilters(final String searchText) { + return rolloutRepository.count(likeNameOrDescription(searchText)); + } + + private static Specification likeNameOrDescription(final String searchText) { + return (rolloutRoot, query, criteriaBuilder) -> { + final String searchTextToLower = searchText.toLowerCase(); + return criteriaBuilder.or( + criteriaBuilder.like(criteriaBuilder.lower(rolloutRoot.get(Rollout_.name)), searchTextToLower), + criteriaBuilder.like(criteriaBuilder.lower(rolloutRoot.get(Rollout_.description)), + searchTextToLower)); + }; + } + + @Override + public Slice findRolloutByFilters(final Pageable pageable, final String searchText) { + final Specification specs = likeNameOrDescription(searchText); + final Slice findAll = criteriaNoCountDao.findAll(specs, pageable, Rollout.class); + setRolloutStatusDetails(findAll); + return findAll; + } + + @Override + public Rollout findRolloutByName(final String rolloutName) { + return rolloutRepository.findByName(rolloutName); + } + + /** + * Update rollout details. + * + * @param rollout + * rollout to be updated + * + * @return Rollout updated rollout + */ + @Override + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + @Modifying + public Rollout updateRollout(final Rollout rollout) { + Assert.notNull(rollout.getId()); + return rolloutRepository.save(rollout); + } + + /** + * Get count of targets in different status in rollout. + * + * @param page + * the page request to sort and limit the result + * @return a list of rollouts with details of targets count for different + * statuses + * + */ + @Override + public Page findAllRolloutsWithDetailedStatus(final Pageable page) { + final Page rollouts = findAll(page); + setRolloutStatusDetails(rollouts); + return rollouts; + + } + + @Override + public Rollout findRolloutWithDetailedStatus(final Long rolloutId) { + final Rollout rollout = findRolloutById(rolloutId); + final List rolloutStatusCountItems = actionRepository + .getStatusCountByRolloutId(rolloutId); + final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems, + rollout.getTotalTargets()); + rollout.setTotalTargetCountStatus(totalTargetCountStatus); + return rollout; + } + + private Map> getStatusCountItemForRollout(final List rolloutIds) { + final List resultList = actionRepository.getStatusCountByRolloutId(rolloutIds); + return resultList.stream().collect(Collectors.groupingBy(TotalTargetCountActionStatus::getId)); + } + + private void setRolloutStatusDetails(final Slice rollouts) { + final List rolloutIds = rollouts.getContent().stream().map(rollout -> rollout.getId()) + .collect(Collectors.toList()); + final Map> allStatesForRollout = getStatusCountItemForRollout( + rolloutIds); + + for (final Rollout rollout : rollouts) { + final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus( + allStatesForRollout.get(rollout.getId()), rollout.getTotalTargets()); + rollout.setTotalTargetCountStatus(totalTargetCountStatus); + } + } + + private static void checkIfRolloutCanStarted(final Rollout rollout, final Rollout mergedRollout) { + if (!(RolloutStatus.READY.equals(mergedRollout.getStatus()))) { + throw new RolloutIllegalStateException("Rollout can only be started in state ready but current state is " + + rollout.getStatus().name().toLowerCase()); + } + } + + @Override + public float getFinishedPercentForRunningGroup(final Long rolloutId, final RolloutGroup rolloutGroup) { + final Long totalGroup = rolloutGroup.getTotalTargets(); + final Long finished = actionRepository.countByRolloutIdAndRolloutGroupIdAndStatus(rolloutId, + rolloutGroup.getId(), Action.Status.FINISHED); + if (totalGroup == 0) { + // in case e.g. targets has been deleted we don't have any actions + // left for this group, so the group is finished + return 100; + } + // calculate threshold + return ((float) finished / (float) totalGroup) * 100; + } +} diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java new file mode 100644 index 000000000..5cc27d75c --- /dev/null +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java @@ -0,0 +1,170 @@ +/** + * 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 org.eclipse.hawkbit.repository.TenantConfigurationManagement; +import org.eclipse.hawkbit.repository.model.TenantConfiguration; +import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; +import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; +import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationValidatorException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.context.ApplicationContext; +import org.springframework.context.EnvironmentAware; +import org.springframework.core.convert.support.ConfigurableConversionService; +import org.springframework.core.convert.support.DefaultConversionService; +import org.springframework.core.env.Environment; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +/** + * Central tenant configuration management operations of the SP server. + */ +@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) +@Validated +@Service +public class JpaTenantConfigurationManagement implements EnvironmentAware, TenantConfigurationManagement { + + @Autowired + private TenantConfigurationRepository tenantConfigurationRepository; + + @Autowired + private ApplicationContext applicationContext; + + private static final ConfigurableConversionService conversionService = new DefaultConversionService(); + + private Environment environment; + + @Override + @Cacheable(value = "tenantConfiguration", key = "#configurationKey.getKeyName()") + public TenantConfigurationValue getConfigurationValue(final TenantConfigurationKey configurationKey, + final Class propertyType) { + validateTenantConfigurationDataType(configurationKey, propertyType); + + final TenantConfiguration tenantConfiguration = tenantConfigurationRepository + .findByKey(configurationKey.getKeyName()); + + return buildTenantConfigurationValueByKey(configurationKey, propertyType, tenantConfiguration); + } + + /** + * Validates the data type of the tenant configuration. If it is possible to + * cast to the given data type. + * + * @param configurationKey + * the key + * @param propertyType + * the class + */ + static void validateTenantConfigurationDataType(final TenantConfigurationKey configurationKey, + final Class propertyType) { + if (!configurationKey.getDataType().isAssignableFrom(propertyType)) { + throw new TenantConfigurationValidatorException( + String.format("Cannot parse the database value of type %s into the type %s.", + configurationKey.getDataType(), propertyType)); + } + } + + @Override + public TenantConfigurationValue buildTenantConfigurationValueByKey( + final TenantConfigurationKey configurationKey, final Class propertyType, + final TenantConfiguration tenantConfiguration) { + if (tenantConfiguration != null) { + return TenantConfigurationValue. builder().isGlobal(false).createdBy(tenantConfiguration.getCreatedBy()) + .createdAt(tenantConfiguration.getCreatedAt()) + .lastModifiedAt(tenantConfiguration.getLastModifiedAt()) + .lastModifiedBy(tenantConfiguration.getLastModifiedBy()) + .value(conversionService.convert(tenantConfiguration.getValue(), propertyType)).build(); + + } else if (configurationKey.getDefaultKeyName() != null) { + + return TenantConfigurationValue. builder().isGlobal(true).createdBy(null).createdAt(null) + .lastModifiedAt(null).lastModifiedBy(null) + .value(getGlobalConfigurationValue(configurationKey, propertyType)).build(); + } + return null; + } + + @Override + public TenantConfigurationValue getConfigurationValue(final TenantConfigurationKey configurationKey) { + return getConfigurationValue(configurationKey, configurationKey.getDataType()); + } + + @Override + public T getGlobalConfigurationValue(final TenantConfigurationKey configurationKey, + final Class propertyType) { + + if (!configurationKey.getDataType().isAssignableFrom(propertyType)) { + throw new TenantConfigurationValidatorException( + String.format("Cannot parse the database value of type %s into the type %s.", + configurationKey.getDataType(), propertyType)); + } + + final T valueInProperties = environment.getProperty(configurationKey.getDefaultKeyName(), propertyType); + + if (valueInProperties == null) { + return conversionService.convert(configurationKey.getDefaultValue(), propertyType); + } + + return valueInProperties; + } + + @Override + @CacheEvict(value = "tenantConfiguration", key = "#configurationKey.getKeyName()") + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + @Modifying + public TenantConfigurationValue addOrUpdateConfiguration(final TenantConfigurationKey configurationKey, + final T value) { + + if (!configurationKey.getDataType().isAssignableFrom(value.getClass())) { + throw new TenantConfigurationValidatorException(String.format( + "Cannot parse the value %s of type %s into the type %s defined by the configuration key.", value, + value.getClass(), configurationKey.getDataType())); + } + + configurationKey.validate(applicationContext, value); + + TenantConfiguration tenantConfiguration = tenantConfigurationRepository + .findByKey(configurationKey.getKeyName()); + + if (tenantConfiguration == null) { + tenantConfiguration = new TenantConfiguration(configurationKey.getKeyName(), value.toString()); + } else { + tenantConfiguration.setValue(value.toString()); + } + + final TenantConfiguration updatedTenantConfiguration = tenantConfigurationRepository.save(tenantConfiguration); + + final Class clazzT = (Class) value.getClass(); + + return TenantConfigurationValue. builder().isGlobal(false) + .createdBy(updatedTenantConfiguration.getCreatedBy()) + .createdAt(updatedTenantConfiguration.getCreatedAt()) + .lastModifiedAt(updatedTenantConfiguration.getLastModifiedAt()) + .lastModifiedBy(updatedTenantConfiguration.getLastModifiedBy()) + .value(conversionService.convert(updatedTenantConfiguration.getValue(), clazzT)).build(); + } + + @Override + @CacheEvict(value = "tenantConfiguration", key = "#configurationKey.getKeyName()") + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + @Modifying + public void deleteConfiguration(final TenantConfigurationKey configurationKey) { + tenantConfigurationRepository.deleteByKey(configurationKey.getKeyName()); + } + + @Override + public void setEnvironment(final Environment environment) { + this.environment = environment; + } +} diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/LocalArtifactRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/LocalArtifactRepository.java similarity index 98% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/LocalArtifactRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/LocalArtifactRepository.java index 4c106957d..fcad668d2 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/LocalArtifactRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/LocalArtifactRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import java.util.List; import java.util.Optional; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/NoCountPagingRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/NoCountPagingRepository.java similarity index 98% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/NoCountPagingRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/NoCountPagingRepository.java index 0624f269a..c0f8b89ff 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/NoCountPagingRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/NoCountPagingRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import java.io.Serializable; import java.util.List; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/OffsetBasedPageRequest.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/OffsetBasedPageRequest.java similarity index 98% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/OffsetBasedPageRequest.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/OffsetBasedPageRequest.java index 753ec6963..80bb85461 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/OffsetBasedPageRequest.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/OffsetBasedPageRequest.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutGroupRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/RolloutGroupRepository.java similarity index 99% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutGroupRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/RolloutGroupRepository.java index e0cb4cc27..2433eb8aa 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutGroupRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/RolloutGroupRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import java.util.List; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/RolloutRepository.java similarity index 98% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/RolloutRepository.java index 5a22f5ab5..212672419 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/RolloutRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import java.util.List; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutTargetGroupRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/RolloutTargetGroupRepository.java similarity index 95% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutTargetGroupRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/RolloutTargetGroupRepository.java index 14d9b8353..2e7eac55d 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/RolloutTargetGroupRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/RolloutTargetGroupRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import org.eclipse.hawkbit.repository.model.RolloutTargetGroup; import org.eclipse.hawkbit.repository.model.RolloutTargetGroupId; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SoftwareManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/SoftwareManagement.java similarity index 99% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SoftwareManagement.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/SoftwareManagement.java index d719667ed..813f2e5a6 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SoftwareManagement.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/SoftwareManagement.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import static com.google.common.base.Preconditions.checkNotNull; @@ -27,6 +27,7 @@ import javax.persistence.criteria.Root; import javax.validation.constraints.NotNull; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; +import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.model.CustomSoftwareModule; @@ -307,7 +308,7 @@ public class SoftwareManagement { private void deleteGridFsArtifacts(final SoftwareModule swModule) { for (final LocalArtifact localArtifact : swModule.getLocalArtifacts()) { - artifactManagement.deleteGridFsArtifact(localArtifact); + artifactManagement.deleteLocalArtifact(localArtifact); } } diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleMetadataRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/SoftwareModuleMetadataRepository.java similarity index 97% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleMetadataRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/SoftwareModuleMetadataRepository.java index c40a96e92..8023a9361 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleMetadataRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/SoftwareModuleMetadataRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import java.util.List; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/SoftwareModuleRepository.java similarity index 99% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/SoftwareModuleRepository.java index 95b01c270..e03b3e084 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/SoftwareModuleRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import java.util.List; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleTypeRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/SoftwareModuleTypeRepository.java similarity index 97% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleTypeRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/SoftwareModuleTypeRepository.java index ac9425a08..fe2cfadf4 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleTypeRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/SoftwareModuleTypeRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.springframework.data.domain.Page; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SystemManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/SystemManagement.java similarity index 99% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SystemManagement.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/SystemManagement.java index 49c9183a7..67f7bf93f 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SystemManagement.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/SystemManagement.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import java.lang.reflect.Method; import java.math.BigDecimal; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TagManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TagManagement.java similarity index 99% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TagManagement.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TagManagement.java index 8db9773e6..dbca65a00 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TagManagement.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TagManagement.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import static com.google.common.base.Preconditions.checkNotNull; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetFilterQueryManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryManagement.java similarity index 99% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetFilterQueryManagement.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryManagement.java index 4f7c91da3..18ab0c486 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetFilterQueryManagement.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryManagement.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import java.util.ArrayList; import java.util.List; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetFilterQueryRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryRepository.java similarity index 96% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetFilterQueryRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryRepository.java index 3604785cd..346fe2ec2 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetFilterQueryRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetFilterQueryRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.springframework.data.domain.Page; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetInfoRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetInfoRepository.java similarity index 98% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetInfoRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetInfoRepository.java index 436f1e7b6..1c3378bba 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetInfoRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetInfoRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import java.util.Collection; import java.util.List; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetManagement.java similarity index 99% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetManagement.java index 4d6540acf..01d6eb527 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetManagement.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import java.net.URI; import java.util.ArrayList; @@ -33,6 +33,7 @@ import org.eclipse.hawkbit.Constants; import org.eclipse.hawkbit.eventbus.event.TargetTagAssigmentResultEvent; import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; +import org.eclipse.hawkbit.repository.TargetFields; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet_; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetRepository.java similarity index 99% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetRepository.java index 5e37902ab..b3d390016 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import java.util.Collection; import java.util.List; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetTagRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetTagRepository.java similarity index 97% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetTagRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetTagRepository.java index e0f03ee78..92f48d699 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetTagRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetTagRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import java.util.List; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetWithActionType.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetWithActionType.java similarity index 97% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetWithActionType.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetWithActionType.java index 18efa23a9..a2c0a7d8f 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TargetWithActionType.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetWithActionType.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.ActionType; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TenantConfigurationRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TenantConfigurationRepository.java similarity index 97% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TenantConfigurationRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TenantConfigurationRepository.java index 0497ea924..6494aafea 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TenantConfigurationRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TenantConfigurationRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import java.util.List; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TenantKeyGenerator.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TenantKeyGenerator.java similarity index 95% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TenantKeyGenerator.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TenantKeyGenerator.java index 3265b622c..0478d99d7 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TenantKeyGenerator.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TenantKeyGenerator.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import java.lang.reflect.Method; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TenantMetaDataRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TenantMetaDataRepository.java similarity index 97% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TenantMetaDataRepository.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TenantMetaDataRepository.java index 4aa4c48dc..63c213d2c 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TenantMetaDataRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TenantMetaDataRepository.java @@ -6,7 +6,7 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import java.util.List; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TenantStatsManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TenantStatsManagement.java similarity index 82% rename from hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TenantStatsManagement.java rename to hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TenantStatsManagement.java index c124037fa..451a6e401 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/TenantStatsManagement.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/jpa/TenantStatsManagement.java @@ -6,13 +6,16 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ -package org.eclipse.hawkbit.repository; +package org.eclipse.hawkbit.repository.jpa; import java.util.Optional; +import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; import org.eclipse.hawkbit.report.model.TenantUsage; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; @@ -43,7 +46,8 @@ public class TenantStatsManagement { * to collect for * @return collected statistics */ - @Transactional(propagation = Propagation.REQUIRES_NEW) + @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED) + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN) public TenantUsage getStatsOfTenant(final String tenant) { final TenantUsage result = new TenantUsage(tenant); diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/helper/SystemManagementHolder.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/helper/SystemManagementHolder.java index d1327c690..fd1d81280 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/helper/SystemManagementHolder.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/helper/SystemManagementHolder.java @@ -8,7 +8,7 @@ */ package org.eclipse.hawkbit.repository.model.helper; -import org.eclipse.hawkbit.repository.SystemManagement; +import org.eclipse.hawkbit.repository.jpa.SystemManagement; import org.springframework.beans.factory.annotation.Autowired; /** diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/rollout/condition/PauseRolloutGroupAction.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/rollout/condition/PauseRolloutGroupAction.java index af335cd0d..3af269819 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/rollout/condition/PauseRolloutGroupAction.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/rollout/condition/PauseRolloutGroupAction.java @@ -10,8 +10,8 @@ package org.eclipse.hawkbit.rollout.condition; import java.util.concurrent.Callable; -import org.eclipse.hawkbit.repository.RolloutGroupRepository; import org.eclipse.hawkbit.repository.RolloutManagement; +import org.eclipse.hawkbit.repository.jpa.RolloutGroupRepository; import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/rollout/condition/StartNextGroupRolloutGroupSuccessAction.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/rollout/condition/StartNextGroupRolloutGroupSuccessAction.java index 21c499d31..224901053 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/rollout/condition/StartNextGroupRolloutGroupSuccessAction.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/rollout/condition/StartNextGroupRolloutGroupSuccessAction.java @@ -11,7 +11,7 @@ package org.eclipse.hawkbit.rollout.condition; import java.util.List; import org.eclipse.hawkbit.repository.DeploymentManagement; -import org.eclipse.hawkbit.repository.RolloutGroupRepository; +import org.eclipse.hawkbit.repository.jpa.RolloutGroupRepository; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.RolloutGroup; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/rollout/condition/ThresholdRolloutGroupErrorCondition.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/rollout/condition/ThresholdRolloutGroupErrorCondition.java index 6e63efa8e..e38cb0f16 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/rollout/condition/ThresholdRolloutGroupErrorCondition.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/rollout/condition/ThresholdRolloutGroupErrorCondition.java @@ -8,7 +8,7 @@ */ package org.eclipse.hawkbit.rollout.condition; -import org.eclipse.hawkbit.repository.ActionRepository; +import org.eclipse.hawkbit.repository.jpa.ActionRepository; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.RolloutGroup; diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/rollout/condition/ThresholdRolloutGroupSuccessCondition.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/rollout/condition/ThresholdRolloutGroupSuccessCondition.java index fcf9762c6..5f9167931 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/rollout/condition/ThresholdRolloutGroupSuccessCondition.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/rollout/condition/ThresholdRolloutGroupSuccessCondition.java @@ -8,7 +8,7 @@ */ package org.eclipse.hawkbit.rollout.condition; -import org.eclipse.hawkbit.repository.ActionRepository; +import org.eclipse.hawkbit.repository.jpa.ActionRepository; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.RolloutGroup; diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/AbstractIntegrationTest.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/AbstractIntegrationTest.java index b7091edf8..99b93926f 100644 --- a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/AbstractIntegrationTest.java +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/AbstractIntegrationTest.java @@ -16,34 +16,34 @@ import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.eclipse.hawkbit.cache.TenantAwareCacheManager; -import org.eclipse.hawkbit.repository.ActionRepository; -import org.eclipse.hawkbit.repository.ActionStatusRepository; import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ControllerManagement; import org.eclipse.hawkbit.repository.DeploymentManagement; import org.eclipse.hawkbit.repository.DistributionSetManagement; -import org.eclipse.hawkbit.repository.DistributionSetRepository; -import org.eclipse.hawkbit.repository.DistributionSetTagRepository; -import org.eclipse.hawkbit.repository.DistributionSetTypeRepository; -import org.eclipse.hawkbit.repository.ExternalArtifactRepository; -import org.eclipse.hawkbit.repository.LocalArtifactRepository; import org.eclipse.hawkbit.repository.RolloutGroupManagement; -import org.eclipse.hawkbit.repository.RolloutGroupRepository; import org.eclipse.hawkbit.repository.RolloutManagement; -import org.eclipse.hawkbit.repository.RolloutRepository; -import org.eclipse.hawkbit.repository.SoftwareManagement; -import org.eclipse.hawkbit.repository.SoftwareModuleMetadataRepository; -import org.eclipse.hawkbit.repository.SoftwareModuleRepository; -import org.eclipse.hawkbit.repository.SoftwareModuleTypeRepository; -import org.eclipse.hawkbit.repository.SystemManagement; -import org.eclipse.hawkbit.repository.TagManagement; -import org.eclipse.hawkbit.repository.TargetFilterQueryManagement; -import org.eclipse.hawkbit.repository.TargetInfoRepository; -import org.eclipse.hawkbit.repository.TargetManagement; -import org.eclipse.hawkbit.repository.TargetRepository; -import org.eclipse.hawkbit.repository.TargetTagRepository; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; -import org.eclipse.hawkbit.repository.TenantMetaDataRepository; +import org.eclipse.hawkbit.repository.jpa.ActionRepository; +import org.eclipse.hawkbit.repository.jpa.ActionStatusRepository; +import org.eclipse.hawkbit.repository.jpa.DistributionSetRepository; +import org.eclipse.hawkbit.repository.jpa.DistributionSetTagRepository; +import org.eclipse.hawkbit.repository.jpa.DistributionSetTypeRepository; +import org.eclipse.hawkbit.repository.jpa.ExternalArtifactRepository; +import org.eclipse.hawkbit.repository.jpa.LocalArtifactRepository; +import org.eclipse.hawkbit.repository.jpa.RolloutGroupRepository; +import org.eclipse.hawkbit.repository.jpa.RolloutRepository; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; +import org.eclipse.hawkbit.repository.jpa.SoftwareModuleMetadataRepository; +import org.eclipse.hawkbit.repository.jpa.SoftwareModuleRepository; +import org.eclipse.hawkbit.repository.jpa.SoftwareModuleTypeRepository; +import org.eclipse.hawkbit.repository.jpa.SystemManagement; +import org.eclipse.hawkbit.repository.jpa.TagManagement; +import org.eclipse.hawkbit.repository.jpa.TargetFilterQueryManagement; +import org.eclipse.hawkbit.repository.jpa.TargetInfoRepository; +import org.eclipse.hawkbit.repository.jpa.TargetManagement; +import org.eclipse.hawkbit.repository.jpa.TargetRepository; +import org.eclipse.hawkbit.repository.jpa.TargetTagRepository; +import org.eclipse.hawkbit.repository.jpa.TenantMetaDataRepository; import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.utils.RepositoryDataGenerator.DatabaseCleanupUtil; diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/TestDataUtil.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/TestDataUtil.java index 1787d29e4..11106eaf1 100644 --- a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/TestDataUtil.java +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/TestDataUtil.java @@ -18,8 +18,8 @@ import java.util.UUID; import org.apache.commons.io.IOUtils; import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.DistributionSetManagement; -import org.eclipse.hawkbit.repository.SoftwareManagement; -import org.eclipse.hawkbit.repository.TargetManagement; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; +import org.eclipse.hawkbit.repository.jpa.TargetManagement; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetTag; import org.eclipse.hawkbit.repository.model.DistributionSetType; diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/ControllerManagementTest.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/ControllerManagementTest.java index 279932ef7..4c0bc1c19 100644 --- a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/ControllerManagementTest.java +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/ControllerManagementTest.java @@ -58,14 +58,14 @@ public class ControllerManagementTest extends AbstractIntegrationTest { System.currentTimeMillis()); actionStatusMessage.addMessage("foobar"); savedAction.setStatus(Status.RUNNING); - controllerManagament.addUpdateActionStatus(actionStatusMessage, savedAction); + controllerManagament.addUpdateActionStatus(actionStatusMessage); assertThat(targetManagement.findTargetByControllerID("4712").getTargetInfo().getUpdateStatus()) .isEqualTo(TargetUpdateStatus.PENDING); actionStatusMessage = new ActionStatus(savedAction, Action.Status.FINISHED, System.currentTimeMillis()); actionStatusMessage.addMessage(RandomStringUtils.randomAscii(512)); savedAction.setStatus(Status.FINISHED); - controllerManagament.addUpdateActionStatus(actionStatusMessage, savedAction); + controllerManagament.addUpdateActionStatus(actionStatusMessage); assertThat(targetManagement.findTargetByControllerID("4712").getTargetInfo().getUpdateStatus()) .isEqualTo(TargetUpdateStatus.IN_SYNC); @@ -111,21 +111,21 @@ public class ControllerManagementTest extends AbstractIntegrationTest { final ActionStatus actionStatusMessage = new ActionStatus(savedAction, Action.Status.RUNNING, System.currentTimeMillis()); actionStatusMessage.addMessage("running"); - savedAction = controllerManagament.addUpdateActionStatus(actionStatusMessage, savedAction); + savedAction = controllerManagament.addUpdateActionStatus(actionStatusMessage); assertThat(targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus()) .isEqualTo(TargetUpdateStatus.PENDING); final ActionStatus actionStatusMessage2 = new ActionStatus(savedAction, Action.Status.ERROR, System.currentTimeMillis()); actionStatusMessage2.addMessage("error"); - savedAction = controllerManagament.addUpdateActionStatus(actionStatusMessage2, savedAction); + savedAction = controllerManagament.addUpdateActionStatus(actionStatusMessage2); assertThat(targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus()) .isEqualTo(TargetUpdateStatus.ERROR); final ActionStatus actionStatusMessage3 = new ActionStatus(savedAction, Action.Status.FINISHED, System.currentTimeMillis()); actionStatusMessage3.addMessage("finish"); - controllerManagament.addUpdateActionStatus(actionStatusMessage3, savedAction); + controllerManagament.addUpdateActionStatus(actionStatusMessage3); targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus(); diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/DeploymentManagementTest.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/DeploymentManagementTest.java index a17054a3d..6a6d92bf0 100644 --- a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/DeploymentManagementTest.java +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/DeploymentManagementTest.java @@ -27,6 +27,7 @@ import org.eclipse.hawkbit.eventbus.event.CancelTargetAssignmentEvent; import org.eclipse.hawkbit.eventbus.event.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.exception.ForceQuitActionNotAllowedException; import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException; +import org.eclipse.hawkbit.repository.jpa.TargetRepository; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.ActionType; import org.eclipse.hawkbit.repository.model.Action.Status; @@ -104,7 +105,7 @@ public class DeploymentManagementTest extends AbstractIntegrationTest { @Test @Description("Ensures that distribution sets can assigned and unassigned to a distribution set tag. Not exists distribution set will be ignored for the assignment.") public void assignAndUnassignDistributionSetToTag() { - final List assignDS = new ArrayList(); + final List assignDS = new ArrayList<>(); for (int i = 0; i < 4; i++) { assignDS.add(TestDataUtil.generateDistributionSet("DS" + i, "1.0", softwareManagement, distributionSetManagement, new ArrayList()).getId()); @@ -199,8 +200,8 @@ public class DeploymentManagementTest extends AbstractIntegrationTest { secondAction = deploymentManagement.findActionWithDetails(secondAction.getId()); // confirm cancellation secondAction.setStatus(Status.CANCELED); - controllerManagement.addCancelActionStatus( - new ActionStatus(secondAction, Status.CANCELED, System.currentTimeMillis()), secondAction); + controllerManagement + .addCancelActionStatus(new ActionStatus(secondAction, Status.CANCELED, System.currentTimeMillis())); assertThat(actionStatusRepository.findAll()).as("wrong size of actions status").hasSize(4); assertThat(targetManagement.findTargetByControllerID("4712").getAssignedDistributionSet()).as("wrong ds") .isEqualTo(dsFirst); @@ -213,8 +214,8 @@ public class DeploymentManagementTest extends AbstractIntegrationTest { firstAction = deploymentManagement.findActionWithDetails(firstAction.getId()); // confirm cancellation firstAction.setStatus(Status.CANCELED); - controllerManagement.addCancelActionStatus( - new ActionStatus(firstAction, Status.CANCELED, System.currentTimeMillis()), firstAction); + controllerManagement + .addCancelActionStatus(new ActionStatus(firstAction, Status.CANCELED, System.currentTimeMillis())); assertThat(actionStatusRepository.findAll()).as("wrong size of action status").hasSize(6); assertThat(targetManagement.findTargetByControllerID("4712").getAssignedDistributionSet()) .as("wrong assigned ds").isEqualTo(dsInstalled); @@ -256,8 +257,8 @@ public class DeploymentManagementTest extends AbstractIntegrationTest { // confirm cancellation firstAction = deploymentManagement.findActionWithDetails(firstAction.getId()); firstAction.setStatus(Status.CANCELED); - controllerManagement.addCancelActionStatus( - new ActionStatus(firstAction, Status.CANCELED, System.currentTimeMillis()), firstAction); + controllerManagement + .addCancelActionStatus(new ActionStatus(firstAction, Status.CANCELED, System.currentTimeMillis())); assertThat(actionStatusRepository.findAll()).as("wrong size of action status").hasSize(4); assertThat(targetManagement.findTargetByControllerID("4712").getAssignedDistributionSet()) .as("wrong assigned ds").isEqualTo(dsSecond); @@ -273,8 +274,8 @@ public class DeploymentManagementTest extends AbstractIntegrationTest { .as("wrong assigned ds").isEqualTo(dsSecond); // confirm cancellation secondAction.setStatus(Status.CANCELED); - controllerManagement.addCancelActionStatus( - new ActionStatus(secondAction, Status.CANCELED, System.currentTimeMillis()), secondAction); + controllerManagement + .addCancelActionStatus(new ActionStatus(secondAction, Status.CANCELED, System.currentTimeMillis())); // cancelled success -> back to dsInstalled assertThat(targetManagement.findTargetByControllerID("4712").getAssignedDistributionSet()) .as("wrong installed ds").isEqualTo(dsInstalled); @@ -742,7 +743,7 @@ public class DeploymentManagementTest extends AbstractIntegrationTest { for (final String msg : msgs) { statusMessages.addMessage(msg); } - controllerManagament.addUpdateActionStatus(statusMessages, updActA); + controllerManagament.addUpdateActionStatus(statusMessages); return targetManagement.findTargetByControllerID(t.getControllerId()); } @@ -786,7 +787,7 @@ public class DeploymentManagementTest extends AbstractIntegrationTest { final Action action = updAct.getContent().get(0); action.setStatus(Status.FINISHED); final ActionStatus statusMessage = new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), ""); - controllerManagament.addUpdateActionStatus(statusMessage, action); + controllerManagament.addUpdateActionStatus(statusMessage); targ = targetManagement.findTargetByControllerID(targ.getControllerId()); diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/DistributionSetManagementTest.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/DistributionSetManagementTest.java index db9618425..8faae175e 100644 --- a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/DistributionSetManagementTest.java +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/DistributionSetManagementTest.java @@ -821,7 +821,7 @@ public class DistributionSetManagementTest extends AbstractIntegrationTest { for (final String msg : msgs) { statusMessages.addMessage(msg); } - controllerManagament.addUpdateActionStatus(statusMessages, updActA); + controllerManagament.addUpdateActionStatus(statusMessages); return targetManagement.findTargetByControllerID(t.getControllerId()); } diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/ReportManagementTest.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/ReportManagementTest.java index 0184a9d26..e41491962 100644 --- a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/ReportManagementTest.java +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/ReportManagementTest.java @@ -558,7 +558,7 @@ public class ReportManagementTest extends AbstractIntegrationTest { for (final String msg : msgs) { statusMessages.addMessage(msg); } - controllerManagament.addUpdateActionStatus(statusMessages, updActA); + controllerManagament.addUpdateActionStatus(statusMessages); return targetManagement.findTargetByControllerID(t.getControllerId()); } diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/RolloutManagementTest.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/RolloutManagementTest.java index 22f9ee8b4..88bb26616 100644 --- a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/RolloutManagementTest.java +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/RolloutManagementTest.java @@ -18,6 +18,7 @@ import java.util.concurrent.Callable; import org.eclipse.hawkbit.AbstractIntegrationTest; import org.eclipse.hawkbit.TestDataUtil; +import org.eclipse.hawkbit.repository.jpa.OffsetBasedPageRequest; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.ActionStatus; @@ -136,8 +137,8 @@ public class RolloutManagementTest extends AbstractIntegrationTest { // 50% final Action action = runningActions.get(0); action.setStatus(Status.FINISHED); - controllerManagament.addUpdateActionStatus( - new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), ""), action); + controllerManagament + .addUpdateActionStatus(new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), "")); // check running rollouts again, now the finish condition should be hit // and should start the next group @@ -178,8 +179,8 @@ public class RolloutManagementTest extends AbstractIntegrationTest { // finish actions with error for (final Action action : runningActions) { action.setStatus(Status.ERROR); - controllerManagament.addUpdateActionStatus( - new ActionStatus(action, Status.ERROR, System.currentTimeMillis(), ""), action); + controllerManagament + .addUpdateActionStatus(new ActionStatus(action, Status.ERROR, System.currentTimeMillis(), "")); } // check running rollouts again, now the error condition should be hit @@ -220,8 +221,8 @@ public class RolloutManagementTest extends AbstractIntegrationTest { // finish actions with error for (final Action action : runningActions) { action.setStatus(Status.ERROR); - controllerManagament.addUpdateActionStatus( - new ActionStatus(action, Status.ERROR, System.currentTimeMillis(), ""), action); + controllerManagament + .addUpdateActionStatus(new ActionStatus(action, Status.ERROR, System.currentTimeMillis(), "")); } // check running rollouts again, now the error condition should be hit @@ -977,8 +978,8 @@ public class RolloutManagementTest extends AbstractIntegrationTest { final List runningActions = deploymentManagement.findActionsByRolloutAndStatus(rollout, Status.RUNNING); for (final Action action : runningActions) { action.setStatus(status); - controllerManagament.addUpdateActionStatus(new ActionStatus(action, status, System.currentTimeMillis(), ""), - action); + controllerManagament + .addUpdateActionStatus(new ActionStatus(action, status, System.currentTimeMillis(), "")); } return runningActions.size(); } @@ -989,14 +990,13 @@ public class RolloutManagementTest extends AbstractIntegrationTest { assertThat(runningActions.size()).isGreaterThanOrEqualTo(amountOfTargetsToGetChanged); for (int i = 0; i < amountOfTargetsToGetChanged; i++) { controllerManagament.addUpdateActionStatus( - new ActionStatus(runningActions.get(i), status, System.currentTimeMillis(), ""), - runningActions.get(i)); + new ActionStatus(runningActions.get(i), status, System.currentTimeMillis(), "")); } return runningActions.size(); } private Map createInitStatusMap() { - final Map map = new HashMap(); + final Map map = new HashMap<>(); for (final TotalTargetCountStatus.Status status : TotalTargetCountStatus.Status.values()) { map.put(status, 0L); } diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/TagManagementTest.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/TagManagementTest.java index 4c6af0d83..3deba1a56 100644 --- a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/TagManagementTest.java +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/TagManagementTest.java @@ -21,6 +21,7 @@ import org.eclipse.hawkbit.AbstractIntegrationTest; import org.eclipse.hawkbit.TestDataUtil; import org.eclipse.hawkbit.repository.DistributionSetFilter.DistributionSetFilterBuilder; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; +import org.eclipse.hawkbit.repository.jpa.TagManagement; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetTag; import org.eclipse.hawkbit.repository.model.DistributionSetTagAssignmentResult; diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/TargetFilterQueryManagenmentTest.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/TargetFilterQueryManagenmentTest.java index 99e08f331..37d4ea683 100644 --- a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/TargetFilterQueryManagenmentTest.java +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/TargetFilterQueryManagenmentTest.java @@ -13,6 +13,7 @@ import static org.junit.Assert.fail; import org.eclipse.hawkbit.AbstractIntegrationTest; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; +import org.eclipse.hawkbit.repository.jpa.TargetFilterQueryManagement; import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.junit.Test; diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/TargetManagementSearchTest.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/TargetManagementSearchTest.java index 73d549eb7..6b509b257 100644 --- a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/TargetManagementSearchTest.java +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/TargetManagementSearchTest.java @@ -96,7 +96,7 @@ public class TargetManagementSearchTest extends AbstractIntegrationTest { final Action action = deploymentManagement.findActionWithDetails(actionId); action.setStatus(Status.FINISHED); controllerManagament.addUpdateActionStatus( - new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), "message"), action); + new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), "message")); deploymentManagement.assignDistributionSet(setA.getId(), installedC); final List unknown = new ArrayList<>(); @@ -745,7 +745,7 @@ public class TargetManagementSearchTest extends AbstractIntegrationTest { final Action action = deploymentManagement.findActionWithDetails(actionId); action.setStatus(Status.FINISHED); controllerManagament.addUpdateActionStatus( - new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), "message"), action); + new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), "message")); }); deploymentManagement.assignDistributionSet(assignedSet, assignedtargets); @@ -773,7 +773,7 @@ public class TargetManagementSearchTest extends AbstractIntegrationTest { final Action action = deploymentManagement.findActionWithDetails(actionId); action.setStatus(Status.FINISHED); controllerManagament.addUpdateActionStatus( - new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), "message"), action); + new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), "message")); }); deploymentManagement.assignDistributionSet(assignedSet, installedtargets); @@ -802,7 +802,7 @@ public class TargetManagementSearchTest extends AbstractIntegrationTest { final Action action = deploymentManagement.findActionWithDetails(actionId); action.setStatus(Status.FINISHED); controllerManagament.addUpdateActionStatus( - new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), "message"), action); + new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), "message")); }); deploymentManagement.assignDistributionSet(assignedSet, installedtargets); @@ -840,7 +840,7 @@ public class TargetManagementSearchTest extends AbstractIntegrationTest { for (final String msg : msgs) { statusMessages.addMessage(msg); } - controllerManagament.addUpdateActionStatus(statusMessages, updActA); + controllerManagament.addUpdateActionStatus(statusMessages); return targetManagement.findTargetByControllerID(t.getControllerId()); } diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/TargetManagementTest.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/TargetManagementTest.java index a223c25b4..ae4447fc6 100644 --- a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/TargetManagementTest.java +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/TargetManagementTest.java @@ -187,7 +187,7 @@ public class TargetManagementTest extends AbstractIntegrationTest { final Action action = deploymentManagement.findActionWithDetails(result.getActions().get(0)); action.setStatus(Status.FINISHED); controllerManagament.addUpdateActionStatus( - new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), "message"), action); + new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), "message")); deploymentManagement.assignDistributionSet(set2.getId(), "4711"); target = targetManagement.findTargetByControllerIDWithDetails("4711"); diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/rsql/RSQLRolloutGroupFields.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/rsql/RSQLRolloutGroupFields.java index 78d333826..18da430c7 100644 --- a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/rsql/RSQLRolloutGroupFields.java +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/rsql/RSQLRolloutGroupFields.java @@ -75,7 +75,7 @@ public class RSQLRolloutGroupFields extends AbstractIntegrationTest { } private void assertRSQLQuery(final String rsqlParam, final long expcetedTargets) { - final Page findTargetPage = rolloutGroupManagement.findRolloutGroupsByPredicate(rollout, + final Page findTargetPage = rolloutGroupManagement.findRolloutGroupsAll(rollout, RSQLUtility.parse(rsqlParam, RolloutGroupFields.class), new PageRequest(0, 100)); final long countTargetsAll = findTargetPage.getTotalElements(); assertThat(findTargetPage).isNotNull(); diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/utils/RepositoryDataGenerator.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/utils/RepositoryDataGenerator.java index a1c74e791..e2e86bed1 100644 --- a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/utils/RepositoryDataGenerator.java +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/utils/RepositoryDataGenerator.java @@ -30,9 +30,9 @@ import org.eclipse.hawkbit.repository.ControllerManagement; import org.eclipse.hawkbit.repository.DeploymentManagement; import org.eclipse.hawkbit.repository.DistributionSetAssignmentResult; import org.eclipse.hawkbit.repository.DistributionSetManagement; -import org.eclipse.hawkbit.repository.SoftwareManagement; -import org.eclipse.hawkbit.repository.TagManagement; -import org.eclipse.hawkbit.repository.TargetManagement; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; +import org.eclipse.hawkbit.repository.jpa.TagManagement; +import org.eclipse.hawkbit.repository.jpa.TargetManagement; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.ActionStatus; @@ -207,14 +207,14 @@ public final class RepositoryDataGenerator { download.setAction(action); download.setStatus(Status.DOWNLOAD); download.addMessage("Controller started download."); - action = controllerManagement.addUpdateActionStatus(download, action); + action = controllerManagement.addUpdateActionStatus(download); // warning final ActionStatus warning = new ActionStatus(); warning.setAction(action); warning.setStatus(Status.WARNING); warning.addMessage("Some warning: " + jlorem.words(new Random().nextInt(50))); - action = controllerManagement.addUpdateActionStatus(warning, action); + action = controllerManagement.addUpdateActionStatus(warning); // garbage for (int i = 0; i < new Random().nextInt(10); i++) { @@ -222,13 +222,13 @@ public final class RepositoryDataGenerator { running.setAction(action); running.setStatus(Status.RUNNING); running.addMessage("Still running: " + jlorem.words(new Random().nextInt(50))); - action = controllerManagement.addUpdateActionStatus(running, action); + action = controllerManagement.addUpdateActionStatus(running); for (int g = 0; g < new Random().nextInt(5); g++) { final ActionStatus rand = new ActionStatus(); rand.setAction(action); rand.setStatus(Status.RUNNING); rand.addMessage(jlorem.words(new Random().nextInt(50))); - action = controllerManagement.addUpdateActionStatus(rand, action); + action = controllerManagement.addUpdateActionStatus(rand); } } @@ -241,13 +241,13 @@ public final class RepositoryDataGenerator { if (incrementAndGet % 5 == 0) { close.setStatus(Status.ERROR); close.addMessage("Controller reported CLOSED with ERROR!"); - action = controllerManagement.addUpdateActionStatus(close, action); + action = controllerManagement.addUpdateActionStatus(close); } // with OK else { close.setStatus(Status.FINISHED); close.addMessage("Controller reported CLOSED with OK!"); - action = controllerManagement.addUpdateActionStatus(close, action); + action = controllerManagement.addUpdateActionStatus(close); } index++; @@ -266,7 +266,7 @@ public final class RepositoryDataGenerator { close.setAction(action); close.setStatus(Status.FINISHED); close.addMessage("Controller reported CLOSED with OK!"); - action = controllerManagement.addUpdateActionStatus(close, action); + action = controllerManagement.addUpdateActionStatus(close); } } @@ -339,8 +339,8 @@ public final class RepositoryDataGenerator { // garbage DS LOG.debug("initDemoRepo - start now DS garbage"); - TestDataUtil.generateDistributionSets("Generic Software Package", sizeMultiplikator, - softwareManagement, distributionSetManagement); + TestDataUtil.generateDistributionSets("Generic Software Package", sizeMultiplikator, softwareManagement, + distributionSetManagement); LOG.debug("initDemoRepo - DS garbage finished"); LOG.debug("initDemoRepo - start now Extra Software Modules and types"); diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/controller/ArtifactStoreController.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/controller/ArtifactStoreController.java index a36443024..d6e057edd 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/controller/ArtifactStoreController.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/controller/ArtifactStoreController.java @@ -149,7 +149,7 @@ public class ArtifactStoreController { actionStatus.addMessage( ControllerManagement.SERVER_MESSAGE_PREFIX + "Target downloads: " + request.getRequestURI()); } - controllerManagement.addActionStatusMessage(actionStatus); + controllerManagement.addInformationalActionStatus(actionStatus); return action; } diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/controller/RootController.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/controller/RootController.java index 14383f30b..9dfcbae55 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/controller/RootController.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/controller/RootController.java @@ -30,8 +30,8 @@ import org.eclipse.hawkbit.controller.model.DeploymentBase; import org.eclipse.hawkbit.controller.model.Result.FinalResult; import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ControllerManagement; -import org.eclipse.hawkbit.repository.SoftwareManagement; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.ActionStatus; @@ -150,7 +150,7 @@ public class RootController { return new ResponseEntity<>( DataConversionHelper.fromTarget(target, controllerManagement.findActionByTargetAndActive(target), - controllerManagement.findPollingTime(), tenantAware), + controllerManagement.getPollingTime(), tenantAware), HttpStatus.OK); } @@ -222,7 +222,7 @@ public class RootController { statusMessage.addMessage( ControllerManagement.SERVER_MESSAGE_PREFIX + "Target downloads " + request.getRequestURI()); } - controllerManagement.addActionStatusMessage(statusMessage); + controllerManagement.addInformationalActionStatus(statusMessage); return action; } @@ -371,8 +371,7 @@ public class RootController { return new ResponseEntity<>(HttpStatus.GONE); } - controllerManagement.addUpdateActionStatus(generateUpdateStatus(feedback, targetid, feedback.getId(), action), - action); + controllerManagement.addUpdateActionStatus(generateUpdateStatus(feedback, targetid, feedback.getId(), action)); return new ResponseEntity<>(HttpStatus.OK); @@ -546,7 +545,7 @@ public class RootController { } controllerManagement - .addCancelActionStatus(generateActionCancelStatus(feedback, target, feedback.getId(), action), action); + .addCancelActionStatus(generateActionCancelStatus(feedback, target, feedback.getId(), action)); return new ResponseEntity<>(HttpStatus.OK); } diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetMapper.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetMapper.java index 8051146a1..95e3f0c8d 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetMapper.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetMapper.java @@ -16,8 +16,8 @@ import java.util.List; import org.eclipse.hawkbit.repository.DistributionSetAssignmentResult; import org.eclipse.hawkbit.repository.DistributionSetManagement; -import org.eclipse.hawkbit.repository.SoftwareManagement; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetMetadata; import org.eclipse.hawkbit.repository.model.DistributionSetType; diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetResource.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetResource.java index 27231308b..9e9dc2369 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetResource.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetResource.java @@ -18,12 +18,12 @@ import org.eclipse.hawkbit.repository.DistributionSetAssignmentResult; import org.eclipse.hawkbit.repository.DistributionSetFields; import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.DistributionSetMetadataFields; -import org.eclipse.hawkbit.repository.SoftwareManagement; -import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.TargetFields; -import org.eclipse.hawkbit.repository.TargetManagement; -import org.eclipse.hawkbit.repository.TargetWithActionType; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; +import org.eclipse.hawkbit.repository.jpa.SystemManagement; +import org.eclipse.hawkbit.repository.jpa.TargetManagement; +import org.eclipse.hawkbit.repository.jpa.TargetWithActionType; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetMetadata; import org.eclipse.hawkbit.repository.model.DsMetadataCompositeKey; diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetTagResource.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetTagResource.java index eaa0fec28..e630003c5 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetTagResource.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetTagResource.java @@ -13,8 +13,8 @@ import java.util.stream.Collectors; import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.TagFields; -import org.eclipse.hawkbit.repository.TagManagement; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.jpa.TagManagement; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetTag; import org.eclipse.hawkbit.repository.model.DistributionSetTagAssignmentResult; diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetTypeMapper.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetTypeMapper.java index 409caf01d..fbe22af5c 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetTypeMapper.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetTypeMapper.java @@ -14,8 +14,8 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; import java.util.ArrayList; import java.util.List; -import org.eclipse.hawkbit.repository.SoftwareManagement; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.rest.resource.api.DistributionSetTypeRestApi; diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetTypeResource.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetTypeResource.java index f7346a6ff..32e87e05e 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetTypeResource.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DistributionSetTypeResource.java @@ -12,8 +12,8 @@ import java.util.List; import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.DistributionSetTypeFields; -import org.eclipse.hawkbit.repository.SoftwareManagement; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.SoftwareModule; @@ -69,7 +69,7 @@ public class DistributionSetTypeResource implements DistributionSetTypeRestApi { final Slice findModuleTypessAll; Long countModulesAll; if (rsqlParam != null) { - findModuleTypessAll = distributionSetManagement.findDistributionSetTypesByPredicate( + findModuleTypessAll = distributionSetManagement.findDistributionSetTypesAll( RSQLUtility.parse(rsqlParam, DistributionSetTypeFields.class), pageable); countModulesAll = ((Page) findModuleTypessAll).getTotalElements(); } else { diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DownloadArtifactResource.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DownloadArtifactResource.java index 00f8f4756..5c057acd4 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DownloadArtifactResource.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/DownloadArtifactResource.java @@ -13,8 +13,8 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.hawkbit.artifact.repository.model.DbArtifact; import org.eclipse.hawkbit.repository.ArtifactManagement; -import org.eclipse.hawkbit.repository.SoftwareManagement; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; import org.eclipse.hawkbit.repository.model.LocalArtifact; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.rest.resource.helper.RestResourceConversionHelper; diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/RolloutResource.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/RolloutResource.java index 78cfefa0d..d8821f113 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/RolloutResource.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/RolloutResource.java @@ -178,7 +178,7 @@ public class RolloutResource implements RolloutRestApi { final Page findRolloutGroupsAll; if (rsqlParam != null) { - findRolloutGroupsAll = this.rolloutGroupManagement.findRolloutGroupsByPredicate(rollout, + findRolloutGroupsAll = this.rolloutGroupManagement.findRolloutGroupsAll(rollout, RSQLUtility.parse(rsqlParam, RolloutGroupFields.class), pageable); } else { findRolloutGroupsAll = this.rolloutGroupManagement.findRolloutGroupsByRolloutId(rolloutId, pageable); diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleMapper.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleMapper.java index 01dfae9cd..8732cc1d1 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleMapper.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleMapper.java @@ -14,8 +14,8 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; import java.util.ArrayList; import java.util.List; -import org.eclipse.hawkbit.repository.SoftwareManagement; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.LocalArtifact; import org.eclipse.hawkbit.repository.model.SoftwareModule; diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleResource.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleResource.java index 830443cbd..0d7a4b8f0 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleResource.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleResource.java @@ -12,10 +12,10 @@ import java.io.IOException; import java.util.List; import org.eclipse.hawkbit.repository.ArtifactManagement; -import org.eclipse.hawkbit.repository.SoftwareManagement; import org.eclipse.hawkbit.repository.SoftwareModuleFields; import org.eclipse.hawkbit.repository.SoftwareModuleMetadataFields; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata; diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleTypeResource.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleTypeResource.java index e3498f141..df042d38c 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleTypeResource.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleTypeResource.java @@ -10,9 +10,9 @@ package org.eclipse.hawkbit.rest.resource; import java.util.List; -import org.eclipse.hawkbit.repository.SoftwareManagement; import org.eclipse.hawkbit.repository.SoftwareModuleTypeFields; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SystemManagementResource.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SystemManagementResource.java index 27cfdb924..c5663f9f5 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SystemManagementResource.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SystemManagementResource.java @@ -16,8 +16,8 @@ import java.util.stream.Collectors; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; import org.eclipse.hawkbit.report.model.SystemUsageReport; import org.eclipse.hawkbit.report.model.TenantUsage; -import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; +import org.eclipse.hawkbit.repository.jpa.SystemManagement; import org.eclipse.hawkbit.rest.resource.model.systemmanagement.CacheRest; import org.eclipse.hawkbit.rest.resource.model.systemmanagement.SystemStatisticsRest; import org.eclipse.hawkbit.rest.resource.model.systemmanagement.TenantSystemUsageRest; diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetResource.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetResource.java index 4b068a41e..df779c688 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetResource.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetResource.java @@ -19,8 +19,8 @@ import org.eclipse.hawkbit.repository.ActionFields; import org.eclipse.hawkbit.repository.ActionStatusFields; import org.eclipse.hawkbit.repository.DeploymentManagement; import org.eclipse.hawkbit.repository.TargetFields; -import org.eclipse.hawkbit.repository.TargetManagement; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.jpa.TargetManagement; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.ActionType; import org.eclipse.hawkbit.repository.model.ActionStatus; diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetTagResource.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetTagResource.java index 1a9155828..b6946145d 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetTagResource.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/TargetTagResource.java @@ -12,9 +12,9 @@ import java.util.List; import java.util.stream.Collectors; import org.eclipse.hawkbit.repository.TagFields; -import org.eclipse.hawkbit.repository.TagManagement; -import org.eclipse.hawkbit.repository.TargetManagement; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.jpa.TagManagement; +import org.eclipse.hawkbit.repository.jpa.TargetManagement; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetTag; import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult; diff --git a/hawkbit-rest-resource/src/test/java/org/eclipse/hawkbit/rest/resource/DistributionSetResourceTest.java b/hawkbit-rest-resource/src/test/java/org/eclipse/hawkbit/rest/resource/DistributionSetResourceTest.java index 6254da992..9d823e832 100644 --- a/hawkbit-rest-resource/src/test/java/org/eclipse/hawkbit/rest/resource/DistributionSetResourceTest.java +++ b/hawkbit-rest-resource/src/test/java/org/eclipse/hawkbit/rest/resource/DistributionSetResourceTest.java @@ -30,12 +30,12 @@ import org.eclipse.hawkbit.AbstractIntegrationTest; import org.eclipse.hawkbit.MockMvcResultPrinter; import org.eclipse.hawkbit.TestDataUtil; import org.eclipse.hawkbit.WithUser; -import org.eclipse.hawkbit.repository.ActionRepository; import org.eclipse.hawkbit.repository.ControllerManagement; import org.eclipse.hawkbit.repository.DistributionSetManagement; -import org.eclipse.hawkbit.repository.SoftwareManagement; -import org.eclipse.hawkbit.repository.TargetManagement; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.jpa.ActionRepository; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; +import org.eclipse.hawkbit.repository.jpa.TargetManagement; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.ActionStatus; @@ -890,7 +890,7 @@ public class DistributionSetResourceTest extends AbstractIntegrationTest { for (final String msg : msgs) { statusMessages.addMessage(msg); } - controllerManagament.addUpdateActionStatus(statusMessages, updActA); + controllerManagament.addUpdateActionStatus(statusMessages); return targetManagement.findTargetByControllerID(t.getControllerId()); } diff --git a/hawkbit-rest-resource/src/test/java/org/eclipse/hawkbit/rest/resource/TargetResourceTest.java b/hawkbit-rest-resource/src/test/java/org/eclipse/hawkbit/rest/resource/TargetResourceTest.java index 8d27d5114..2d3a9707e 100644 --- a/hawkbit-rest-resource/src/test/java/org/eclipse/hawkbit/rest/resource/TargetResourceTest.java +++ b/hawkbit-rest-resource/src/test/java/org/eclipse/hawkbit/rest/resource/TargetResourceTest.java @@ -108,8 +108,7 @@ public class TargetResourceTest extends AbstractIntegrationTest { final List actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId); actions.get(0).setStatus(Status.FINISHED); controllerManagament.addUpdateActionStatus( - new ActionStatus(actions.get(0), Status.FINISHED, System.currentTimeMillis(), "testmessage"), - actions.get(0)); + new ActionStatus(actions.get(0), Status.FINISHED, System.currentTimeMillis(), "testmessage")); final PageRequest pageRequest = new PageRequest(0, 1000, Direction.ASC, ActionFields.ID.getFieldName()); final ActionStatus status = deploymentManagement @@ -1284,8 +1283,8 @@ public class TargetResourceTest extends AbstractIntegrationTest { final Pageable pageReq = new PageRequest(0, 100); final Action action = actionRepository.findByDistributionSet(pageReq, savedSet).getContent().get(0); - final ActionStatus actionStatus = new ActionStatus(action, Status.FINISHED, 0l); - controllerManagement.addUpdateActionStatus(actionStatus, action); + final ActionStatus actionStatus = new ActionStatus(action, Status.FINISHED, 0L); + controllerManagement.addUpdateActionStatus(actionStatus); } /** diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactBeanQuery.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactBeanQuery.java index 2fa3763fd..5e7c0f3c7 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactBeanQuery.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactBeanQuery.java @@ -12,7 +12,7 @@ import java.util.List; import java.util.Map; import org.eclipse.hawkbit.repository.ArtifactManagement; -import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; +import org.eclipse.hawkbit.repository.jpa.OffsetBasedPageRequest; import org.eclipse.hawkbit.repository.model.LocalArtifact; import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/footer/UploadViewConfirmationWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/footer/UploadViewConfirmationWindowLayout.java index e6284d5d6..364b659da 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/footer/UploadViewConfirmationWindowLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/footer/UploadViewConfirmationWindowLayout.java @@ -14,7 +14,7 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.eclipse.hawkbit.repository.SoftwareManagement; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; import org.eclipse.hawkbit.ui.artifacts.event.UploadArtifactUIEvent; import org.eclipse.hawkbit.ui.artifacts.state.ArtifactUploadState; import org.eclipse.hawkbit.ui.artifacts.state.CustomFile; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/BaseSwModuleBeanQuery.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/BaseSwModuleBeanQuery.java index 29bea30eb..d1d034555 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/BaseSwModuleBeanQuery.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/BaseSwModuleBeanQuery.java @@ -12,8 +12,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; -import org.eclipse.hawkbit.repository.SoftwareManagement; +import org.eclipse.hawkbit.repository.jpa.OffsetBasedPageRequest; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.ui.common.UserDetailsFormatter; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleAddUpdateWindow.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleAddUpdateWindow.java index 1e4029087..6a033bfd8 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleAddUpdateWindow.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleAddUpdateWindow.java @@ -10,7 +10,7 @@ package org.eclipse.hawkbit.ui.artifacts.smtable; import java.io.Serializable; -import org.eclipse.hawkbit.repository.SoftwareManagement; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleEvent; import org.eclipse.hawkbit.ui.common.SoftwareModuleTypeBeanQuery; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTable.java index 478438891..4fea3df08 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTable.java @@ -12,7 +12,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.eclipse.hawkbit.repository.SoftwareManagement; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.ui.artifacts.event.SMFilterEvent; import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleEvent; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/CreateUpdateSoftwareTypeLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/CreateUpdateSoftwareTypeLayout.java index f2e43c1fb..4039a2be2 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/CreateUpdateSoftwareTypeLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/CreateUpdateSoftwareTypeLayout.java @@ -13,8 +13,8 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import org.eclipse.hawkbit.repository.SoftwareManagement; import org.eclipse.hawkbit.repository.SpPermissionChecker; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleTypeEvent; import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleTypeEvent.SoftwareModuleTypeEnum; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/SMTypeFilterButtonClick.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/SMTypeFilterButtonClick.java index 4a4006c2a..4727ecae7 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/SMTypeFilterButtonClick.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtype/SMTypeFilterButtonClick.java @@ -10,7 +10,7 @@ package org.eclipse.hawkbit.ui.artifacts.smtype; import java.io.Serializable; -import org.eclipse.hawkbit.repository.SoftwareManagement; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.ui.artifacts.event.SMFilterEvent; import org.eclipse.hawkbit.ui.artifacts.state.ArtifactUploadState; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/DistributionSetTypeBeanQuery.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/DistributionSetTypeBeanQuery.java index b09255bc7..b8f5435dd 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/DistributionSetTypeBeanQuery.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/DistributionSetTypeBeanQuery.java @@ -13,7 +13,7 @@ import java.util.List; import java.util.Map; import org.eclipse.hawkbit.repository.DistributionSetManagement; -import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; +import org.eclipse.hawkbit.repository.jpa.OffsetBasedPageRequest; import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; import org.eclipse.hawkbit.ui.utils.SpringContextHelper; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/SoftwareModuleTypeBeanQuery.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/SoftwareModuleTypeBeanQuery.java index 6fd382084..f8d6dba66 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/SoftwareModuleTypeBeanQuery.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/SoftwareModuleTypeBeanQuery.java @@ -11,8 +11,8 @@ package org.eclipse.hawkbit.ui.common; import java.util.List; import java.util.Map; -import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; -import org.eclipse.hawkbit.repository.SoftwareManagement; +import org.eclipse.hawkbit.repository.jpa.OffsetBasedPageRequest; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; import org.eclipse.hawkbit.ui.utils.SpringContextHelper; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/tagdetails/AbstractTargetTagToken.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/tagdetails/AbstractTargetTagToken.java index ce9f101f3..1e3ab9b03 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/tagdetails/AbstractTargetTagToken.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/tagdetails/AbstractTargetTagToken.java @@ -10,7 +10,7 @@ package org.eclipse.hawkbit.ui.common.tagdetails; import org.eclipse.hawkbit.eventbus.event.TargetTagCreatedBulkEvent; import org.eclipse.hawkbit.eventbus.event.TargetTagDeletedEvent; -import org.eclipse.hawkbit.repository.TagManagement; +import org.eclipse.hawkbit.repository.jpa.TagManagement; import org.eclipse.hawkbit.repository.model.BaseEntity; import org.eclipse.hawkbit.repository.model.TargetTag; import org.springframework.beans.factory.annotation.Autowired; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/tagdetails/DistributionTagToken.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/tagdetails/DistributionTagToken.java index 178610c23..bc46f50fd 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/tagdetails/DistributionTagToken.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/tagdetails/DistributionTagToken.java @@ -19,7 +19,7 @@ import org.eclipse.hawkbit.eventbus.event.DistributionSetTagCreatedBulkEvent; import org.eclipse.hawkbit.eventbus.event.DistributionSetTagDeletedEvent; import org.eclipse.hawkbit.eventbus.event.DistributionSetTagUpdateEvent; import org.eclipse.hawkbit.repository.DistributionSetManagement; -import org.eclipse.hawkbit.repository.TagManagement; +import org.eclipse.hawkbit.repository.jpa.TagManagement; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetTag; import org.eclipse.hawkbit.repository.model.DistributionSetTagAssignmentResult; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/tagdetails/TargetTagToken.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/tagdetails/TargetTagToken.java index 6f03c5860..e607c99d2 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/tagdetails/TargetTagToken.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/tagdetails/TargetTagToken.java @@ -16,7 +16,7 @@ import java.util.stream.Collectors; import org.eclipse.hawkbit.eventbus.event.TargetTagAssigmentResultEvent; import org.eclipse.hawkbit.eventbus.event.TargetTagUpdateEvent; -import org.eclipse.hawkbit.repository.TargetManagement; +import org.eclipse.hawkbit.repository.jpa.TargetManagement; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetTag; import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/disttype/CreateUpdateDistSetTypeLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/disttype/CreateUpdateDistSetTypeLayout.java index 64a06bd38..0e3ed8da3 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/disttype/CreateUpdateDistSetTypeLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/disttype/CreateUpdateDistSetTypeLayout.java @@ -14,9 +14,9 @@ import java.util.List; import java.util.Set; import org.eclipse.hawkbit.repository.DistributionSetManagement; -import org.eclipse.hawkbit.repository.DistributionSetRepository; -import org.eclipse.hawkbit.repository.SoftwareManagement; import org.eclipse.hawkbit.repository.SpPermissionChecker; +import org.eclipse.hawkbit.repository.jpa.DistributionSetRepository; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.ui.common.CoordinatesToColor; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java index a1d1ffcfe..32bb0ce86 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java @@ -14,7 +14,7 @@ import java.util.Map; import java.util.Set; import org.eclipse.hawkbit.repository.DistributionSetManagement; -import org.eclipse.hawkbit.repository.SoftwareManagement; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetIdName; import org.eclipse.hawkbit.repository.model.SoftwareModule; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetTable.java index 01c242247..ca469c967 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetTable.java @@ -18,10 +18,9 @@ import java.util.Map.Entry; import java.util.Set; import org.eclipse.hawkbit.repository.DistributionSetManagement; -import org.eclipse.hawkbit.repository.SoftwareManagement; import org.eclipse.hawkbit.repository.SpPermissionChecker; -import org.eclipse.hawkbit.repository.TargetManagement; -import org.eclipse.hawkbit.repository.exception.EntityLockedException; +import org.eclipse.hawkbit.repository.jpa.SoftwareManagement; +import org.eclipse.hawkbit.repository.jpa.TargetManagement; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetIdName; import org.eclipse.hawkbit.repository.model.SoftwareModule; @@ -322,11 +321,11 @@ public class DistributionSetTable extends AbstractNamedVersionTable