1. add _locked_ column in the tables 2. add _locked_ field to the JPA entities 3. add lock set/get at JPA level (with lock DS checking for complete) 4. along with that little reordering of code is made to make it more structured Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -204,7 +204,7 @@ public enum SpServerError {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SP_DS_INCOMPLETE("hawkbit.server.error.distributionset.incomplete",
|
SP_DS_INCOMPLETE("hawkbit.server.error.distributionset.incomplete",
|
||||||
"Distribution set is assigned to a target that is incomplete (i.e. mandatory modules are missing)"),
|
"Distribution set is assigned/locked to a target that is incomplete (i.e. mandatory modules are missing)"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -83,9 +83,10 @@ public class JpaArtifact extends AbstractJpaTenantAwareBaseEntity implements Art
|
|||||||
*/
|
*/
|
||||||
public JpaArtifact(@NotEmpty final String sha1Hash, @NotNull final String filename,
|
public JpaArtifact(@NotEmpty final String sha1Hash, @NotNull final String filename,
|
||||||
final SoftwareModule softwareModule) {
|
final SoftwareModule softwareModule) {
|
||||||
setSoftwareModule(softwareModule);
|
|
||||||
this.sha1Hash = sha1Hash;
|
this.sha1Hash = sha1Hash;
|
||||||
this.filename = filename;
|
this.filename = filename;
|
||||||
|
this.softwareModule = (JpaSoftwareModule) softwareModule;
|
||||||
|
this.softwareModule.addArtifact(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -130,11 +131,6 @@ public class JpaArtifact extends AbstractJpaTenantAwareBaseEntity implements Art
|
|||||||
return softwareModule;
|
return softwareModule;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void setSoftwareModule(final SoftwareModule softwareModule) {
|
|
||||||
this.softwareModule = (JpaSoftwareModule) softwareModule;
|
|
||||||
this.softwareModule.addArtifact(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getFilename() {
|
public String getFilename() {
|
||||||
return filename;
|
return filename;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.repository.jpa.model;
|
package org.eclipse.hawkbit.repository.jpa.model;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -34,10 +35,15 @@ import jakarta.persistence.Table;
|
|||||||
import jakarta.persistence.UniqueConstraint;
|
import jakarta.persistence.UniqueConstraint;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.ToString;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.DistributionSetDeletedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.DistributionSetDeletedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.exception.DistributionSetTypeUndefinedException;
|
import org.eclipse.hawkbit.repository.exception.DistributionSetTypeUndefinedException;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||||
import org.eclipse.hawkbit.repository.exception.UnsupportedSoftwareModuleForThisDistributionSetException;
|
import org.eclipse.hawkbit.repository.exception.UnsupportedSoftwareModuleForThisDistributionSetException;
|
||||||
import org.eclipse.hawkbit.repository.model.Action;
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
@@ -56,8 +62,10 @@ import org.springframework.context.ApplicationEvent;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Jpa implementation of {@link DistributionSet}.
|
* Jpa implementation of {@link DistributionSet}.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
@NoArgsConstructor // Default constructor for JPA
|
||||||
|
@Getter
|
||||||
|
@ToString(callSuper = true)
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "sp_distribution_set", uniqueConstraints = {
|
@Table(name = "sp_distribution_set", uniqueConstraints = {
|
||||||
@UniqueConstraint(columnNames = { "name", "version", "tenant" }, name = "uk_distrib_set") }, indexes = {
|
@UniqueConstraint(columnNames = { "name", "version", "tenant" }, name = "uk_distrib_set") }, indexes = {
|
||||||
@@ -69,12 +77,16 @@ import org.springframework.context.ApplicationEvent;
|
|||||||
// sub entities
|
// sub entities
|
||||||
@SuppressWarnings("squid:S2160")
|
@SuppressWarnings("squid:S2160")
|
||||||
public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implements DistributionSet, EventAwareEntity {
|
public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implements DistributionSet, EventAwareEntity {
|
||||||
|
|
||||||
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private static final String DELETED_PROPERTY = "deleted";
|
private static final String DELETED_PROPERTY = "deleted";
|
||||||
|
|
||||||
@Column(name = "required_migration_step")
|
@ManyToOne(fetch = FetchType.LAZY, optional = false, targetEntity = JpaDistributionSetType.class)
|
||||||
private boolean requiredMigrationStep;
|
@JoinColumn(name = "ds_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstype_ds"))
|
||||||
|
@NotNull
|
||||||
|
private DistributionSetType type;
|
||||||
|
|
||||||
@CascadeOnDelete
|
@CascadeOnDelete
|
||||||
@ManyToMany(targetEntity = JpaSoftwareModule.class, fetch = FetchType.LAZY)
|
@ManyToMany(targetEntity = JpaSoftwareModule.class, fetch = FetchType.LAZY)
|
||||||
@@ -87,97 +99,126 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
|
|||||||
@ManyToMany(targetEntity = JpaDistributionSetTag.class)
|
@ManyToMany(targetEntity = JpaDistributionSetTag.class)
|
||||||
@JoinTable(name = "sp_ds_dstag", joinColumns = {
|
@JoinTable(name = "sp_ds_dstag", joinColumns = {
|
||||||
@JoinColumn(name = "ds", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstag_ds")) }, inverseJoinColumns = {
|
@JoinColumn(name = "ds", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstag_ds")) }, inverseJoinColumns = {
|
||||||
@JoinColumn(name = "TAG", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstag_tag")) })
|
@JoinColumn(name = "TAG", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstag_tag")) })
|
||||||
private Set<DistributionSetTag> tags;
|
private Set<DistributionSetTag> tags;
|
||||||
|
|
||||||
@Column(name = "deleted")
|
@ToString.Exclude
|
||||||
private boolean deleted;
|
|
||||||
|
|
||||||
@OneToMany(mappedBy = "assignedDistributionSet", targetEntity = JpaTarget.class, fetch = FetchType.LAZY)
|
|
||||||
private List<JpaTarget> assignedToTargets;
|
|
||||||
|
|
||||||
@OneToMany(mappedBy = "autoAssignDistributionSet", targetEntity = JpaTargetFilterQuery.class, fetch = FetchType.LAZY)
|
|
||||||
private List<TargetFilterQuery> autoAssignFilters;
|
|
||||||
|
|
||||||
@OneToMany(mappedBy = "installedDistributionSet", targetEntity = JpaTarget.class, fetch = FetchType.LAZY)
|
|
||||||
private List<JpaTarget> installedAtTargets;
|
|
||||||
|
|
||||||
@OneToMany(mappedBy = "distributionSet", targetEntity = JpaAction.class, fetch = FetchType.LAZY)
|
|
||||||
private List<JpaAction> actions;
|
|
||||||
|
|
||||||
@CascadeOnDelete
|
@CascadeOnDelete
|
||||||
@OneToMany(mappedBy = "distributionSet", fetch = FetchType.LAZY, targetEntity = JpaDistributionSetMetadata.class)
|
@OneToMany(mappedBy = "distributionSet", fetch = FetchType.LAZY, targetEntity = JpaDistributionSetMetadata.class)
|
||||||
private List<DistributionSetMetadata> metadata;
|
private List<DistributionSetMetadata> metadata;
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY, optional = false, targetEntity = JpaDistributionSetType.class)
|
|
||||||
@JoinColumn(name = "ds_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstype_ds"))
|
|
||||||
@NotNull
|
|
||||||
private DistributionSetType type;
|
|
||||||
|
|
||||||
@Column(name = "complete")
|
@Column(name = "complete")
|
||||||
private boolean complete;
|
private boolean complete;
|
||||||
|
|
||||||
|
@Column(name = "locked")
|
||||||
|
private boolean locked;
|
||||||
|
|
||||||
|
@Column(name = "deleted")
|
||||||
|
private boolean deleted;
|
||||||
|
|
||||||
@Column(name = "valid")
|
@Column(name = "valid")
|
||||||
private boolean valid;
|
private boolean valid;
|
||||||
|
|
||||||
/**
|
@Column(name = "required_migration_step")
|
||||||
* Default constructor.
|
private boolean requiredMigrationStep;
|
||||||
*/
|
|
||||||
public JpaDistributionSet() {
|
@ToString.Exclude
|
||||||
// Default constructor for JPA
|
@OneToMany(mappedBy = "autoAssignDistributionSet", targetEntity = JpaTargetFilterQuery.class, fetch = FetchType.LAZY)
|
||||||
}
|
private List<TargetFilterQuery> autoAssignFilters;
|
||||||
|
|
||||||
|
@ToString.Exclude
|
||||||
|
@Getter(AccessLevel.NONE)
|
||||||
|
@OneToMany(mappedBy = "assignedDistributionSet", targetEntity = JpaTarget.class, fetch = FetchType.LAZY)
|
||||||
|
private List<JpaTarget> assignedToTargets;
|
||||||
|
|
||||||
|
@ToString.Exclude
|
||||||
|
@Getter(AccessLevel.NONE)
|
||||||
|
@OneToMany(mappedBy = "installedDistributionSet", targetEntity = JpaTarget.class, fetch = FetchType.LAZY)
|
||||||
|
private List<JpaTarget> installedAtTargets;
|
||||||
|
|
||||||
|
@ToString.Exclude
|
||||||
|
@Getter(AccessLevel.NONE)
|
||||||
|
@OneToMany(mappedBy = "distributionSet", targetEntity = JpaAction.class, fetch = FetchType.LAZY)
|
||||||
|
private List<JpaAction> actions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parameterized constructor.
|
* Parameterized constructor.
|
||||||
*
|
|
||||||
* @param name
|
|
||||||
* of the {@link DistributionSet}
|
|
||||||
* @param version
|
|
||||||
* of the {@link DistributionSet}
|
|
||||||
* @param description
|
|
||||||
* of the {@link DistributionSet}
|
|
||||||
* @param type
|
|
||||||
* of the {@link DistributionSet}
|
|
||||||
* @param moduleList
|
|
||||||
* {@link SoftwareModule}s of the {@link DistributionSet}
|
|
||||||
* @param requiredMigrationStep
|
|
||||||
* of the {@link DistributionSet}
|
|
||||||
*/
|
*/
|
||||||
public JpaDistributionSet(final String name, final String version, final String description,
|
public JpaDistributionSet(final String name, final String version, final String description,
|
||||||
final DistributionSetType type, final Collection<SoftwareModule> moduleList,
|
final DistributionSetType type, final Collection<SoftwareModule> moduleList,
|
||||||
final boolean requiredMigrationStep) {
|
final boolean requiredMigrationStep) {
|
||||||
super(name, version, description);
|
super(name, version, description);
|
||||||
|
|
||||||
this.requiredMigrationStep = requiredMigrationStep;
|
|
||||||
this.valid = true;
|
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
// modules shall be set before type.checkComplete call
|
||||||
if (moduleList != null) {
|
if (moduleList != null) {
|
||||||
moduleList.forEach(this::addModule);
|
moduleList.forEach(this::addModule);
|
||||||
}
|
}
|
||||||
if (this.type != null) {
|
if (this.type != null) {
|
||||||
complete = this.type.checkComplete(this);
|
complete = this.type.checkComplete(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.valid = true;
|
||||||
|
this.requiredMigrationStep = requiredMigrationStep;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parameterized constructor.
|
* Parameterized constructor.
|
||||||
*
|
|
||||||
* @param name
|
|
||||||
* of the {@link DistributionSet}
|
|
||||||
* @param version
|
|
||||||
* of the {@link DistributionSet}
|
|
||||||
* @param description
|
|
||||||
* of the {@link DistributionSet}
|
|
||||||
* @param type
|
|
||||||
* of the {@link DistributionSet}
|
|
||||||
* @param moduleList
|
|
||||||
* {@link SoftwareModule}s of the {@link DistributionSet}
|
|
||||||
*/
|
*/
|
||||||
public JpaDistributionSet(final String name, final String version, final String description,
|
public JpaDistributionSet(final String name, final String version, final String description,
|
||||||
final DistributionSetType type, final Collection<SoftwareModule> moduleList) {
|
final DistributionSetType type, final Collection<SoftwareModule> moduleList) {
|
||||||
this(name, version, description, type, moduleList, false);
|
this(name, version, description, type, moduleList, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setType(final DistributionSetType type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<SoftwareModule> getModules() {
|
||||||
|
if (modules == null) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.unmodifiableSet(modules);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean addModule(final SoftwareModule softwareModule) {
|
||||||
|
if (modules == null) {
|
||||||
|
modules = new HashSet<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
checkTypeCompatability(softwareModule);
|
||||||
|
|
||||||
|
final Optional<SoftwareModule> found = modules.stream()
|
||||||
|
.filter(module -> module.getId().equals(softwareModule.getId())).findAny();
|
||||||
|
|
||||||
|
if (found.isPresent()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final long already = modules.stream()
|
||||||
|
.filter(module -> module.getType().getKey().equals(softwareModule.getType().getKey())).count();
|
||||||
|
|
||||||
|
if (already >= softwareModule.getType().getMaxAssignments()) {
|
||||||
|
modules.stream().filter(module -> module.getType().getKey().equals(softwareModule.getType().getKey()))
|
||||||
|
.findAny().ifPresent(modules::remove);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modules.add(softwareModule)) {
|
||||||
|
complete = type.checkComplete(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeModule(final SoftwareModule softwareModule) {
|
||||||
|
if (modules != null && modules.removeIf(m -> m.getId().equals(softwareModule.getId()))) {
|
||||||
|
complete = type.checkComplete(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Set<DistributionSetTag> getTags() {
|
public Set<DistributionSetTag> getTags() {
|
||||||
if (tags == null) {
|
if (tags == null) {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
@@ -202,11 +243,6 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
|
|||||||
return tags.remove(tag);
|
return tags.remove(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDeleted() {
|
|
||||||
return deleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<DistributionSetMetadata> getMetadata() {
|
public List<DistributionSetMetadata> getMetadata() {
|
||||||
if (metadata == null) {
|
if (metadata == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
@@ -215,6 +251,25 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
|
|||||||
return Collections.unmodifiableList(metadata);
|
return Collections.unmodifiableList(metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void lock() {
|
||||||
|
if (!complete) {
|
||||||
|
throw new IncompleteDistributionSetException("Could not be locked while incomplete!");
|
||||||
|
}
|
||||||
|
locked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeleted(final boolean deleted) {
|
||||||
|
this.deleted = deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void invalidate() {
|
||||||
|
this.valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRequiredMigrationStep(final boolean isRequiredMigrationStep) {
|
||||||
|
requiredMigrationStep = isRequiredMigrationStep;
|
||||||
|
}
|
||||||
|
|
||||||
public List<Action> getActions() {
|
public List<Action> getActions() {
|
||||||
if (actions == null) {
|
if (actions == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
@@ -223,112 +278,6 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
|
|||||||
return Collections.unmodifiableList(actions);
|
return Collections.unmodifiableList(actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isRequiredMigrationStep() {
|
|
||||||
return requiredMigrationStep;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DistributionSet setDeleted(final boolean deleted) {
|
|
||||||
this.deleted = deleted;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DistributionSet setRequiredMigrationStep(final boolean isRequiredMigrationStep) {
|
|
||||||
requiredMigrationStep = isRequiredMigrationStep;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<TargetFilterQuery> getAutoAssignFilters() {
|
|
||||||
return autoAssignFilters;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "DistributionSet [getName()=" + getName() + ", getOptLockRevision()=" + getOptLockRevision()
|
|
||||||
+ ", getId()=" + getId() + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<SoftwareModule> getModules() {
|
|
||||||
if (modules == null) {
|
|
||||||
return Collections.emptySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Collections.unmodifiableSet(modules);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean addModule(final SoftwareModule softwareModule) {
|
|
||||||
if (modules == null) {
|
|
||||||
modules = new HashSet<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
checkTypeCompatability(softwareModule);
|
|
||||||
|
|
||||||
final Optional<SoftwareModule> found = modules.stream()
|
|
||||||
.filter(module -> module.getId().equals(softwareModule.getId())).findAny();
|
|
||||||
|
|
||||||
if (found.isPresent()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final long allready = modules.stream()
|
|
||||||
.filter(module -> module.getType().getKey().equals(softwareModule.getType().getKey())).count();
|
|
||||||
|
|
||||||
if (allready >= softwareModule.getType().getMaxAssignments()) {
|
|
||||||
modules.stream().filter(module -> module.getType().getKey().equals(softwareModule.getType().getKey()))
|
|
||||||
.findAny().ifPresent(modules::remove);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modules.add(softwareModule)) {
|
|
||||||
complete = type.checkComplete(this);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkTypeCompatability(final SoftwareModule softwareModule) {
|
|
||||||
// we cannot allow that modules are added without a type defined
|
|
||||||
if (type == null) {
|
|
||||||
throw new DistributionSetTypeUndefinedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if it is allowed to such a module to this DS type
|
|
||||||
if (!type.containsModuleType(softwareModule.getType())) {
|
|
||||||
throw new UnsupportedSoftwareModuleForThisDistributionSetException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeModule(final SoftwareModule softwareModule) {
|
|
||||||
if (modules != null && modules.removeIf(m -> m.getId().equals(softwareModule.getId()))) {
|
|
||||||
complete = type.checkComplete(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DistributionSetType getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setType(final DistributionSetType type) {
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isComplete() {
|
|
||||||
return complete;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isValid() {
|
|
||||||
return valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void invalidate() {
|
|
||||||
this.valid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
|
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
|
||||||
publishEventWithEventPublisher(
|
publishEventWithEventPublisher(
|
||||||
@@ -337,7 +286,6 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
|
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
|
||||||
|
|
||||||
publishEventWithEventPublisher(
|
publishEventWithEventPublisher(
|
||||||
new DistributionSetUpdatedEvent(this, EventPublisherHolder.getInstance().getApplicationId(), complete));
|
new DistributionSetUpdatedEvent(this, EventPublisherHolder.getInstance().getApplicationId(), complete));
|
||||||
|
|
||||||
@@ -353,6 +301,18 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
|
|||||||
EventPublisherHolder.getInstance().getApplicationId()));
|
EventPublisherHolder.getInstance().getApplicationId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkTypeCompatability(final SoftwareModule softwareModule) {
|
||||||
|
// we cannot allow that modules are added without a type defined
|
||||||
|
if (type == null) {
|
||||||
|
throw new DistributionSetTypeUndefinedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if it is allowed to such a module to this DS type
|
||||||
|
if (!type.containsModuleType(softwareModule.getType())) {
|
||||||
|
throw new UnsupportedSoftwareModuleForThisDistributionSetException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void publishEventWithEventPublisher(final ApplicationEvent event) {
|
private static void publishEventWithEventPublisher(final ApplicationEvent event) {
|
||||||
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(event);
|
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(event);
|
||||||
}
|
}
|
||||||
@@ -366,5 +326,4 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
|
|||||||
return changes.stream().anyMatch(changeRecord -> DELETED_PROPERTY.equals(changeRecord.getAttribute())
|
return changes.stream().anyMatch(changeRecord -> DELETED_PROPERTY.equals(changeRecord.getAttribute())
|
||||||
&& Boolean.parseBoolean(changeRecord.getNewValue().toString()));
|
&& Boolean.parseBoolean(changeRecord.getNewValue().toString()));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
@@ -9,10 +9,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.repository.jpa.model;
|
package org.eclipse.hawkbit.repository.jpa.model;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import jakarta.persistence.CascadeType;
|
import jakarta.persistence.CascadeType;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
@@ -32,6 +32,10 @@ import jakarta.persistence.UniqueConstraint;
|
|||||||
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.ToString;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.SoftwareModuleDeletedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.SoftwareModuleDeletedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent;
|
||||||
@@ -49,8 +53,10 @@ import org.eclipse.persistence.sessions.changesets.ObjectChangeSet;
|
|||||||
/**
|
/**
|
||||||
* Base Software Module that is supported by OS level provisioning mechanism on
|
* Base Software Module that is supported by OS level provisioning mechanism on
|
||||||
* the edge controller, e.g. OS, JVM, AgentHub.
|
* the edge controller, e.g. OS, JVM, AgentHub.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
@NoArgsConstructor // Default constructor for JPA
|
||||||
|
@Getter
|
||||||
|
@ToString(callSuper = true)
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "sp_base_software_module", uniqueConstraints = @UniqueConstraint(columnNames = { "module_type", "name",
|
@Table(name = "sp_base_software_module", uniqueConstraints = @UniqueConstraint(columnNames = { "module_type", "name",
|
||||||
"version", "tenant" }, name = "uk_base_sw_mod"), indexes = {
|
"version", "tenant" }, name = "uk_base_sw_mod"), indexes = {
|
||||||
@@ -62,6 +68,8 @@ import org.eclipse.persistence.sessions.changesets.ObjectChangeSet;
|
|||||||
// sub entities
|
// sub entities
|
||||||
@SuppressWarnings("squid:S2160")
|
@SuppressWarnings("squid:S2160")
|
||||||
public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implements SoftwareModule, EventAwareEntity {
|
public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implements SoftwareModule, EventAwareEntity {
|
||||||
|
|
||||||
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private static final String DELETED_PROPERTY = "deleted";
|
private static final String DELETED_PROPERTY = "deleted";
|
||||||
@@ -72,64 +80,43 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
|
|||||||
private JpaSoftwareModuleType type;
|
private JpaSoftwareModuleType type;
|
||||||
|
|
||||||
@CascadeOnDelete
|
@CascadeOnDelete
|
||||||
@ManyToMany(mappedBy = "modules", targetEntity = JpaDistributionSet.class, fetch = FetchType.LAZY)
|
@OneToMany(fetch = FetchType.LAZY, mappedBy = "softwareModule", cascade = {
|
||||||
private List<DistributionSet> assignedTo;
|
CascadeType.PERSIST }, targetEntity = JpaArtifact.class, orphanRemoval = true)
|
||||||
|
private List<JpaArtifact> artifacts;
|
||||||
@Column(name = "deleted")
|
|
||||||
private boolean deleted;
|
|
||||||
|
|
||||||
@Column(name = "vendor", nullable = true, length = SoftwareModule.VENDOR_MAX_SIZE)
|
@Column(name = "vendor", nullable = true, length = SoftwareModule.VENDOR_MAX_SIZE)
|
||||||
@Size(max = SoftwareModule.VENDOR_MAX_SIZE)
|
@Size(max = SoftwareModule.VENDOR_MAX_SIZE)
|
||||||
private String vendor;
|
private String vendor;
|
||||||
|
|
||||||
@CascadeOnDelete
|
@ToString.Exclude
|
||||||
@OneToMany(fetch = FetchType.LAZY, mappedBy = "softwareModule", cascade = {
|
|
||||||
CascadeType.PERSIST }, targetEntity = JpaArtifact.class, orphanRemoval = true)
|
|
||||||
private List<JpaArtifact> artifacts;
|
|
||||||
|
|
||||||
@CascadeOnDelete
|
@CascadeOnDelete
|
||||||
@OneToMany(mappedBy = "softwareModule", fetch = FetchType.LAZY, targetEntity = JpaSoftwareModuleMetadata.class)
|
@OneToMany(mappedBy = "softwareModule", fetch = FetchType.LAZY, targetEntity = JpaSoftwareModuleMetadata.class)
|
||||||
private List<JpaSoftwareModuleMetadata> metadata;
|
private List<JpaSoftwareModuleMetadata> metadata;
|
||||||
|
|
||||||
|
@Column(name = "locked")
|
||||||
|
private boolean locked;
|
||||||
|
|
||||||
|
@Column(name = "deleted")
|
||||||
|
private boolean deleted;
|
||||||
|
|
||||||
@Column(name = "encrypted")
|
@Column(name = "encrypted")
|
||||||
private boolean encrypted;
|
private boolean encrypted;
|
||||||
|
|
||||||
/**
|
@ToString.Exclude
|
||||||
* Default constructor.
|
@Getter(AccessLevel.NONE)
|
||||||
*/
|
@CascadeOnDelete
|
||||||
public JpaSoftwareModule() {
|
@ManyToMany(mappedBy = "modules", targetEntity = JpaDistributionSet.class, fetch = FetchType.LAZY)
|
||||||
// Default constructor for JPA
|
private List<DistributionSet> assignedTo;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* parameterized constructor.
|
* Parameterized constructor.
|
||||||
*
|
|
||||||
* @param type
|
|
||||||
* of the {@link SoftwareModule}
|
|
||||||
* @param name
|
|
||||||
* abstract name of the {@link SoftwareModule}
|
|
||||||
* @param version
|
|
||||||
* of the {@link SoftwareModule}
|
|
||||||
*/
|
*/
|
||||||
public JpaSoftwareModule(final SoftwareModuleType type, final String name, final String version) {
|
public JpaSoftwareModule(final SoftwareModuleType type, final String name, final String version) {
|
||||||
this(type, name, version, null, null, false);
|
this(type, name, version, null, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* parameterized constructor.
|
* Parameterized constructor.
|
||||||
*
|
|
||||||
* @param type
|
|
||||||
* of the {@link SoftwareModule}
|
|
||||||
* @param name
|
|
||||||
* abstract name of the {@link SoftwareModule}
|
|
||||||
* @param version
|
|
||||||
* of the {@link SoftwareModule}
|
|
||||||
* @param description
|
|
||||||
* of the {@link SoftwareModule}
|
|
||||||
* @param vendor
|
|
||||||
* of the {@link SoftwareModule}
|
|
||||||
* @param encrypted
|
|
||||||
* encryption flag of the {@link SoftwareModule}
|
|
||||||
*/
|
*/
|
||||||
public JpaSoftwareModule(final SoftwareModuleType type, final String name, final String version,
|
public JpaSoftwareModule(final SoftwareModuleType type, final String name, final String version,
|
||||||
final String description, final String vendor, final boolean encrypted) {
|
final String description, final String vendor, final boolean encrypted) {
|
||||||
@@ -139,8 +126,21 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
|
|||||||
this.encrypted = encrypted;
|
this.encrypted = encrypted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setType(final JpaSoftwareModuleType type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Artifact> getArtifacts() {
|
||||||
|
if (artifacts == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.unmodifiableList(artifacts);
|
||||||
|
}
|
||||||
|
|
||||||
public void addArtifact(final Artifact artifact) {
|
public void addArtifact(final Artifact artifact) {
|
||||||
if (null == artifacts) {
|
if (artifacts == null) {
|
||||||
artifacts = new ArrayList<>(4);
|
artifacts = new ArrayList<>(4);
|
||||||
artifacts.add((JpaArtifact) artifact);
|
artifacts.add((JpaArtifact) artifact);
|
||||||
return;
|
return;
|
||||||
@@ -152,25 +152,7 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the artifacts
|
* @param artifact is removed from the assigned {@link Artifact}s.
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public List<Artifact> getArtifacts() {
|
|
||||||
if (artifacts == null) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Collections.unmodifiableList(artifacts);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getVendor() {
|
|
||||||
return vendor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param artifact
|
|
||||||
* is removed from the assigned {@link Artifact}s.
|
|
||||||
*/
|
*/
|
||||||
public void removeArtifact(final Artifact artifact) {
|
public void removeArtifact(final Artifact artifact) {
|
||||||
if (artifacts != null) {
|
if (artifacts != null) {
|
||||||
@@ -182,14 +164,8 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
|
|||||||
this.vendor = vendor;
|
this.vendor = vendor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void lock() {
|
||||||
public SoftwareModuleType getType() {
|
locked = true;
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDeleted() {
|
|
||||||
return deleted;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -203,15 +179,6 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
|
|||||||
this.deleted = deleted;
|
this.deleted = deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setType(final JpaSoftwareModuleType type) {
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEncrypted() {
|
|
||||||
return encrypted;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks this software module as encrypted.
|
* Marks this software module as encrypted.
|
||||||
*
|
*
|
||||||
@@ -223,13 +190,6 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
|
|||||||
this.encrypted = encrypted;
|
this.encrypted = encrypted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "SoftwareModule [deleted=" + isDeleted() + ", encrypted=" + isEncrypted() + ", name=" + getName()
|
|
||||||
+ ", version=" + getVersion() + ", revision=" + getOptLockRevision() + ", Id=" + getId() + ", type="
|
|
||||||
+ getType().getName() + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<DistributionSet> getAssignedTo() {
|
public List<DistributionSet> getAssignedTo() {
|
||||||
if (assignedTo == null) {
|
if (assignedTo == null) {
|
||||||
@@ -256,20 +216,19 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isSoftDeleted(final DescriptorEvent event) {
|
|
||||||
final ObjectChangeSet changeSet = ((UpdateObjectQuery) event.getQuery()).getObjectChangeSet();
|
|
||||||
final List<DirectToFieldChangeRecord> changes = changeSet.getChanges().stream()
|
|
||||||
.filter(DirectToFieldChangeRecord.class::isInstance).map(DirectToFieldChangeRecord.class::cast)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
return changes.stream().anyMatch(changeRecord -> DELETED_PROPERTY.equals(changeRecord.getAttribute())
|
|
||||||
&& Boolean.parseBoolean(changeRecord.getNewValue().toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fireDeleteEvent(final DescriptorEvent descriptorEvent) {
|
public void fireDeleteEvent(final DescriptorEvent descriptorEvent) {
|
||||||
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new SoftwareModuleDeletedEvent(getTenant(),
|
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new SoftwareModuleDeletedEvent(getTenant(),
|
||||||
getId(), getClass(), EventPublisherHolder.getInstance().getApplicationId()));
|
getId(), getClass(), EventPublisherHolder.getInstance().getApplicationId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
private static boolean isSoftDeleted(final DescriptorEvent event) {
|
||||||
|
final ObjectChangeSet changeSet = ((UpdateObjectQuery) event.getQuery()).getObjectChangeSet();
|
||||||
|
final List<DirectToFieldChangeRecord> changes = changeSet.getChanges().stream()
|
||||||
|
.filter(DirectToFieldChangeRecord.class::isInstance).map(DirectToFieldChangeRecord.class::cast)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return changes.stream().anyMatch(changeRecord -> DELETED_PROPERTY.equals(changeRecord.getAttribute())
|
||||||
|
&& Boolean.parseBoolean(changeRecord.getNewValue().toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE sp_base_software_module ADD COLUMN locked BOOLEAN NOT NULL DEFAULT true;
|
||||||
|
ALTER TABLE sp_distribution_set ADD COLUMN locked BOOLEAN NOT NULL DEFAULT true;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE sp_base_software_module ADD COLUMN locked BOOLEAN NOT NULL DEFAULT true;
|
||||||
|
ALTER TABLE sp_distribution_set ADD COLUMN locked BOOLEAN NOT NULL DEFAULT true;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE sp_base_software_module ADD COLUMN locked BOOLEAN NOT NULL DEFAULT true;
|
||||||
|
ALTER TABLE sp_distribution_set ADD COLUMN locked BOOLEAN NOT NULL DEFAULT true;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE sp_base_software_module ADD COLUMN locked BOOLEAN NOT NULL DEFAULT true;
|
||||||
|
ALTER TABLE sp_distribution_set ADD COLUMN locked BOOLEAN NOT NULL DEFAULT true;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE sp_base_software_module ADD locked BIT NOT NULL DEFAULT 1;
|
||||||
|
ALTER TABLE sp_distribution_set ADD locked BIT NOT NULL DEFAULT 1;
|
||||||
Reference in New Issue
Block a user