[#1580] Software Module & Distribution Set lock: add lock at db level (#1643)

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:
Avgustin Marinov
2024-02-14 20:59:34 +02:00
committed by GitHub
parent 4ebabcb256
commit 9bc0e74f47
9 changed files with 196 additions and 272 deletions

View File

@@ -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)"),
/** /**
* *

View File

@@ -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;

View File

@@ -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()));
} }
}
}

View File

@@ -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()));
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;