Skip to content

Commit

Permalink
Switch (mostly) to JPA generic events (#2104)
Browse files Browse the repository at this point in the history
instead of EclipseLink specific

Signed-off-by: Avgustin Marinov <[email protected]>
  • Loading branch information
avgustinmm authored Nov 26, 2024
1 parent 46c83fe commit d435c0a
Show file tree
Hide file tree
Showing 18 changed files with 218 additions and 480 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String> attributes,
UpdateMode mode);
Target updateControllerAttributes(@NotEmpty String controllerId, @NotNull Map<String, String> attributes, UpdateMode mode);

/**
* Finds {@link Target} based on given controller ID returns found Target
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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 <code>null</code> if target
* @return time in {@link TimeUnit#MILLISECONDS} GMT when the {@link Target} polled the server the last time or <code>null</code> if target
* has never queried yet.
*/
Long getLastTargetQuery();
Expand Down Expand Up @@ -113,4 +114,4 @@ default TargetWithActionType getTargetWithActionType() {
* {@link #getControllerAttributes()}.
*/
boolean isRequestControllerAttributes();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String> 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<String, String> 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();
}
Expand Down Expand Up @@ -889,7 +884,7 @@ private Optional<String> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1009,7 +1009,7 @@ private List<JpaAction> activateActionsOfRolloutGroup(final List<JpaAction> acti
private void setAssignmentOnTargets(final List<JpaAction> actions) {
final List<JpaTarget> 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());
Expand All @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
Expand All @@ -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() {
Expand All @@ -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
Expand All @@ -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());
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down
Loading

0 comments on commit d435c0a

Please sign in to comment.