Switch (mostly) to JPA generic events (#2104)
instead of EclipseLink specific Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -321,8 +321,7 @@ public interface ControllerManagement {
|
|||||||
* @throws InvalidTargetAttributeException if attributes violate constraints
|
* @throws InvalidTargetAttributeException if attributes violate constraints
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||||
Target updateControllerAttributes(@NotEmpty String controllerId, @NotNull Map<String, String> attributes,
|
Target updateControllerAttributes(@NotEmpty String controllerId, @NotNull Map<String, String> attributes, UpdateMode mode);
|
||||||
UpdateMode mode);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds {@link Target} based on given controller ID returns found Target
|
* Finds {@link Target} based on given controller ID returns found Target
|
||||||
|
|||||||
@@ -53,7 +53,9 @@ public interface Target extends NamedEntity {
|
|||||||
String getControllerId();
|
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();
|
String getSecurityToken();
|
||||||
|
|
||||||
@@ -70,8 +72,7 @@ public interface Target extends NamedEntity {
|
|||||||
URI getAddress();
|
URI getAddress();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return time in {@link TimeUnit#MILLISECONDS} GMT when the {@link Target}
|
* @return time in {@link TimeUnit#MILLISECONDS} GMT when the {@link Target} polled the server the last time or <code>null</code> if target
|
||||||
* polled the server the last time or <code>null</code> if target
|
|
||||||
* has never queried yet.
|
* has never queried yet.
|
||||||
*/
|
*/
|
||||||
Long getLastTargetQuery();
|
Long getLastTargetQuery();
|
||||||
@@ -113,4 +114,4 @@ public interface Target extends NamedEntity {
|
|||||||
* {@link #getControllerAttributes()}.
|
* {@link #getControllerAttributes()}.
|
||||||
*/
|
*/
|
||||||
boolean isRequestControllerAttributes();
|
boolean isRequestControllerAttributes();
|
||||||
}
|
}
|
||||||
@@ -73,9 +73,8 @@ public class ExceptionMappingAspectHandler implements Ordered {
|
|||||||
@SuppressWarnings({ "squid:S00112", "squid:S1162" })
|
@SuppressWarnings({ "squid:S00112", "squid:S1162" })
|
||||||
public void catchAndWrapJpaExceptionsService(final Exception ex) throws Throwable {
|
public void catchAndWrapJpaExceptionsService(final Exception ex) throws Throwable {
|
||||||
|
|
||||||
// Workarround for EclipseLink merge where it does not throw
|
// Workaround for EclipseLink merge where it does not throw ConstraintViolationException directly in case of
|
||||||
// ConstraintViolationException directly in case of existing entity
|
// existing entity update
|
||||||
// update
|
|
||||||
if (ex instanceof TransactionSystemException) {
|
if (ex instanceof TransactionSystemException) {
|
||||||
throw replaceWithCauseIfConstraintViolationException((TransactionSystemException) ex);
|
throw replaceWithCauseIfConstraintViolationException((TransactionSystemException) ex);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -408,13 +408,8 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
|||||||
@Transactional
|
@Transactional
|
||||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||||
public Target updateControllerAttributes(final String controllerId, final Map<String, String> data,
|
public Target updateControllerAttributes(final String controllerId, final Map<String, String> data, final UpdateMode mode) {
|
||||||
final UpdateMode mode) {
|
// Constraints on attribute keys & values are not validated by EclipseLink. Hence, they are validated here.
|
||||||
|
|
||||||
/*
|
|
||||||
* Constraints on attribute keys & values are not validated by
|
|
||||||
* EclipseLink. Hence, they are validated here.
|
|
||||||
*/
|
|
||||||
if (data.entrySet().stream().anyMatch(e -> !isAttributeEntryValid(e))) {
|
if (data.entrySet().stream().anyMatch(e -> !isAttributeEntryValid(e))) {
|
||||||
throw new InvalidTargetAttributeException();
|
throw new InvalidTargetAttributeException();
|
||||||
}
|
}
|
||||||
@@ -889,7 +884,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
|||||||
// because the currently assigned DS can be unequal to the currently
|
// because the currently assigned DS can be unequal to the currently
|
||||||
// installed DS (the downloadOnly DS)
|
// installed DS (the downloadOnly DS)
|
||||||
if (isDownloadOnly(action)) {
|
if (isDownloadOnly(action)) {
|
||||||
target.setAssignedDistributionSet(action.getDistributionSet());
|
target.setAssignedDistributionSet((JpaDistributionSet) action.getDistributionSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the assigned set is equal to the installed set (not
|
// check if the assigned set is equal to the installed set (not
|
||||||
|
|||||||
@@ -1009,7 +1009,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
|||||||
private void setAssignmentOnTargets(final List<JpaAction> actions) {
|
private void setAssignmentOnTargets(final List<JpaAction> actions) {
|
||||||
final List<JpaTarget> assignedDsTargets = actions.stream().map(savedAction -> {
|
final List<JpaTarget> assignedDsTargets = actions.stream().map(savedAction -> {
|
||||||
final JpaTarget mergedTarget = (JpaTarget) entityManager.merge(savedAction.getTarget());
|
final JpaTarget mergedTarget = (JpaTarget) entityManager.merge(savedAction.getTarget());
|
||||||
mergedTarget.setAssignedDistributionSet(savedAction.getDistributionSet());
|
mergedTarget.setAssignedDistributionSet((JpaDistributionSet) savedAction.getDistributionSet());
|
||||||
mergedTarget.setUpdateStatus(TargetUpdateStatus.PENDING);
|
mergedTarget.setUpdateStatus(TargetUpdateStatus.PENDING);
|
||||||
return mergedTarget;
|
return mergedTarget;
|
||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
@@ -1022,8 +1022,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
|||||||
actionStatus.setAction(action);
|
actionStatus.setAction(action);
|
||||||
actionStatus.setOccurredAt(action.getCreatedAt());
|
actionStatus.setOccurredAt(action.getCreatedAt());
|
||||||
actionStatus.setStatus(Status.RUNNING);
|
actionStatus.setStatus(Status.RUNNING);
|
||||||
actionStatus.addMessage(RepositoryConstants.SERVER_MESSAGE_PREFIX
|
actionStatus.addMessage(RepositoryConstants.SERVER_MESSAGE_PREFIX + "Distribution Set is already assigned. Skipping this action.");
|
||||||
+ "Distribution Set is already assigned. Skipping this action.");
|
|
||||||
actionStatusRepository.save(actionStatus);
|
actionStatusRepository.save(actionStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,11 +19,15 @@ import jakarta.persistence.GeneratedValue;
|
|||||||
import jakarta.persistence.GenerationType;
|
import jakarta.persistence.GenerationType;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.MappedSuperclass;
|
import jakarta.persistence.MappedSuperclass;
|
||||||
|
import jakarta.persistence.PostPersist;
|
||||||
|
import jakarta.persistence.PostRemove;
|
||||||
|
import jakarta.persistence.PostUpdate;
|
||||||
import jakarta.persistence.Version;
|
import jakarta.persistence.Version;
|
||||||
|
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
|
||||||
import org.eclipse.hawkbit.repository.model.BaseEntity;
|
import org.eclipse.hawkbit.repository.model.BaseEntity;
|
||||||
import org.eclipse.hawkbit.tenancy.TenantAwareAuthenticationDetails;
|
import org.eclipse.hawkbit.tenancy.TenantAwareAuthenticationDetails;
|
||||||
import org.springframework.data.annotation.CreatedBy;
|
import org.springframework.data.annotation.CreatedBy;
|
||||||
@@ -39,7 +43,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
|||||||
@NoArgsConstructor(access = AccessLevel.PROTECTED) // Default constructor needed for JPA entities.
|
@NoArgsConstructor(access = AccessLevel.PROTECTED) // Default constructor needed for JPA entities.
|
||||||
@MappedSuperclass
|
@MappedSuperclass
|
||||||
@Access(AccessType.FIELD)
|
@Access(AccessType.FIELD)
|
||||||
@EntityListeners({ AuditingEntityListener.class, EntityPropertyChangeListener.class, EntityInterceptorListener.class })
|
@EntityListeners({ AuditingEntityListener.class, EntityInterceptorListener.class })
|
||||||
public abstract class AbstractJpaBaseEntity implements BaseEntity {
|
public abstract class AbstractJpaBaseEntity implements BaseEntity {
|
||||||
|
|
||||||
protected static final int USERNAME_FIELD_LENGTH = 64;
|
protected static final int USERNAME_FIELD_LENGTH = 64;
|
||||||
@@ -199,6 +203,32 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity {
|
|||||||
return this.getClass().getSimpleName() + " [id=" + id + "]";
|
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() {
|
private boolean isController() {
|
||||||
return SecurityContextHolder.getContext().getAuthentication() != null
|
return SecurityContextHolder.getContext().getAuthentication() != null
|
||||||
&& SecurityContextHolder.getContext().getAuthentication()
|
&& SecurityContextHolder.getContext().getAuthentication()
|
||||||
|
|||||||
@@ -16,13 +16,19 @@ import jakarta.persistence.MappedSuperclass;
|
|||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import jakarta.validation.constraints.Size;
|
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.NamedEntity;
|
||||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link TenantAwareBaseEntity} extension for all entities that are named in
|
* {@link TenantAwareBaseEntity} extension for all entities that are named in addition to their technical ID.
|
||||||
* addition to their technical ID.
|
|
||||||
*/
|
*/
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PROTECTED) // Default constructor needed for JPA entities
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
@MappedSuperclass
|
@MappedSuperclass
|
||||||
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities
|
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities
|
||||||
@SuppressWarnings("squid:S2160")
|
@SuppressWarnings("squid:S2160")
|
||||||
@@ -40,39 +46,8 @@ public abstract class AbstractJpaNamedEntity extends AbstractJpaTenantAwareBaseE
|
|||||||
@Size(max = DESCRIPTION_MAX_SIZE)
|
@Size(max = DESCRIPTION_MAX_SIZE)
|
||||||
private String description;
|
private String description;
|
||||||
|
|
||||||
/**
|
protected AbstractJpaNamedEntity(final String name, final 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.name = name;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public String getDescription() {
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(final String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDescription(final String description) {
|
|
||||||
this.description = description;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,6 +17,10 @@ import jakarta.persistence.PrePersist;
|
|||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import jakarta.validation.constraints.Size;
|
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.exception.TenantNotExistException;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.helper.TenantAwareHolder;
|
import org.eclipse.hawkbit.repository.jpa.model.helper.TenantAwareHolder;
|
||||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||||
@@ -28,6 +32,7 @@ import org.eclipse.persistence.annotations.TenantDiscriminatorColumn;
|
|||||||
/**
|
/**
|
||||||
* Holder of the base attributes common to all tenant aware entities.
|
* Holder of the base attributes common to all tenant aware entities.
|
||||||
*/
|
*/
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PROTECTED) // Default constructor needed for JPA entities.
|
||||||
@MappedSuperclass
|
@MappedSuperclass
|
||||||
@TenantDiscriminatorColumn(name = "tenant", length = 40)
|
@TenantDiscriminatorColumn(name = "tenant", length = 40)
|
||||||
@Multitenant(MultitenantType.SINGLE_TABLE)
|
@Multitenant(MultitenantType.SINGLE_TABLE)
|
||||||
@@ -36,34 +41,16 @@ public abstract class AbstractJpaTenantAwareBaseEntity extends AbstractJpaBaseEn
|
|||||||
@Serial
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
@Column(name = "tenant", nullable = false, insertable = false, updatable = false, length = 40)
|
@Column(name = "tenant", nullable = false, insertable = false, updatable = false, length = 40)
|
||||||
@Size(min = 1, max = 40)
|
@Size(min = 1, max = 40)
|
||||||
@NotNull
|
@NotNull
|
||||||
private String tenant;
|
private String tenant;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor needed for JPA entities.
|
* 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.
|
||||||
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()
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
@@ -74,12 +61,9 @@ public abstract class AbstractJpaTenantAwareBaseEntity extends AbstractJpaBaseEn
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tenant aware entities extend the equals/hashcode strategy with the tenant
|
* Tenant aware entities extend the equals/hashcode strategy with the tenant name. That would allow for instance in a
|
||||||
* name. That would allow for instance in a multi-schema based data
|
* multi-schema based data separation setup to have the same primary key for different entities of
|
||||||
* separation setup to have the same primary key for different entities of
|
|
||||||
* different tenants.
|
* different tenants.
|
||||||
*
|
|
||||||
* @see org.eclipse.hawkbit.repository.model.BaseEntity#equals(java.lang.Object)
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
// exception squid:S2259 - obj is checked for null in super
|
// exception squid:S2259 - obj is checked for null in super
|
||||||
@@ -105,21 +89,18 @@ public abstract class AbstractJpaTenantAwareBaseEntity extends AbstractJpaBaseEn
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PrePersist listener method for all {@link TenantAwareBaseEntity}
|
* PrePersist listener method for all {@link TenantAwareBaseEntity} entities.
|
||||||
* entities.
|
|
||||||
*/
|
*/
|
||||||
@PrePersist
|
@PrePersist
|
||||||
void prePersist() {
|
void prePersist() {
|
||||||
// before persisting the entity check the current ID of the tenant by
|
// before persisting the entity check the current ID of the tenant by using the TenantAware service
|
||||||
// using the TenantAware
|
|
||||||
// service
|
|
||||||
final String currentTenant = SystemManagementHolder.getInstance().currentTenant();
|
final String currentTenant = SystemManagementHolder.getInstance().currentTenant();
|
||||||
if (currentTenant == null) {
|
if (currentTenant == null) {
|
||||||
throw new TenantNotExistException("Tenant "
|
throw new TenantNotExistException(
|
||||||
+ TenantAwareHolder.getInstance().getTenantAware().getCurrentTenant()
|
String.format(
|
||||||
+ " does not exists, cannot create entity " + this.getClass() + " with id " + super.getId());
|
"Tenant %s does not exists, cannot create entity %s with id %d",
|
||||||
|
TenantAwareHolder.getInstance().getTenantAware().getCurrentTenant(), getClass(), getId()));
|
||||||
}
|
}
|
||||||
setTenant(currentTenant.toUpperCase());
|
setTenant(currentTenant.toUpperCase());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
@@ -16,13 +16,19 @@ import jakarta.persistence.MappedSuperclass;
|
|||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import jakarta.validation.constraints.Size;
|
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.TenantAwareBaseEntity;
|
||||||
import org.eclipse.hawkbit.repository.model.Type;
|
import org.eclipse.hawkbit.repository.model.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link TenantAwareBaseEntity} extension for all entities that are named in
|
* {@link TenantAwareBaseEntity} extension for all entities that are named in addition to their technical ID.
|
||||||
* addition to their technical ID.
|
|
||||||
*/
|
*/
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PROTECTED) // Default constructor needed for JPA entities
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
@MappedSuperclass
|
@MappedSuperclass
|
||||||
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities
|
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities
|
||||||
@SuppressWarnings("squid:S2160")
|
@SuppressWarnings("squid:S2160")
|
||||||
@@ -40,40 +46,9 @@ public abstract class AbstractJpaTypeEntity extends AbstractJpaNamedEntity imple
|
|||||||
@Size(max = COLOUR_MAX_SIZE)
|
@Size(max = COLOUR_MAX_SIZE)
|
||||||
private String colour;
|
private String colour;
|
||||||
|
|
||||||
/**
|
protected AbstractJpaTypeEntity(final String name, final String description, final String key, final 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) {
|
|
||||||
super(name, description);
|
super(name, description);
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.colour = colour;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -23,8 +23,7 @@ import org.eclipse.hawkbit.repository.jpa.EntityInterceptor;
|
|||||||
import org.eclipse.hawkbit.repository.jpa.model.helper.EntityInterceptorHolder;
|
import org.eclipse.hawkbit.repository.jpa.model.helper.EntityInterceptorHolder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entity listener which calls the callback's of all registered entity
|
* Entity listener which calls the callback's of all registered entity interceptors.
|
||||||
* interceptors.
|
|
||||||
*/
|
*/
|
||||||
public class EntityInterceptorListener {
|
public class EntityInterceptorListener {
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,12 +9,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.repository.jpa.model;
|
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
|
* Interfaces which can be implemented by entities to be called when the entity should fire an event because the entity has been created,
|
||||||
* should fire an event because the entity has been created, updated or deleted.
|
* updated or deleted.
|
||||||
*/
|
*/
|
||||||
public interface EventAwareEntity {
|
public interface EventAwareEntity {
|
||||||
|
|
||||||
@@ -32,11 +29,4 @@ public interface EventAwareEntity {
|
|||||||
* Fired for the Entity deletion.
|
* Fired for the Entity deletion.
|
||||||
*/
|
*/
|
||||||
void fireDeleteEvent();
|
void fireDeleteEvent();
|
||||||
|
}
|
||||||
/**
|
|
||||||
* @return list of entity fields that if the only changed fields prevents {@link #fireUpdateEvent()} call.
|
|
||||||
*/
|
|
||||||
default List<String> getUpdateIgnoreFields() {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -55,11 +55,12 @@ import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
|
|||||||
@Getter
|
@Getter
|
||||||
@ToString(callSuper = true)
|
@ToString(callSuper = true)
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "sp_base_software_module", uniqueConstraints = @UniqueConstraint(columnNames = { "module_type", "name",
|
@Table(name = "sp_base_software_module",
|
||||||
"version", "tenant" }, name = "uk_base_sw_mod"), indexes = {
|
uniqueConstraints = @UniqueConstraint(columnNames = { "module_type", "name", "version", "tenant" }, name = "uk_base_sw_mod"),
|
||||||
@Index(name = "sp_idx_base_sw_module_01", columnList = "tenant,deleted,name,version"),
|
indexes = {
|
||||||
@Index(name = "sp_idx_base_sw_module_02", columnList = "tenant,deleted,module_type"),
|
@Index(name = "sp_idx_base_sw_module_01", columnList = "tenant,deleted,name,version"),
|
||||||
@Index(name = "sp_idx_base_sw_module_prim", columnList = "tenant,id") })
|
@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") })
|
@NamedEntityGraph(name = "SoftwareModule.artifacts", attributeNodes = { @NamedAttributeNode("artifacts") })
|
||||||
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities
|
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities
|
||||||
@SuppressWarnings("squid:S2160")
|
@SuppressWarnings("squid:S2160")
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import jakarta.persistence.Convert;
|
|||||||
import jakarta.persistence.Converter;
|
import jakarta.persistence.Converter;
|
||||||
import jakarta.persistence.ElementCollection;
|
import jakarta.persistence.ElementCollection;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.EntityListeners;
|
||||||
import jakarta.persistence.FetchType;
|
import jakarta.persistence.FetchType;
|
||||||
import jakarta.persistence.ForeignKey;
|
import jakarta.persistence.ForeignKey;
|
||||||
import jakarta.persistence.Index;
|
import jakarta.persistence.Index;
|
||||||
@@ -44,6 +45,10 @@ import jakarta.validation.constraints.NotNull;
|
|||||||
import jakarta.validation.constraints.Pattern;
|
import jakarta.validation.constraints.Pattern;
|
||||||
import jakarta.validation.constraints.Size;
|
import jakarta.validation.constraints.Size;
|
||||||
|
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
|
||||||
@@ -52,8 +57,6 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
|||||||
import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHolder;
|
import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHolder;
|
||||||
import org.eclipse.hawkbit.repository.jpa.utils.MapAttributeConverter;
|
import org.eclipse.hawkbit.repository.jpa.utils.MapAttributeConverter;
|
||||||
import org.eclipse.hawkbit.repository.model.Action;
|
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.PollStatus;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||||
@@ -64,78 +67,97 @@ import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
|
|||||||
import org.eclipse.hawkbit.repository.model.helper.SystemSecurityContextHolder;
|
import org.eclipse.hawkbit.repository.model.helper.SystemSecurityContextHolder;
|
||||||
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
|
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
|
||||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
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}.
|
* JPA implementation of {@link Target}.
|
||||||
*/
|
*/
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PUBLIC) // empty constructor for JPA.
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "sp_target", indexes = {
|
@Table(name = "sp_target",
|
||||||
@Index(name = "sp_idx_target_01", columnList = "tenant,name,assigned_distribution_set"),
|
indexes = {
|
||||||
@Index(name = "sp_idx_target_03", columnList = "tenant,controller_id,assigned_distribution_set"),
|
@Index(name = "sp_idx_target_01", columnList = "tenant,name,assigned_distribution_set"),
|
||||||
@Index(name = "sp_idx_target_04", columnList = "tenant,created_at"),
|
@Index(name = "sp_idx_target_03", columnList = "tenant,controller_id,assigned_distribution_set"),
|
||||||
@Index(name = "sp_idx_target_05", columnList = "tenant,last_modified_at"),
|
@Index(name = "sp_idx_target_04", columnList = "tenant,created_at"),
|
||||||
@Index(name = "sp_idx_target_prim", columnList = "tenant,id") }, uniqueConstraints = @UniqueConstraint(columnNames = {
|
@Index(name = "sp_idx_target_05", columnList = "tenant,last_modified_at"),
|
||||||
"controller_id", "tenant" }, name = "uk_tenant_controller_id"))
|
@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
|
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for sub entities
|
||||||
@SuppressWarnings("squid:S2160")
|
@SuppressWarnings("squid:S2160")
|
||||||
|
@EntityListeners({ JpaTarget.EntityPropertyChangeListener.class }) // add listener to the listeners declared into suppers
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAwareEntity {
|
public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAwareEntity {
|
||||||
|
|
||||||
@Serial
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private static final List<String> 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)
|
@Column(name = "controller_id", length = Target.CONTROLLER_ID_MAX_SIZE, updatable = false, nullable = false)
|
||||||
@Size(min = 1, max = Target.CONTROLLER_ID_MAX_SIZE)
|
@Size(min = 1, max = Target.CONTROLLER_ID_MAX_SIZE)
|
||||||
@NotNull
|
@NotNull
|
||||||
@Pattern(regexp = "[\\S]*", message = "has whitespaces which are not allowed")
|
@Pattern(regexp = "[\\S]*", message = "has whitespaces which are not allowed")
|
||||||
private String controllerId;
|
private String controllerId;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE }, targetEntity = JpaAction.class)
|
/** the security token of the target which allows if enabled to authenticate with this security token */
|
||||||
private List<JpaAction> actions;
|
@Setter
|
||||||
|
|
||||||
/**
|
|
||||||
* the security token of the target which allows if enabled to authenticate
|
|
||||||
* with this security token.
|
|
||||||
*/
|
|
||||||
@Column(name = "sec_token", nullable = false, length = Target.SECURITY_TOKEN_MAX_SIZE)
|
@Column(name = "sec_token", nullable = false, length = Target.SECURITY_TOKEN_MAX_SIZE)
|
||||||
@Size(min = 1, max = Target.SECURITY_TOKEN_MAX_SIZE)
|
@Size(min = 1, max = Target.SECURITY_TOKEN_MAX_SIZE)
|
||||||
@NotNull
|
@NotNull
|
||||||
private String securityToken;
|
private String securityToken;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.REMOVE })
|
@Setter
|
||||||
private List<RolloutTargetGroup> rolloutTargetGroup;
|
|
||||||
|
|
||||||
@Column(name = "address", length = Target.ADDRESS_MAX_SIZE)
|
@Column(name = "address", length = Target.ADDRESS_MAX_SIZE)
|
||||||
@Size(max = Target.ADDRESS_MAX_SIZE)
|
@Size(max = Target.ADDRESS_MAX_SIZE)
|
||||||
private String address;
|
private String address;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
@Column(name = "last_target_query")
|
@Column(name = "last_target_query")
|
||||||
private Long lastTargetQuery;
|
private Long lastTargetQuery;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
@Column(name = "install_date")
|
@Column(name = "install_date")
|
||||||
private Long installationDate;
|
private Long installationDate;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
@Column(name = "update_status", nullable = false)
|
@Column(name = "update_status", nullable = false)
|
||||||
@Convert(converter = TargetUpdateStatusConverter.class)
|
@Convert(converter = TargetUpdateStatusConverter.class)
|
||||||
@NotNull
|
@NotNull
|
||||||
private TargetUpdateStatus updateStatus = TargetUpdateStatus.UNKNOWN;
|
private TargetUpdateStatus updateStatus = TargetUpdateStatus.UNKNOWN;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
@ManyToOne(optional = true, fetch = FetchType.LAZY)
|
@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;
|
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;
|
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)
|
@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;
|
private boolean requestControllerAttributes = true;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
@OneToOne(fetch = FetchType.LAZY, mappedBy = "target", orphanRemoval = true)
|
@OneToOne(fetch = FetchType.LAZY, mappedBy = "target", orphanRemoval = true)
|
||||||
@PrimaryKeyJoinColumn
|
@PrimaryKeyJoinColumn
|
||||||
private JpaAutoConfirmationStatus autoConfirmationStatus;
|
private JpaAutoConfirmationStatus autoConfirmationStatus;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
@ManyToOne(fetch = FetchType.LAZY, targetEntity = JpaTargetType.class)
|
@ManyToOne(fetch = FetchType.LAZY, targetEntity = JpaTargetType.class)
|
||||||
@JoinColumn(name = "target_type", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_relation_target_type"))
|
@JoinColumn(name = "target_type", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_relation_target_type"))
|
||||||
private TargetType targetType;
|
private TargetType targetType;
|
||||||
|
|
||||||
@ManyToMany(targetEntity = JpaTargetTag.class)
|
@ManyToMany(targetEntity = JpaTargetTag.class)
|
||||||
@JoinTable(
|
@JoinTable(
|
||||||
name = "sp_target_target_tag",
|
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"))
|
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_targtag_tag"))
|
||||||
})
|
})
|
||||||
private Set<TargetTag> tags;
|
private Set<TargetTag> 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.
|
// no cascade option on an ElementCollection, the target objects are always persisted, merged, removed with their parent.
|
||||||
|
@Getter
|
||||||
@ElementCollection
|
@ElementCollection
|
||||||
@Column(name = "attribute_value", length = Target.CONTROLLER_ATTRIBUTE_VALUE_SIZE)
|
@Column(name = "attribute_value", length = Target.CONTROLLER_ATTRIBUTE_VALUE_SIZE)
|
||||||
@MapKeyColumn(name = "attribute_key", nullable = false, length = Target.CONTROLLER_ATTRIBUTE_KEY_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) },
|
joinColumns = { @JoinColumn(name = "target_id", nullable = false, insertable = false, updatable = false) },
|
||||||
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_attrib_target"))
|
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_attrib_target"))
|
||||||
private Map<String, String> controllerAttributes;
|
private Map<String, String> controllerAttributes;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE }, targetEntity = JpaTargetMetadata.class)
|
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE }, targetEntity = JpaTargetMetadata.class)
|
||||||
private List<TargetMetadata> metadata;
|
private List<TargetMetadata> metadata;
|
||||||
|
|
||||||
/**
|
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE }, targetEntity = JpaAction.class)
|
||||||
* Constructor.
|
private List<JpaAction> actions;
|
||||||
*
|
|
||||||
* @param controllerId controller ID of the {@link Target}
|
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.REMOVE })
|
||||||
*/
|
private List<RolloutTargetGroup> rolloutTargetGroup;
|
||||||
|
|
||||||
public JpaTarget(final String controllerId) {
|
public JpaTarget(final String controllerId) {
|
||||||
this(controllerId, SecurityTokenGeneratorHolder.getInstance().generateToken());
|
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) {
|
public JpaTarget(final String controllerId, final String securityToken) {
|
||||||
this.controllerId = controllerId;
|
this.controllerId = controllerId;
|
||||||
// truncate controller ID to max name length (if needed)
|
// truncate controller ID to max name length (if needed)
|
||||||
@@ -186,63 +203,20 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
|||||||
this.securityToken = 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
|
@Override
|
||||||
public String getControllerId() {
|
public String getControllerId() {
|
||||||
return controllerId;
|
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
|
@Override
|
||||||
public String getSecurityToken() {
|
public String getSecurityToken() {
|
||||||
final SystemSecurityContext systemSecurityContext =
|
final SystemSecurityContext systemSecurityContext = SystemSecurityContextHolder.getInstance().getSystemSecurityContext();
|
||||||
SystemSecurityContextHolder.getInstance().getSystemSecurityContext();
|
if (systemSecurityContext.isCurrentThreadSystemCode() || systemSecurityContext.hasPermission(SpPermission.READ_TARGET_SEC_TOKEN)) {
|
||||||
if (systemSecurityContext.isCurrentThreadSystemCode() ||
|
|
||||||
systemSecurityContext.hasPermission(SpPermission.READ_TARGET_SEC_TOKEN)) {
|
|
||||||
return securityToken;
|
return securityToken;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param securityToken token value
|
|
||||||
*/
|
|
||||||
public void setSecurityToken(final String securityToken) {
|
|
||||||
this.securityToken = securityToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the ipAddress
|
* @return the ipAddress
|
||||||
*/
|
*/
|
||||||
@@ -259,31 +233,9 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@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
|
* @return the poll time which holds the last poll time of the target, the next poll time and the overdue time. In case 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}
|
||||||
* {@link #lastTargetQuery} is not set e.g. the target never polled
|
|
||||||
* before this method returns {@code null}
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public PollStatus getPollStatus() {
|
public PollStatus getPollStatus() {
|
||||||
@@ -292,89 +244,10 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return TenantConfigurationManagementHolder.getInstance().getTenantConfigurationManagement()
|
return TenantConfigurationManagementHolder.getInstance().getTenantConfigurationManagement()
|
||||||
.pollStatusResolver().apply(this);
|
.pollStatusResolver()
|
||||||
|
.apply(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public AutoConfirmationStatus getAutoConfirmationStatus() {
|
|
||||||
return autoConfirmationStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
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<TargetTag> getTags() {
|
|
||||||
if (tags == null) {
|
|
||||||
return Collections.emptySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Collections.unmodifiableSet(tags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return rollouts target group
|
|
||||||
*/
|
|
||||||
public List<RolloutTargetGroup> getRolloutTargetGroup() {
|
|
||||||
if (rolloutTargetGroup == null) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return Collections.unmodifiableList(rolloutTargetGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param tag to be added
|
|
||||||
*/
|
|
||||||
public void addTag(final TargetTag tag) {
|
public void addTag(final TargetTag tag) {
|
||||||
if (tags == null) {
|
if (tags == null) {
|
||||||
tags = new HashSet<>();
|
tags = new HashSet<>();
|
||||||
@@ -382,29 +255,20 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
|||||||
tags.add(tag);
|
tags.add(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param tag the tag to be removed from the target
|
|
||||||
*/
|
|
||||||
public void removeTag(final TargetTag tag) {
|
public void removeTag(final TargetTag tag) {
|
||||||
if (tags != null) {
|
if (tags != null) {
|
||||||
tags.remove(tag);
|
tags.remove(tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public Set<TargetTag> getTags() {
|
||||||
* @return list of action
|
return tags == null ? Collections.emptySet() : Collections.unmodifiableSet(tags);
|
||||||
*/
|
}
|
||||||
public List<Action> getActions() {
|
|
||||||
if (actions == null) {
|
public List<TargetMetadata> getMetadata() {
|
||||||
return Collections.emptyList();
|
return metadata == null ? Collections.emptyList() : Collections.unmodifiableList(metadata);
|
||||||
}
|
|
||||||
|
|
||||||
return Collections.unmodifiableList(actions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param action Action
|
|
||||||
*/
|
|
||||||
public void addAction(final Action action) {
|
public void addAction(final Action action) {
|
||||||
if (actions == null) {
|
if (actions == null) {
|
||||||
actions = new ArrayList<>();
|
actions = new ArrayList<>();
|
||||||
@@ -412,42 +276,8 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
|||||||
actions.add((JpaAction) action);
|
actions.add((JpaAction) action);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public List<Action> getActions() {
|
||||||
* @return Distribution set
|
return actions == null ? Collections.emptyList() : Collections.unmodifiableList(actions);
|
||||||
*/
|
|
||||||
public JpaDistributionSet getInstalledDistributionSet() {
|
|
||||||
return installedDistributionSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param installedDistributionSet Distribution set
|
|
||||||
*/
|
|
||||||
public void setInstalledDistributionSet(final JpaDistributionSet installedDistributionSet) {
|
|
||||||
this.installedDistributionSet = installedDistributionSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return controller attributes
|
|
||||||
*/
|
|
||||||
public Map<String, String> getControllerAttributes() {
|
|
||||||
return controllerAttributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return target metadata
|
|
||||||
*/
|
|
||||||
public List<TargetMetadata> getMetadata() {
|
|
||||||
if (metadata == null) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Collections.unmodifiableList(metadata);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "JpaTarget [controllerId=" + controllerId + ", revision=" + getOptLockRevision() + ", id=" + getId()
|
|
||||||
+ "]";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -469,9 +299,20 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
|||||||
getClass(), EventPublisherHolder.getInstance().getApplicationId()));
|
getClass(), EventPublisherHolder.getInstance().getApplicationId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<RolloutTargetGroup> getRolloutTargetGroup() {
|
||||||
|
return rolloutTargetGroup == null ? Collections.emptyList() : Collections.unmodifiableList(rolloutTargetGroup);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getUpdateIgnoreFields() {
|
public String toString() {
|
||||||
return TARGET_UPDATE_EVENT_IGNORE_FIELDS;
|
return "JpaTarget [controllerId=" + controllerId + ", revision=" + getOptLockRevision() + ", id=" + getId() + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
// if "lastTargetQuery", "address" only are changed - the remove events are skipped
|
||||||
|
@Override
|
||||||
|
public void postUpdate() {
|
||||||
|
// do nothing - processed by EntityPropertyChangeListener
|
||||||
|
// since we want to skip the event if only "lastTargetQuery" or "address" are changed
|
||||||
}
|
}
|
||||||
|
|
||||||
@Converter
|
@Converter
|
||||||
@@ -487,4 +328,24 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
|||||||
), null);
|
), 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<String> 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,15 +9,16 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.repository.jpa.model.helper;
|
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.executor.AfterTransactionCommitExecutor;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.EntityPropertyChangeListener;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A singleton bean which holds the {@link AfterTransactionCommitExecutor} to
|
* A singleton bean which holds the {@link AfterTransactionCommitExecutor} to provide it to in beans not instantiated by spring e.g. JPA
|
||||||
* have to the cache manager in beans not instantiated by spring e.g. JPA
|
* entities which cannot be autowired.
|
||||||
* entities or {@link EntityPropertyChangeListener} which cannot be autowired.
|
|
||||||
*/
|
*/
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
public final class AfterTransactionCommitExecutorHolder {
|
public final class AfterTransactionCommitExecutorHolder {
|
||||||
|
|
||||||
private static final AfterTransactionCommitExecutorHolder SINGLETON = new AfterTransactionCommitExecutorHolder();
|
private static final AfterTransactionCommitExecutorHolder SINGLETON = new AfterTransactionCommitExecutorHolder();
|
||||||
@@ -25,10 +26,6 @@ public final class AfterTransactionCommitExecutorHolder {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private AfterTransactionCommitExecutor afterCommit;
|
private AfterTransactionCommitExecutor afterCommit;
|
||||||
|
|
||||||
private AfterTransactionCommitExecutorHolder() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the cache manager holder singleton instance
|
* @return the cache manager holder singleton instance
|
||||||
*/
|
*/
|
||||||
@@ -49,5 +46,4 @@ public final class AfterTransactionCommitExecutorHolder {
|
|||||||
public void setAfterCommit(final AfterTransactionCommitExecutor afterCommit) {
|
public void setAfterCommit(final AfterTransactionCommitExecutor afterCommit) {
|
||||||
this.afterCommit = afterCommit;
|
this.afterCommit = afterCommit;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
@@ -18,6 +18,7 @@ import lombok.NoArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
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.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.model.JpaTarget;
|
||||||
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
|
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
|
||||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
|
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
|
||||||
@@ -68,7 +69,7 @@ public final class DeploymentHelper {
|
|||||||
target.setAssignedDistributionSet(target.getInstalledDistributionSet());
|
target.setAssignedDistributionSet(target.getInstalledDistributionSet());
|
||||||
target.setUpdateStatus(TargetUpdateStatus.IN_SYNC);
|
target.setUpdateStatus(TargetUpdateStatus.IN_SYNC);
|
||||||
} else {
|
} else {
|
||||||
target.setAssignedDistributionSet(nextActiveActions.get(0).getDistributionSet());
|
target.setAssignedDistributionSet((JpaDistributionSet) nextActiveActions.get(0).getDistributionSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
targetRepository.save(target);
|
targetRepository.save(target);
|
||||||
|
|||||||
@@ -622,22 +622,25 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
@Description("Register a controller which does not exist")
|
@Description("Register a controller which does not exist")
|
||||||
@WithUser(principal = "controller", authorities = { CONTROLLER_ROLE })
|
@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) })
|
@Expect(type = TargetPollEvent.class, count = 2) })
|
||||||
void findOrRegisterTargetIfItDoesNotExist() {
|
void findOrRegisterTargetIfItDoesNotExist() {
|
||||||
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST);
|
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST);
|
||||||
assertThat(target).as("target should not be null").isNotNull();
|
assertThat(target).as("target should not be null").isNotNull();
|
||||||
|
|
||||||
final Target sameTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST);
|
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);
|
assertThat(targetRepository.count()).as("Only 1 target should be registered").isEqualTo(1L);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Register a controller with name which does not exist and update its name")
|
@Description("Register a controller with name which does not exist and update its name")
|
||||||
@WithUser(principal = "controller", authorities = { CONTROLLER_ROLE })
|
@WithUser(principal = "controller", authorities = { CONTROLLER_ROLE })
|
||||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
@ExpectEvents({
|
||||||
@Expect(type = TargetPollEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 1) })
|
@Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetPollEvent.class, count = 2),
|
||||||
|
@Expect(type = TargetUpdatedEvent.class, count = 1) })
|
||||||
void findOrRegisterTargetIfItDoesNotExistWithName() {
|
void findOrRegisterTargetIfItDoesNotExistWithName() {
|
||||||
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST, "TestName", null);
|
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST, "TestName", null);
|
||||||
final Target sameTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST,
|
final Target sameTarget = controllerManagement.findOrRegisterTargetIfItDoesNotExist("AA", LOCALHOST,
|
||||||
@@ -1085,10 +1088,10 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Ensures that target attributes can be updated using different update modes.")
|
@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) })
|
@Expect(type = TargetUpdatedEvent.class, count = 4) })
|
||||||
void updateTargetAttributesWithDifferentUpdateModes() {
|
void updateTargetAttributesWithDifferentUpdateModes() {
|
||||||
|
|
||||||
final String controllerId = "testCtrl";
|
final String controllerId = "testCtrl";
|
||||||
testdataFactory.createTarget(controllerId);
|
testdataFactory.createTarget(controllerId);
|
||||||
|
|
||||||
@@ -1103,7 +1106,6 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
// update mode REMOVE
|
// update mode REMOVE
|
||||||
updateTargetAttributesWithUpdateModeRemove(controllerId);
|
updateTargetAttributesWithUpdateModeRemove(controllerId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1710,7 +1712,6 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
@Step
|
@Step
|
||||||
private void updateTargetAttributesWithUpdateModeRemove(final String controllerId) {
|
private void updateTargetAttributesWithUpdateModeRemove(final String controllerId) {
|
||||||
|
|
||||||
final int previousSize = targetManagement.getControllerAttributes(controllerId).size();
|
final int previousSize = targetManagement.getControllerAttributes(controllerId).size();
|
||||||
|
|
||||||
// update the attributes using update mode REMOVE
|
// update the attributes using update mode REMOVE
|
||||||
@@ -1723,14 +1724,12 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
final Map<String, String> updatedAttributes = targetManagement.getControllerAttributes(controllerId);
|
final Map<String, String> updatedAttributes = targetManagement.getControllerAttributes(controllerId);
|
||||||
assertThat(updatedAttributes.size()).isEqualTo(previousSize - 2);
|
assertThat(updatedAttributes.size()).isEqualTo(previousSize - 2);
|
||||||
assertThat(updatedAttributes).doesNotContainKeys("k1", "k3");
|
assertThat(updatedAttributes).doesNotContainKeys("k1", "k3");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Step
|
@Step
|
||||||
private void updateTargetAttributesWithUpdateModeMerge(final String controllerId) {
|
private void updateTargetAttributesWithUpdateModeMerge(final String controllerId) {
|
||||||
// get the current attributes
|
// get the current attributes
|
||||||
final HashMap<String, String> attributes = new HashMap<>(
|
final HashMap<String, String> attributes = new HashMap<>(targetManagement.getControllerAttributes(controllerId));
|
||||||
targetManagement.getControllerAttributes(controllerId));
|
|
||||||
|
|
||||||
// update the attributes using update mode MERGE
|
// update the attributes using update mode MERGE
|
||||||
final Map<String, String> mergeAttributes = new HashMap<>();
|
final Map<String, String> mergeAttributes = new HashMap<>();
|
||||||
@@ -1748,10 +1747,8 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
@Step
|
@Step
|
||||||
private void updateTargetAttributesWithUpdateModeReplace(final String controllerId) {
|
private void updateTargetAttributesWithUpdateModeReplace(final String controllerId) {
|
||||||
|
|
||||||
// get the current attributes
|
// get the current attributes
|
||||||
final HashMap<String, String> attributes = new HashMap<>(
|
final HashMap<String, String> attributes = new HashMap<>(targetManagement.getControllerAttributes(controllerId));
|
||||||
targetManagement.getControllerAttributes(controllerId));
|
|
||||||
|
|
||||||
// update the attributes using update mode REPLACE
|
// update the attributes using update mode REPLACE
|
||||||
final Map<String, String> replacementAttributes = new HashMap<>();
|
final Map<String, String> replacementAttributes = new HashMap<>();
|
||||||
@@ -1770,7 +1767,6 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
@Step
|
@Step
|
||||||
private void updateTargetAttributesWithoutUpdateMode(final String controllerId) {
|
private void updateTargetAttributesWithoutUpdateMode(final String controllerId) {
|
||||||
|
|
||||||
// set the initial attributes
|
// set the initial attributes
|
||||||
final Map<String, String> attributes = new HashMap<>();
|
final Map<String, String> attributes = new HashMap<>();
|
||||||
attributes.put("k0", "v0");
|
attributes.put("k0", "v0");
|
||||||
|
|||||||
@@ -58,21 +58,20 @@ public class EventVerifier extends AbstractTestExecutionListener {
|
|||||||
* executor in the ApplicationEventMultiCaster, so the order of the events
|
* executor in the ApplicationEventMultiCaster, so the order of the events
|
||||||
* keep the same.
|
* keep the same.
|
||||||
*
|
*
|
||||||
* @param publisher the {@link ApplicationEventPublisher} to publish the marker
|
* @param publisher the {@link ApplicationEventPublisher} to publish the marker event to
|
||||||
* event to
|
|
||||||
*/
|
*/
|
||||||
public static void publishResetMarkerEvent(final ApplicationEventPublisher publisher) {
|
public static void publishResetMarkerEvent(final ApplicationEventPublisher publisher) {
|
||||||
publisher.publishEvent(new ResetCounterMarkerEvent());
|
publisher.publishEvent(new ResetCounterMarkerEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beforeTestMethod(final TestContext testContext) throws Exception {
|
public void beforeTestMethod(final TestContext testContext) {
|
||||||
final Optional<Expect[]> expectedEvents = getExpectationsFrom(testContext.getTestMethod());
|
final Optional<Expect[]> expectedEvents = getExpectationsFrom(testContext.getTestMethod());
|
||||||
expectedEvents.ifPresent(events -> beforeTest(testContext));
|
expectedEvents.ifPresent(events -> beforeTest(testContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterTestMethod(final TestContext testContext) throws Exception {
|
public void afterTestMethod(final TestContext testContext) {
|
||||||
final Optional<Expect[]> expectedEvents = getExpectationsFrom(testContext.getTestMethod());
|
final Optional<Expect[]> expectedEvents = getExpectationsFrom(testContext.getTestMethod());
|
||||||
try {
|
try {
|
||||||
expectedEvents.ifPresent(events -> afterTest(events));
|
expectedEvents.ifPresent(events -> afterTest(events));
|
||||||
|
|||||||
Reference in New Issue
Block a user