From d435c0a424190322b20274172eaf039ba5b6e541 Mon Sep 17 00:00:00 2001 From: Avgustin Marinov Date: Tue, 26 Nov 2024 12:59:45 +0200 Subject: [PATCH] Switch (mostly) to JPA generic events (#2104) instead of EclipseLink specific Signed-off-by: Avgustin Marinov --- .../repository/ControllerManagement.java | 3 +- .../hawkbit/repository/model/Target.java | 9 +- .../ExceptionMappingAspectHandler.java | 5 +- .../management/JpaControllerManagement.java | 11 +- .../management/JpaDeploymentManagement.java | 5 +- .../jpa/model/AbstractJpaBaseEntity.java | 32 +- .../jpa/model/AbstractJpaNamedEntity.java | 45 +-- .../AbstractJpaTenantAwareBaseEntity.java | 55 +-- .../jpa/model/AbstractJpaTypeEntity.java | 45 +-- .../jpa/model/EntityInterceptorListener.java | 3 +- .../model/EntityPropertyChangeListener.java | 59 --- .../jpa/model/EventAwareEntity.java | 16 +- .../jpa/model/JpaSoftwareModule.java | 11 +- .../repository/jpa/model/JpaTarget.java | 347 ++++++------------ .../AfterTransactionCommitExecutorHolder.java | 16 +- .../jpa/utils/DeploymentHelper.java | 3 +- .../management/ControllerManagementTest.java | 26 +- .../test/matcher/EventVerifier.java | 7 +- 18 files changed, 218 insertions(+), 480 deletions(-) delete mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/EntityPropertyChangeListener.java diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java index b4110fc798..b56d7a6825 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java @@ -321,8 +321,7 @@ Target findOrRegisterTargetIfItDoesNotExist(@NotEmpty String controllerId, @NotN * @throws InvalidTargetAttributeException if attributes violate constraints */ @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) - Target updateControllerAttributes(@NotEmpty String controllerId, @NotNull Map attributes, - UpdateMode mode); + Target updateControllerAttributes(@NotEmpty String controllerId, @NotNull Map attributes, UpdateMode mode); /** * Finds {@link Target} based on given controller ID returns found Target diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Target.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Target.java index ef7a946c08..6c189990aa 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Target.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Target.java @@ -53,7 +53,9 @@ public interface Target extends NamedEntity { String getControllerId(); /** - * @return the securityToken + * @return the securityToken if the current security context contains the necessary permission + * {@link org.eclipse.hawkbit.im.authentication.SpPermission#READ_TARGET_SEC_TOKEN} + * or the current context is executed as system code, otherwise {@code null}. */ String getSecurityToken(); @@ -70,8 +72,7 @@ default TargetWithActionType getTargetWithActionType() { URI getAddress(); /** - * @return time in {@link TimeUnit#MILLISECONDS} GMT when the {@link Target} - * polled the server the last time or null if target + * @return time in {@link TimeUnit#MILLISECONDS} GMT when the {@link Target} polled the server the last time or null if target * has never queried yet. */ Long getLastTargetQuery(); @@ -113,4 +114,4 @@ default TargetWithActionType getTargetWithActionType() { * {@link #getControllerAttributes()}. */ boolean isRequestControllerAttributes(); -} +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/aspects/ExceptionMappingAspectHandler.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/aspects/ExceptionMappingAspectHandler.java index 2c4360654b..d39974a4ac 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/aspects/ExceptionMappingAspectHandler.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/aspects/ExceptionMappingAspectHandler.java @@ -73,9 +73,8 @@ public class ExceptionMappingAspectHandler implements Ordered { @SuppressWarnings({ "squid:S00112", "squid:S1162" }) public void catchAndWrapJpaExceptionsService(final Exception ex) throws Throwable { - // Workarround for EclipseLink merge where it does not throw - // ConstraintViolationException directly in case of existing entity - // update + // Workaround for EclipseLink merge where it does not throw ConstraintViolationException directly in case of + // existing entity update if (ex instanceof TransactionSystemException) { throw replaceWithCauseIfConstraintViolationException((TransactionSystemException) ex); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java index 73185e7b97..142dba9267 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java @@ -408,13 +408,8 @@ public Action registerRetrieved(final long actionId, final String message) { @Transactional @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) - public Target updateControllerAttributes(final String controllerId, final Map data, - final UpdateMode mode) { - - /* - * Constraints on attribute keys & values are not validated by - * EclipseLink. Hence, they are validated here. - */ + public Target updateControllerAttributes(final String controllerId, final Map data, final UpdateMode mode) { + // Constraints on attribute keys & values are not validated by EclipseLink. Hence, they are validated here. if (data.entrySet().stream().anyMatch(e -> !isAttributeEntryValid(e))) { throw new InvalidTargetAttributeException(); } @@ -889,7 +884,7 @@ private Optional handleFinishedAndStoreInTargetStatus(final JpaAction ac // because the currently assigned DS can be unequal to the currently // installed DS (the downloadOnly DS) if (isDownloadOnly(action)) { - target.setAssignedDistributionSet(action.getDistributionSet()); + target.setAssignedDistributionSet((JpaDistributionSet) action.getDistributionSet()); } // check if the assigned set is equal to the installed set (not diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java index bc08b85b88..fd553e7798 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java @@ -1009,7 +1009,7 @@ private List activateActionsOfRolloutGroup(final List acti private void setAssignmentOnTargets(final List actions) { final List assignedDsTargets = actions.stream().map(savedAction -> { final JpaTarget mergedTarget = (JpaTarget) entityManager.merge(savedAction.getTarget()); - mergedTarget.setAssignedDistributionSet(savedAction.getDistributionSet()); + mergedTarget.setAssignedDistributionSet((JpaDistributionSet) savedAction.getDistributionSet()); mergedTarget.setUpdateStatus(TargetUpdateStatus.PENDING); return mergedTarget; }).collect(Collectors.toList()); @@ -1022,8 +1022,7 @@ private void setSkipActionStatus(final JpaAction action) { actionStatus.setAction(action); actionStatus.setOccurredAt(action.getCreatedAt()); actionStatus.setStatus(Status.RUNNING); - actionStatus.addMessage(RepositoryConstants.SERVER_MESSAGE_PREFIX - + "Distribution Set is already assigned. Skipping this action."); + actionStatus.addMessage(RepositoryConstants.SERVER_MESSAGE_PREFIX + "Distribution Set is already assigned. Skipping this action."); actionStatusRepository.save(actionStatus); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaBaseEntity.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaBaseEntity.java index 08103a2fba..26f3dd7513 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaBaseEntity.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaBaseEntity.java @@ -19,11 +19,15 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.PostPersist; +import jakarta.persistence.PostRemove; +import jakarta.persistence.PostUpdate; import jakarta.persistence.Version; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.Setter; +import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder; import org.eclipse.hawkbit.repository.model.BaseEntity; import org.eclipse.hawkbit.tenancy.TenantAwareAuthenticationDetails; import org.springframework.data.annotation.CreatedBy; @@ -39,7 +43,7 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) // Default constructor needed for JPA entities. @MappedSuperclass @Access(AccessType.FIELD) -@EntityListeners({ AuditingEntityListener.class, EntityPropertyChangeListener.class, EntityInterceptorListener.class }) +@EntityListeners({ AuditingEntityListener.class, EntityInterceptorListener.class }) public abstract class AbstractJpaBaseEntity implements BaseEntity { protected static final int USERNAME_FIELD_LENGTH = 64; @@ -199,6 +203,32 @@ public String toString() { return this.getClass().getSimpleName() + " [id=" + id + "]"; } + @PostPersist + public void postInsert() { + if (this instanceof EventAwareEntity eventAwareEntity) { + doNotify(eventAwareEntity::fireCreateEvent); + } + } + + @PostUpdate + public void postUpdate() { + if (this instanceof EventAwareEntity eventAwareEntity) { + doNotify(eventAwareEntity::fireUpdateEvent); + } + } + + @PostRemove + public void postDelete() { + if (this instanceof EventAwareEntity eventAwareEntity) { + doNotify(eventAwareEntity::fireDeleteEvent); + } + } + + protected static void doNotify(final Runnable runnable) { + // fire events onl AFTER transaction commit + AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(runnable); + } + private boolean isController() { return SecurityContextHolder.getContext().getAuthentication() != null && SecurityContextHolder.getContext().getAuthentication() diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaNamedEntity.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaNamedEntity.java index ef59260e86..62ab39bef9 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaNamedEntity.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaNamedEntity.java @@ -16,13 +16,19 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; import org.eclipse.hawkbit.repository.model.NamedEntity; import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity; /** - * {@link TenantAwareBaseEntity} extension for all entities that are named in - * addition to their technical ID. + * {@link TenantAwareBaseEntity} extension for all entities that are named in addition to their technical ID. */ +@NoArgsConstructor(access = AccessLevel.PROTECTED) // Default constructor needed for JPA entities +@Setter +@Getter @MappedSuperclass // exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities @SuppressWarnings("squid:S2160") @@ -40,39 +46,8 @@ public abstract class AbstractJpaNamedEntity extends AbstractJpaTenantAwareBaseE @Size(max = DESCRIPTION_MAX_SIZE) private String description; - /** - * Default constructor. - */ - protected AbstractJpaNamedEntity() { - // Default constructor needed for JPA entities - } - - /** - * Parameterized constructor. - * - * @param name of the {@link NamedEntity} - * @param description of the {@link NamedEntity} - */ - AbstractJpaNamedEntity(final String name, final String description) { - this.name = name; - this.description = description; - } - - @Override - public String getDescription() { - return description; - } - - @Override - public String getName() { - return name; - } - - public void setName(final String name) { + protected AbstractJpaNamedEntity(final String name, final String description) { this.name = name; - } - - public void setDescription(final String description) { this.description = description; } -} +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaTenantAwareBaseEntity.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaTenantAwareBaseEntity.java index d366e65123..c1af2081db 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaTenantAwareBaseEntity.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaTenantAwareBaseEntity.java @@ -17,6 +17,10 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; import org.eclipse.hawkbit.repository.exception.TenantNotExistException; import org.eclipse.hawkbit.repository.jpa.model.helper.TenantAwareHolder; import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity; @@ -28,6 +32,7 @@ /** * Holder of the base attributes common to all tenant aware entities. */ +@NoArgsConstructor(access = AccessLevel.PROTECTED) // Default constructor needed for JPA entities. @MappedSuperclass @TenantDiscriminatorColumn(name = "tenant", length = 40) @Multitenant(MultitenantType.SINGLE_TABLE) @@ -36,34 +41,16 @@ public abstract class AbstractJpaTenantAwareBaseEntity extends AbstractJpaBaseEn @Serial private static final long serialVersionUID = 1L; + @Setter + @Getter @Column(name = "tenant", nullable = false, insertable = false, updatable = false, length = 40) @Size(min = 1, max = 40) @NotNull private String tenant; /** - * Default constructor needed for JPA entities. - */ - protected AbstractJpaTenantAwareBaseEntity() { - // Default constructor needed for JPA entities. - } - - @Override - public String getTenant() { - return tenant; - } - - public void setTenant(final String tenant) { - this.tenant = tenant; - } - - /** - * Tenant aware entities extend the equals/hashcode strategy with the tenant - * name. That would allow for instance in a multi-schema based data - * separation setup to have the same primary key for different entities of - * different tenants. - * - * @see org.eclipse.hawkbit.repository.model.BaseEntity#hashCode() + * Tenant aware entities extend the equals/hashcode strategy with the tenant name. That would allow for instance in a + * multi-schema based data separation setup to have the same primary key for different entities of different tenants. */ @Override public int hashCode() { @@ -74,12 +61,9 @@ public int hashCode() { } /** - * Tenant aware entities extend the equals/hashcode strategy with the tenant - * name. That would allow for instance in a multi-schema based data - * separation setup to have the same primary key for different entities of + * Tenant aware entities extend the equals/hashcode strategy with the tenant name. That would allow for instance in a + * multi-schema based data separation setup to have the same primary key for different entities of * different tenants. - * - * @see org.eclipse.hawkbit.repository.model.BaseEntity#equals(java.lang.Object) */ @Override // exception squid:S2259 - obj is checked for null in super @@ -105,21 +89,18 @@ public String toString() { } /** - * PrePersist listener method for all {@link TenantAwareBaseEntity} - * entities. + * PrePersist listener method for all {@link TenantAwareBaseEntity} entities. */ @PrePersist void prePersist() { - // before persisting the entity check the current ID of the tenant by - // using the TenantAware - // service + // before persisting the entity check the current ID of the tenant by using the TenantAware service final String currentTenant = SystemManagementHolder.getInstance().currentTenant(); if (currentTenant == null) { - throw new TenantNotExistException("Tenant " - + TenantAwareHolder.getInstance().getTenantAware().getCurrentTenant() - + " does not exists, cannot create entity " + this.getClass() + " with id " + super.getId()); + throw new TenantNotExistException( + String.format( + "Tenant %s does not exists, cannot create entity %s with id %d", + TenantAwareHolder.getInstance().getTenantAware().getCurrentTenant(), getClass(), getId())); } setTenant(currentTenant.toUpperCase()); } - -} +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaTypeEntity.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaTypeEntity.java index 7d2b757ffe..faf182c387 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaTypeEntity.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaTypeEntity.java @@ -16,13 +16,19 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity; import org.eclipse.hawkbit.repository.model.Type; /** - * {@link TenantAwareBaseEntity} extension for all entities that are named in - * addition to their technical ID. + * {@link TenantAwareBaseEntity} extension for all entities that are named in addition to their technical ID. */ +@NoArgsConstructor(access = AccessLevel.PROTECTED) // Default constructor needed for JPA entities +@Setter +@Getter @MappedSuperclass // exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities @SuppressWarnings("squid:S2160") @@ -40,40 +46,9 @@ public abstract class AbstractJpaTypeEntity extends AbstractJpaNamedEntity imple @Size(max = COLOUR_MAX_SIZE) private String colour; - /** - * Default constructor. - */ - protected AbstractJpaTypeEntity() { - // Default constructor needed for JPA entities - } - - /** - * Parameterized constructor. - * - * @param key of the {@link Type} - * @param colour of the {@link Type} - */ - AbstractJpaTypeEntity(final String name, final String description, final String key, final String colour) { + protected AbstractJpaTypeEntity(final String name, final String description, final String key, final String colour) { super(name, description); this.key = key; this.colour = colour; } - - @Override - public String getKey() { - return key; - } - - @Override - public String getColour() { - return colour; - } - - public void setColour(final String colour) { - this.colour = colour; - } - - public void setKey(final String key) { - this.key = key; - } -} +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/EntityInterceptorListener.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/EntityInterceptorListener.java index 892d1b38b9..073e7ff77e 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/EntityInterceptorListener.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/EntityInterceptorListener.java @@ -23,8 +23,7 @@ import org.eclipse.hawkbit.repository.jpa.model.helper.EntityInterceptorHolder; /** - * Entity listener which calls the callback's of all registered entity - * interceptors. + * Entity listener which calls the callback's of all registered entity interceptors. */ public class EntityInterceptorListener { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/EntityPropertyChangeListener.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/EntityPropertyChangeListener.java deleted file mode 100644 index 9bd07b4c7d..0000000000 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/EntityPropertyChangeListener.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.hawkbit.repository.jpa.model; - -import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder; -import org.eclipse.persistence.descriptors.DescriptorEvent; -import org.eclipse.persistence.descriptors.DescriptorEventAdapter; -import org.eclipse.persistence.queries.UpdateObjectQuery; - -/** - * Listens to change in property values of an entity and calls the corresponding - * {@link EventAwareEntity}. - */ -public class EntityPropertyChangeListener extends DescriptorEventAdapter { - - @Override - public void postDelete(final DescriptorEvent event) { - final Object object = event.getObject(); - if (isEventAwareEntity(object)) { - doNotify(() -> ((EventAwareEntity) object).fireDeleteEvent()); - } - } - - @Override - public void postInsert(final DescriptorEvent event) { - final Object object = event.getObject(); - if (isEventAwareEntity(object)) { - doNotify(() -> ((EventAwareEntity) object).fireCreateEvent()); - } - } - - @Override - public void postUpdate(final DescriptorEvent event) { - final Object object = event.getObject(); - if (isEventAwareEntity(object) && isFireUpdate((EventAwareEntity) object, (UpdateObjectQuery) event.getQuery())) { - doNotify(() -> ((EventAwareEntity) object).fireUpdateEvent()); - } - } - - private static boolean isEventAwareEntity(final Object object) { - return object instanceof EventAwareEntity; - } - - private static void doNotify(final Runnable runnable) { - AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(runnable); - } - - private static boolean isFireUpdate(final EventAwareEntity entity, final UpdateObjectQuery query) { - return entity.getUpdateIgnoreFields().isEmpty() || query.getObjectChangeSet().getChangedAttributeNames() - .stream().anyMatch(field -> !entity.getUpdateIgnoreFields().contains(field)); - } -} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/EventAwareEntity.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/EventAwareEntity.java index d74506b2f3..3fea738766 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/EventAwareEntity.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/EventAwareEntity.java @@ -9,12 +9,9 @@ */ package org.eclipse.hawkbit.repository.jpa.model; -import java.util.Collections; -import java.util.List; - /** - * Interfaces which can be implemented by entities to be called when the entity - * should fire an event because the entity has been created, updated or deleted. + * Interfaces which can be implemented by entities to be called when the entity should fire an event because the entity has been created, + * updated or deleted. */ public interface EventAwareEntity { @@ -32,11 +29,4 @@ public interface EventAwareEntity { * Fired for the Entity deletion. */ void fireDeleteEvent(); - - /** - * @return list of entity fields that if the only changed fields prevents {@link #fireUpdateEvent()} call. - */ - default List getUpdateIgnoreFields() { - return Collections.emptyList(); - } -} +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java index fb70e930de..6d27362c2a 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java @@ -55,11 +55,12 @@ @Getter @ToString(callSuper = true) @Entity -@Table(name = "sp_base_software_module", uniqueConstraints = @UniqueConstraint(columnNames = { "module_type", "name", - "version", "tenant" }, name = "uk_base_sw_mod"), indexes = { - @Index(name = "sp_idx_base_sw_module_01", columnList = "tenant,deleted,name,version"), - @Index(name = "sp_idx_base_sw_module_02", columnList = "tenant,deleted,module_type"), - @Index(name = "sp_idx_base_sw_module_prim", columnList = "tenant,id") }) +@Table(name = "sp_base_software_module", + uniqueConstraints = @UniqueConstraint(columnNames = { "module_type", "name", "version", "tenant" }, name = "uk_base_sw_mod"), + indexes = { + @Index(name = "sp_idx_base_sw_module_01", columnList = "tenant,deleted,name,version"), + @Index(name = "sp_idx_base_sw_module_02", columnList = "tenant,deleted,module_type"), + @Index(name = "sp_idx_base_sw_module_prim", columnList = "tenant,id") }) @NamedEntityGraph(name = "SoftwareModule.artifacts", attributeNodes = { @NamedAttributeNode("artifacts") }) // exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities @SuppressWarnings("squid:S2160") diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java index bcc8083817..189817f396 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java @@ -27,6 +27,7 @@ import jakarta.persistence.Converter; import jakarta.persistence.ElementCollection; import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; import jakarta.persistence.FetchType; import jakarta.persistence.ForeignKey; import jakarta.persistence.Index; @@ -44,6 +45,10 @@ import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.eclipse.hawkbit.im.authentication.SpPermission; import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent; @@ -52,8 +57,6 @@ import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHolder; import org.eclipse.hawkbit.repository.jpa.utils.MapAttributeConverter; import org.eclipse.hawkbit.repository.model.Action; -import org.eclipse.hawkbit.repository.model.AutoConfirmationStatus; -import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.PollStatus; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetMetadata; @@ -64,78 +67,97 @@ import org.eclipse.hawkbit.repository.model.helper.SystemSecurityContextHolder; import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder; import org.eclipse.hawkbit.security.SystemSecurityContext; +import org.eclipse.persistence.descriptors.DescriptorEvent; +import org.eclipse.persistence.descriptors.DescriptorEventAdapter; +import org.eclipse.persistence.queries.UpdateObjectQuery; /** * JPA implementation of {@link Target}. */ +@NoArgsConstructor(access = AccessLevel.PUBLIC) // empty constructor for JPA. @Entity -@Table(name = "sp_target", indexes = { - @Index(name = "sp_idx_target_01", columnList = "tenant,name,assigned_distribution_set"), - @Index(name = "sp_idx_target_03", columnList = "tenant,controller_id,assigned_distribution_set"), - @Index(name = "sp_idx_target_04", columnList = "tenant,created_at"), - @Index(name = "sp_idx_target_05", columnList = "tenant,last_modified_at"), - @Index(name = "sp_idx_target_prim", columnList = "tenant,id") }, uniqueConstraints = @UniqueConstraint(columnNames = { - "controller_id", "tenant" }, name = "uk_tenant_controller_id")) +@Table(name = "sp_target", + indexes = { + @Index(name = "sp_idx_target_01", columnList = "tenant,name,assigned_distribution_set"), + @Index(name = "sp_idx_target_03", columnList = "tenant,controller_id,assigned_distribution_set"), + @Index(name = "sp_idx_target_04", columnList = "tenant,created_at"), + @Index(name = "sp_idx_target_05", columnList = "tenant,last_modified_at"), + @Index(name = "sp_idx_target_prim", columnList = "tenant,id") }, + uniqueConstraints = @UniqueConstraint(columnNames = { "controller_id", "tenant" }, name = "uk_tenant_controller_id")) // exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities @SuppressWarnings("squid:S2160") +@EntityListeners({ JpaTarget.EntityPropertyChangeListener.class }) // add listener to the listeners declared into suppers @Slf4j public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAwareEntity { @Serial private static final long serialVersionUID = 1L; - private static final List TARGET_UPDATE_EVENT_IGNORE_FIELDS = Arrays.asList("lastTargetQuery", "address", - "optLockRevision", "lastModifiedAt", "lastModifiedBy"); - @Column(name = "controller_id", length = Target.CONTROLLER_ID_MAX_SIZE, updatable = false, nullable = false) @Size(min = 1, max = Target.CONTROLLER_ID_MAX_SIZE) @NotNull @Pattern(regexp = "[\\S]*", message = "has whitespaces which are not allowed") private String controllerId; - @OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE }, targetEntity = JpaAction.class) - private List actions; - - /** - * the security token of the target which allows if enabled to authenticate - * with this security token. - */ + /** the security token of the target which allows if enabled to authenticate with this security token */ + @Setter @Column(name = "sec_token", nullable = false, length = Target.SECURITY_TOKEN_MAX_SIZE) @Size(min = 1, max = Target.SECURITY_TOKEN_MAX_SIZE) @NotNull private String securityToken; - @OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.REMOVE }) - private List rolloutTargetGroup; - + @Setter @Column(name = "address", length = Target.ADDRESS_MAX_SIZE) @Size(max = Target.ADDRESS_MAX_SIZE) private String address; + @Setter + @Getter @Column(name = "last_target_query") private Long lastTargetQuery; + @Setter + @Getter @Column(name = "install_date") private Long installationDate; + + @Setter + @Getter @Column(name = "update_status", nullable = false) @Convert(converter = TargetUpdateStatusConverter.class) @NotNull private TargetUpdateStatus updateStatus = TargetUpdateStatus.UNKNOWN; + + @Setter + @Getter @ManyToOne(optional = true, fetch = FetchType.LAZY) - @JoinColumn(name = "installed_distribution_set", nullable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_inst_ds")) + @JoinColumn(name = "installed_distribution_set", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_inst_ds")) private JpaDistributionSet installedDistributionSet; - @ManyToOne(optional = true, fetch = FetchType.LAZY) - @JoinColumn(name = "assigned_distribution_set", nullable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_assign_ds")) + + @Setter + @Getter + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "assigned_distribution_set", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_assign_ds")) private JpaDistributionSet assignedDistributionSet; - // set default request controller attributes to true, because we want to request them the first time + + @Setter + @Getter @Column(name = "request_controller_attributes", nullable = false) + // set default request controller attributes to true, because we want to request them the first time private boolean requestControllerAttributes = true; + + @Setter + @Getter @OneToOne(fetch = FetchType.LAZY, mappedBy = "target", orphanRemoval = true) @PrimaryKeyJoinColumn private JpaAutoConfirmationStatus autoConfirmationStatus; + + @Setter + @Getter @ManyToOne(fetch = FetchType.LAZY, targetEntity = JpaTargetType.class) @JoinColumn(name = "target_type", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_relation_target_type")) private TargetType targetType; + @ManyToMany(targetEntity = JpaTargetTag.class) @JoinTable( name = "sp_target_target_tag", @@ -149,10 +171,9 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_targtag_tag")) }) private Set tags; - /** - * Supplied / committed by the controller. Read-only via management API. - */ + // no cascade option on an ElementCollection, the target objects are always persisted, merged, removed with their parent. + @Getter @ElementCollection @Column(name = "attribute_value", length = Target.CONTROLLER_ATTRIBUTE_VALUE_SIZE) @MapKeyColumn(name = "attribute_key", nullable = false, length = Target.CONTROLLER_ATTRIBUTE_KEY_SIZE) @@ -161,24 +182,20 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw joinColumns = { @JoinColumn(name = "target_id", nullable = false, insertable = false, updatable = false) }, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_attrib_target")) private Map controllerAttributes; + @OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE }, targetEntity = JpaTargetMetadata.class) private List metadata; - /** - * Constructor. - * - * @param controllerId controller ID of the {@link Target} - */ + @OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE }, targetEntity = JpaAction.class) + private List actions; + + @OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.REMOVE }) + private List rolloutTargetGroup; + public JpaTarget(final String controllerId) { this(controllerId, SecurityTokenGeneratorHolder.getInstance().generateToken()); } - /** - * Constructor. - * - * @param controllerId controller ID of the {@link Target} - * @param securityToken for target authentication if enabled - */ public JpaTarget(final String controllerId, final String securityToken) { this.controllerId = controllerId; // truncate controller ID to max name length (if needed) @@ -186,63 +203,20 @@ public JpaTarget(final String controllerId, final String securityToken) { this.securityToken = securityToken; } - /** - * Constructor - */ - public JpaTarget() { - // empty constructor for JPA. - } - - /** - * @return assigned distribution set - */ - public DistributionSet getAssignedDistributionSet() { - return assignedDistributionSet; - } - - /** - * @param assignedDistributionSet Distribution set - */ - public void setAssignedDistributionSet(final DistributionSet assignedDistributionSet) { - this.assignedDistributionSet = (JpaDistributionSet) assignedDistributionSet; - } - @Override public String getControllerId() { return controllerId; } - /** - * @param controllerId Controller ID - */ - public void setControllerId(final String controllerId) { - this.controllerId = controllerId; - } - - /** - * @return the securityToken if the current security context contains the - * necessary permission {@link SpPermission#READ_TARGET_SEC_TOKEN} - * or the current context is executed as system code, otherwise - * {@code null}. - */ @Override public String getSecurityToken() { - final SystemSecurityContext systemSecurityContext = - SystemSecurityContextHolder.getInstance().getSystemSecurityContext(); - if (systemSecurityContext.isCurrentThreadSystemCode() || - systemSecurityContext.hasPermission(SpPermission.READ_TARGET_SEC_TOKEN)) { + final SystemSecurityContext systemSecurityContext = SystemSecurityContextHolder.getInstance().getSystemSecurityContext(); + if (systemSecurityContext.isCurrentThreadSystemCode() || systemSecurityContext.hasPermission(SpPermission.READ_TARGET_SEC_TOKEN)) { return securityToken; } return null; } - /** - * @param securityToken token value - */ - public void setSecurityToken(final String securityToken) { - this.securityToken = securityToken; - } - /** * @return the ipAddress */ @@ -259,31 +233,9 @@ public URI getAddress() { } } - @Override - public Long getLastTargetQuery() { - return lastTargetQuery; - } - - @Override - public Long getInstallationDate() { - return installationDate; - } - - @Override - public TargetUpdateStatus getUpdateStatus() { - return updateStatus; - } - - @Override - public TargetType getTargetType() { - return targetType; - } - /** - * @return the poll time which holds the last poll time of the target, the - * next poll time and the overdue time. In case the - * {@link #lastTargetQuery} is not set e.g. the target never polled - * before this method returns {@code null} + * @return the poll time which holds the last poll time of the target, the next poll time and the overdue time. In case the + * {@link #lastTargetQuery} is not set e.g. the target never polled before this method returns {@code null} */ @Override public PollStatus getPollStatus() { @@ -292,89 +244,10 @@ public PollStatus getPollStatus() { return null; } return TenantConfigurationManagementHolder.getInstance().getTenantConfigurationManagement() - .pollStatusResolver().apply(this); - } - - @Override - public AutoConfirmationStatus getAutoConfirmationStatus() { - return autoConfirmationStatus; + .pollStatusResolver() + .apply(this); } - public void setAutoConfirmationStatus(final JpaAutoConfirmationStatus autoConfirmationStatus) { - this.autoConfirmationStatus = autoConfirmationStatus; - } - - @Override - public boolean isRequestControllerAttributes() { - return requestControllerAttributes; - } - - /** - * @param requestControllerAttributes Attributes - */ - public void setRequestControllerAttributes(final boolean requestControllerAttributes) { - this.requestControllerAttributes = requestControllerAttributes; - } - - /** - * @param type Target type - */ - public void setTargetType(final TargetType type) { - this.targetType = type; - } - - /** - * @param updateStatus Status - */ - public void setUpdateStatus(final TargetUpdateStatus updateStatus) { - this.updateStatus = updateStatus; - } - - /** - * @param installationDate installation date - */ - public void setInstallationDate(final Long installationDate) { - this.installationDate = installationDate; - } - - /** - * @param lastTargetQuery last query ID - */ - public void setLastTargetQuery(final Long lastTargetQuery) { - this.lastTargetQuery = lastTargetQuery; - } - - /** - * @param address Address - */ - public void setAddress(final String address) { - this.address = address; - } - - /** - * @return tags - */ - public Set getTags() { - if (tags == null) { - return Collections.emptySet(); - } - - return Collections.unmodifiableSet(tags); - } - - /** - * @return rollouts target group - */ - public List getRolloutTargetGroup() { - if (rolloutTargetGroup == null) { - return Collections.emptyList(); - } - return Collections.unmodifiableList(rolloutTargetGroup); - } - - /** - * @param tag to be added - */ public void addTag(final TargetTag tag) { if (tags == null) { tags = new HashSet<>(); @@ -382,29 +255,20 @@ public void addTag(final TargetTag tag) { tags.add(tag); } - /** - * @param tag the tag to be removed from the target - */ public void removeTag(final TargetTag tag) { if (tags != null) { tags.remove(tag); } } - /** - * @return list of action - */ - public List getActions() { - if (actions == null) { - return Collections.emptyList(); - } + public Set getTags() { + return tags == null ? Collections.emptySet() : Collections.unmodifiableSet(tags); + } - return Collections.unmodifiableList(actions); + public List getMetadata() { + return metadata == null ? Collections.emptyList() : Collections.unmodifiableList(metadata); } - /** - * @param action Action - */ public void addAction(final Action action) { if (actions == null) { actions = new ArrayList<>(); @@ -412,42 +276,8 @@ public void addAction(final Action action) { actions.add((JpaAction) action); } - /** - * @return Distribution set - */ - public JpaDistributionSet getInstalledDistributionSet() { - return installedDistributionSet; - } - - /** - * @param installedDistributionSet Distribution set - */ - public void setInstalledDistributionSet(final JpaDistributionSet installedDistributionSet) { - this.installedDistributionSet = installedDistributionSet; - } - - /** - * @return controller attributes - */ - public Map getControllerAttributes() { - return controllerAttributes; - } - - /** - * @return target metadata - */ - public List getMetadata() { - if (metadata == null) { - return Collections.emptyList(); - } - - return Collections.unmodifiableList(metadata); - } - - @Override - public String toString() { - return "JpaTarget [controllerId=" + controllerId + ", revision=" + getOptLockRevision() + ", id=" + getId() - + "]"; + public List getActions() { + return actions == null ? Collections.emptyList() : Collections.unmodifiableList(actions); } @Override @@ -469,9 +299,20 @@ public void fireDeleteEvent() { getClass(), EventPublisherHolder.getInstance().getApplicationId())); } + public List getRolloutTargetGroup() { + return rolloutTargetGroup == null ? Collections.emptyList() : Collections.unmodifiableList(rolloutTargetGroup); + } + + @Override + public String toString() { + return "JpaTarget [controllerId=" + controllerId + ", revision=" + getOptLockRevision() + ", id=" + getId() + "]"; + } + + // if "lastTargetQuery", "address" only are changed - the remove events are skipped @Override - public List getUpdateIgnoreFields() { - return TARGET_UPDATE_EVENT_IGNORE_FIELDS; + public void postUpdate() { + // do nothing - processed by EntityPropertyChangeListener + // since we want to skip the event if only "lastTargetQuery" or "address" are changed } @Converter @@ -487,4 +328,24 @@ public TargetUpdateStatusConverter() { ), null); } } -} + + /** + * Listens to updates on {@link JpaTarget} entities, Filtering out updates that only change the "lastTargetQuery" or "address" fields. + */ + public static class EntityPropertyChangeListener extends DescriptorEventAdapter { + + private static final List TARGET_UPDATE_EVENT_IGNORE_FIELDS = List.of( + "lastTargetQuery", "address", // actual to be skipped + "optLockRevision", "lastModifiedAt", "lastModifiedBy" // system to be skipped + ); + + @Override + public void postUpdate(final DescriptorEvent event) { + final Object object = event.getObject(); + if (((UpdateObjectQuery) event.getQuery()).getObjectChangeSet().getChangedAttributeNames().stream() + .anyMatch(field -> !TARGET_UPDATE_EVENT_IGNORE_FIELDS.contains(field))) { + doNotify(() -> ((EventAwareEntity) object).fireUpdateEvent()); + } + } + } +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/helper/AfterTransactionCommitExecutorHolder.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/helper/AfterTransactionCommitExecutorHolder.java index 46534ba33f..79878af7ab 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/helper/AfterTransactionCommitExecutorHolder.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/helper/AfterTransactionCommitExecutorHolder.java @@ -9,15 +9,16 @@ */ package org.eclipse.hawkbit.repository.jpa.model.helper; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor; -import org.eclipse.hawkbit.repository.jpa.model.EntityPropertyChangeListener; import org.springframework.beans.factory.annotation.Autowired; /** - * A singleton bean which holds the {@link AfterTransactionCommitExecutor} to - * have to the cache manager in beans not instantiated by spring e.g. JPA - * entities or {@link EntityPropertyChangeListener} which cannot be autowired. + * A singleton bean which holds the {@link AfterTransactionCommitExecutor} to provide it to in beans not instantiated by spring e.g. JPA + * entities which cannot be autowired. */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) public final class AfterTransactionCommitExecutorHolder { private static final AfterTransactionCommitExecutorHolder SINGLETON = new AfterTransactionCommitExecutorHolder(); @@ -25,10 +26,6 @@ public final class AfterTransactionCommitExecutorHolder { @Autowired private AfterTransactionCommitExecutor afterCommit; - private AfterTransactionCommitExecutorHolder() { - - } - /** * @return the cache manager holder singleton instance */ @@ -49,5 +46,4 @@ public AfterTransactionCommitExecutor getAfterCommit() { public void setAfterCommit(final AfterTransactionCommitExecutor afterCommit) { this.afterCommit = afterCommit; } - -} +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/utils/DeploymentHelper.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/utils/DeploymentHelper.java index 49797bb42c..89bfdfeb8a 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/utils/DeploymentHelper.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/utils/DeploymentHelper.java @@ -18,6 +18,7 @@ import lombok.extern.slf4j.Slf4j; import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaAction_; +import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository; import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository; @@ -68,7 +69,7 @@ public static void successCancellation(final JpaAction action, final ActionRepos target.setAssignedDistributionSet(target.getInstalledDistributionSet()); target.setUpdateStatus(TargetUpdateStatus.IN_SYNC); } else { - target.setAssignedDistributionSet(nextActiveActions.get(0).getDistributionSet()); + target.setAssignedDistributionSet((JpaDistributionSet) nextActiveActions.get(0).getDistributionSet()); } targetRepository.save(target); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/ControllerManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/ControllerManagementTest.java index db969d82fc..77f175db52 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/ControllerManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/ControllerManagementTest.java @@ -622,22 +622,25 @@ void hasTargetArtifactAssignedIsTrueWithMultipleArtifacts() { @Test @Description("Register a controller which does not exist") @WithUser(principal = "controller", authorities = { CONTROLLER_ROLE }) - @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), + @ExpectEvents({ + @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 2) }) void findOrRegisterTargetIfItDoesNotExist() { final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST); assertThat(target).as("target should not be null").isNotNull(); final Target sameTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST); - assertThat(target.getId()).as("Target should be the equals").isEqualTo(sameTarget.getId()); + assertThat(target.getId()).as("Target should ben equals").isEqualTo(sameTarget.getId()); assertThat(targetRepository.count()).as("Only 1 target should be registered").isEqualTo(1L); } @Test @Description("Register a controller with name which does not exist and update its name") @WithUser(principal = "controller", authorities = { CONTROLLER_ROLE }) - @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), - @Expect(type = TargetPollEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 1) }) + @ExpectEvents({ + @Expect(type = TargetCreatedEvent.class, count = 1), + @Expect(type = TargetPollEvent.class, count = 2), + @Expect(type = TargetUpdatedEvent.class, count = 1) }) void findOrRegisterTargetIfItDoesNotExistWithName() { final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST, "TestName", null); final Target sameTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST, @@ -1085,10 +1088,10 @@ void updateTargetAttributes() throws Exception { @Test @Description("Ensures that target attributes can be updated using different update modes.") - @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), + @ExpectEvents({ + @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 4) }) void updateTargetAttributesWithDifferentUpdateModes() { - final String controllerId = "testCtrl"; testdataFactory.createTarget(controllerId); @@ -1103,7 +1106,6 @@ void updateTargetAttributesWithDifferentUpdateModes() { // update mode REMOVE updateTargetAttributesWithUpdateModeRemove(controllerId); - } @Test @@ -1710,7 +1712,6 @@ private void updateAttributeAndVerify(final String controllerId) { @Step private void updateTargetAttributesWithUpdateModeRemove(final String controllerId) { - final int previousSize = targetManagement.getControllerAttributes(controllerId).size(); // update the attributes using update mode REMOVE @@ -1723,14 +1724,12 @@ private void updateTargetAttributesWithUpdateModeRemove(final String controllerI final Map updatedAttributes = targetManagement.getControllerAttributes(controllerId); assertThat(updatedAttributes.size()).isEqualTo(previousSize - 2); assertThat(updatedAttributes).doesNotContainKeys("k1", "k3"); - } @Step private void updateTargetAttributesWithUpdateModeMerge(final String controllerId) { // get the current attributes - final HashMap attributes = new HashMap<>( - targetManagement.getControllerAttributes(controllerId)); + final HashMap attributes = new HashMap<>(targetManagement.getControllerAttributes(controllerId)); // update the attributes using update mode MERGE final Map mergeAttributes = new HashMap<>(); @@ -1748,10 +1747,8 @@ private void updateTargetAttributesWithUpdateModeMerge(final String controllerId @Step private void updateTargetAttributesWithUpdateModeReplace(final String controllerId) { - // get the current attributes - final HashMap attributes = new HashMap<>( - targetManagement.getControllerAttributes(controllerId)); + final HashMap attributes = new HashMap<>(targetManagement.getControllerAttributes(controllerId)); // update the attributes using update mode REPLACE final Map replacementAttributes = new HashMap<>(); @@ -1770,7 +1767,6 @@ private void updateTargetAttributesWithUpdateModeReplace(final String controller @Step private void updateTargetAttributesWithoutUpdateMode(final String controllerId) { - // set the initial attributes final Map attributes = new HashMap<>(); attributes.put("k0", "v0"); diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/matcher/EventVerifier.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/matcher/EventVerifier.java index ed9bf975ab..2f75ab4a7d 100644 --- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/matcher/EventVerifier.java +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/matcher/EventVerifier.java @@ -58,21 +58,20 @@ public class EventVerifier extends AbstractTestExecutionListener { * executor in the ApplicationEventMultiCaster, so the order of the events * keep the same. * - * @param publisher the {@link ApplicationEventPublisher} to publish the marker - * event to + * @param publisher the {@link ApplicationEventPublisher} to publish the marker event to */ public static void publishResetMarkerEvent(final ApplicationEventPublisher publisher) { publisher.publishEvent(new ResetCounterMarkerEvent()); } @Override - public void beforeTestMethod(final TestContext testContext) throws Exception { + public void beforeTestMethod(final TestContext testContext) { final Optional expectedEvents = getExpectationsFrom(testContext.getTestMethod()); expectedEvents.ifPresent(events -> beforeTest(testContext)); } @Override - public void afterTestMethod(final TestContext testContext) throws Exception { + public void afterTestMethod(final TestContext testContext) { final Optional expectedEvents = getExpectationsFrom(testContext.getTestMethod()); try { expectedEvents.ifPresent(events -> afterTest(events));