TargetType management over common RepositoryManagement (#2581)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-08-01 19:24:49 +03:00
committed by GitHub
parent c79e35b9de
commit a689733d4c
40 changed files with 435 additions and 762 deletions

View File

@@ -15,7 +15,6 @@ import org.eclipse.hawkbit.repository.builder.RolloutBuilder;
import org.eclipse.hawkbit.repository.builder.RolloutGroupBuilder;
import org.eclipse.hawkbit.repository.builder.TargetBuilder;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder;
import org.eclipse.hawkbit.repository.builder.TargetTypeBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaActionStatusBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupBuilder;
import org.springframework.validation.annotation.Validated;
@@ -27,17 +26,14 @@ import org.springframework.validation.annotation.Validated;
public class JpaEntityFactory implements EntityFactory {
private final TargetBuilder targetBuilder;
private final TargetTypeBuilder targetTypeBuilder;
private final TargetFilterQueryBuilder targetFilterQueryBuilder;
private final RolloutBuilder rolloutBuilder;
@SuppressWarnings("java:S107")
public JpaEntityFactory(
final TargetBuilder targetBuilder, final TargetTypeBuilder targetTypeBuilder,
final TargetFilterQueryBuilder targetFilterQueryBuilder,
final TargetBuilder targetBuilder, final TargetFilterQueryBuilder targetFilterQueryBuilder,
final RolloutBuilder rolloutBuilder) {
this.targetBuilder = targetBuilder;
this.targetTypeBuilder = targetTypeBuilder;
this.targetFilterQueryBuilder = targetFilterQueryBuilder;
this.rolloutBuilder = rolloutBuilder;
}
@@ -61,11 +57,6 @@ public class JpaEntityFactory implements EntityFactory {
return targetBuilder;
}
@Override
public TargetTypeBuilder targetType() {
return targetTypeBuilder;
}
@Override
public TargetFilterQueryBuilder targetFilterQuery() {
return targetFilterQueryBuilder;

View File

@@ -46,7 +46,6 @@ import org.eclipse.hawkbit.repository.autoassign.AutoAssignExecutor;
import org.eclipse.hawkbit.repository.builder.RolloutBuilder;
import org.eclipse.hawkbit.repository.builder.TargetBuilder;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder;
import org.eclipse.hawkbit.repository.builder.TargetTypeBuilder;
import org.eclipse.hawkbit.repository.event.ApplicationEventFilter;
import org.eclipse.hawkbit.repository.event.remote.EventEntityManager;
import org.eclipse.hawkbit.repository.event.remote.EventEntityManagerHolder;
@@ -61,7 +60,6 @@ import org.eclipse.hawkbit.repository.jpa.autocleanup.CleanupTask;
import org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetFilterQueryBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetTypeBuilder;
import org.eclipse.hawkbit.repository.jpa.cluster.DistributedLockRepository;
import org.eclipse.hawkbit.repository.jpa.cluster.LockProperties;
import org.eclipse.hawkbit.repository.jpa.event.JpaEventEntityManager;
@@ -311,15 +309,6 @@ public class JpaRepositoryConfiguration {
return new JpaTargetBuilder(targetTypeManagement);
}
/**
* @param dsTypeManagement for loading {@link TargetType#getCompatibleDistributionSetTypes()}
* @return TargetTypeBuilder bean
*/
@Bean
TargetTypeBuilder targetTypeBuilder(final DistributionSetTypeManagement<? extends DistributionSetType> dsTypeManagement) {
return new JpaTargetTypeBuilder(dsTypeManagement);
}
/**
* @param distributionSetManagement for loading {@link Rollout#getDistributionSet()}
* @return RolloutBuilder bean
@@ -450,10 +439,9 @@ public class JpaRepositoryConfiguration {
@Bean
@ConditionalOnMissingBean
EntityFactory entityFactory(
final TargetBuilder targetBuilder, final TargetTypeBuilder targetTypeBuilder,
final TargetFilterQueryBuilder targetFilterQueryBuilder,
final TargetBuilder targetBuilder, final TargetFilterQueryBuilder targetFilterQueryBuilder,
final RolloutBuilder rolloutBuilder) {
return new JpaEntityFactory(targetBuilder, targetTypeBuilder, targetFilterQueryBuilder, rolloutBuilder);
return new JpaEntityFactory(targetBuilder, targetFilterQueryBuilder, rolloutBuilder);
}
/**

View File

@@ -23,9 +23,9 @@ import org.springframework.util.ObjectUtils;
*/
public class JpaTargetCreate extends AbstractTargetUpdateCreate<TargetCreate> implements TargetCreate {
private final TargetTypeManagement targetTypeManagement;
private final TargetTypeManagement<? extends TargetType> targetTypeManagement;
JpaTargetCreate(final TargetTypeManagement targetTypeManagement) {
JpaTargetCreate(final TargetTypeManagement<? extends TargetType> targetTypeManagement) {
super(null);
this.targetTypeManagement = targetTypeManagement;
}

View File

@@ -1,40 +0,0 @@
/**
* Copyright (c) 2021 Bosch.IO 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.builder;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.builder.GenericTargetTypeUpdate;
import org.eclipse.hawkbit.repository.builder.TargetTypeBuilder;
import org.eclipse.hawkbit.repository.builder.TargetTypeCreate;
import org.eclipse.hawkbit.repository.builder.TargetTypeUpdate;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.TargetType;
/**
* Builder implementation for {@link TargetType}.
*/
public class JpaTargetTypeBuilder implements TargetTypeBuilder {
private final DistributionSetTypeManagement<? extends DistributionSetType> distributionSetTypeManagement;
public JpaTargetTypeBuilder(DistributionSetTypeManagement<? extends DistributionSetType> distributionSetTypeManagement) {
this.distributionSetTypeManagement = distributionSetTypeManagement;
}
@Override
public TargetTypeUpdate update(long id) {
return new GenericTargetTypeUpdate(id);
}
@Override
public TargetTypeCreate create() {
return new JpaTargetTypeCreate(distributionSetTypeManagement);
}
}

View File

@@ -1,60 +0,0 @@
/**
* Copyright (c) 2021 Bosch.IO 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.builder;
import java.util.Collection;
import java.util.Collections;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.builder.AbstractTargetTypeUpdateCreate;
import org.eclipse.hawkbit.repository.builder.TargetTypeCreate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.TargetTypeKeyOrNameRequiredException;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.springframework.util.CollectionUtils;
/**
* Create/build implementation.
*/
public class JpaTargetTypeCreate extends AbstractTargetTypeUpdateCreate<TargetTypeCreate> implements TargetTypeCreate {
private final DistributionSetTypeManagement<? extends DistributionSetType> distributionSetTypeManagement;
JpaTargetTypeCreate(final DistributionSetTypeManagement<? extends DistributionSetType> distributionSetTypeManagement) {
this.distributionSetTypeManagement = distributionSetTypeManagement;
}
@Override
public JpaTargetType build() {
if (key == null && name == null) {
throw new TargetTypeKeyOrNameRequiredException("Key or name of the target type shall be specified!");
}
final JpaTargetType result = new JpaTargetType(key == null ? name : key, name == null ? key : name, description, colour);
findDistributionSetTypeWithExceptionIfNotFound(compatible).forEach(result::addCompatibleDistributionSetType);
return result;
}
private Collection<? extends DistributionSetType> findDistributionSetTypeWithExceptionIfNotFound(
final Collection<Long> distributionSetTypeId) {
if (CollectionUtils.isEmpty(distributionSetTypeId)) {
return Collections.emptyList();
}
final Collection<? extends DistributionSetType> type = distributionSetTypeManagement.get(distributionSetTypeId);
if (type.size() < distributionSetTypeId.size()) {
throw new EntityNotFoundException(SoftwareModuleType.class, distributionSetTypeId);
}
return type;
}
}

View File

@@ -719,7 +719,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
final Set<DistributionSetType> incompatibleDistSetTypes = distributionSetManagement.get(distSetIds).stream()
.map(DistributionSet::getType)
.collect(Collectors.toSet());
incompatibleDistSetTypes.removeAll(target.getTargetType().getCompatibleDistributionSetTypes());
incompatibleDistSetTypes.removeAll(target.getTargetType().getDistributionSetTypes());
if (!incompatibleDistSetTypes.isEmpty()) {
final Set<String> distSetTypeNames = incompatibleDistSetTypes.stream()

View File

@@ -10,37 +10,31 @@
package org.eclipse.hawkbit.repository.jpa.management;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.ToLongFunction;
import jakarta.persistence.EntityManager;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.TargetTypeFields;
import org.eclipse.hawkbit.repository.TargetTypeManagement;
import org.eclipse.hawkbit.repository.builder.GenericTargetTypeUpdate;
import org.eclipse.hawkbit.repository.builder.TargetTypeCreate;
import org.eclipse.hawkbit.repository.builder.TargetTypeUpdate;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.TargetTypeInUseException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetTypeCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetTypeSpecification;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.retry.annotation.Backoff;
@@ -56,119 +50,53 @@ import org.springframework.validation.annotation.Validated;
@Validated
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "target-type-management" }, matchIfMissing = true)
public class JpaTargetTypeManagement implements TargetTypeManagement {
public class JpaTargetTypeManagement
extends AbstractJpaRepositoryManagement<JpaTargetType, TargetTypeManagement.Create, TargetTypeManagement.Update, TargetTypeRepository, TargetTypeFields>
implements TargetTypeManagement<JpaTargetType>{
private final TargetTypeRepository targetTypeRepository;
private final TargetRepository targetRepository;
private final DistributionSetTypeRepository distributionSetTypeRepository;
private final QuotaManagement quotaManagement;
protected JpaTargetTypeManagement(final TargetTypeRepository targetTypeRepository,
final TargetRepository targetRepository, final DistributionSetTypeRepository distributionSetTypeRepository,
final QuotaManagement quotaManagement) {
this.targetTypeRepository = targetTypeRepository;
protected JpaTargetTypeManagement(
final TargetTypeRepository targetTypeRepository, final EntityManager entityManager,
final TargetRepository targetRepository,
final DistributionSetTypeRepository distributionSetTypeRepository, final QuotaManagement quotaManagement) {
super(targetTypeRepository, entityManager);
this.targetRepository = targetRepository;
this.distributionSetTypeRepository = distributionSetTypeRepository;
this.quotaManagement = quotaManagement;
}
@Override
protected void delete0(final Collection<Long> ids) {
for (final Long id : ids) {
if (targetRepository.countByTargetTypeId(id) > 0) {
throw new TargetTypeInUseException("Cannot delete target type that is in use: " + id);
}
}
super.delete0(ids);
}
@Override
public Optional<TargetType> getByKey(final String key) {
return targetTypeRepository.findOne(TargetTypeSpecification.hasKey(key)).map(TargetType.class::cast);
return jpaRepository.findOne(TargetTypeSpecification.hasKey(key)).map(TargetType.class::cast);
}
@Override
public Optional<TargetType> getByName(final String name) {
return targetTypeRepository.findOne(TargetTypeSpecification.hasName(name)).map(TargetType.class::cast);
}
@Override
public long count() {
return targetTypeRepository.count();
}
@Override
public long countByName(final String name) {
return targetTypeRepository.count(TargetTypeSpecification.hasName(name));
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetType create(final TargetTypeCreate create) {
final JpaTargetType typeCreate = ((JpaTargetTypeCreate) create).build();
return targetTypeRepository.save(AccessController.Operation.CREATE, typeCreate);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<TargetType> create(final Collection<TargetTypeCreate> creates) {
final List<JpaTargetType> typeCreate =
creates.stream().map(create -> ((JpaTargetTypeCreate) create).build()).toList();
return Collections.unmodifiableList(targetTypeRepository.saveAll(AccessController.Operation.CREATE, typeCreate));
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final Long id) {
getByIdAndThrowIfNotFound(id);
if (targetRepository.countByTargetTypeId(id) > 0) {
throw new TargetTypeInUseException("Cannot delete target type that is in use");
}
targetTypeRepository.deleteById(id);
}
@Override
public Slice<TargetType> findAll(final Pageable pageable) {
return targetTypeRepository.findAllWithoutCount(pageable).map(TargetType.class::cast);
}
@Override
public Page<TargetType> findByRsql(final String rsql, final Pageable pageable) {
return JpaManagementHelper.findAllWithCountBySpec(
targetTypeRepository,
List.of(RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetTypeFields.class)),
pageable);
return jpaRepository.findOne(TargetTypeSpecification.hasName(name)).map(TargetType.class::cast);
}
@Override
public Slice<TargetType> findByName(final String name, final Pageable pageable) {
return JpaManagementHelper.findAllWithoutCountBySpec(targetTypeRepository, List.of(TargetTypeSpecification.likeName(name)), pageable
);
return JpaManagementHelper.findAllWithoutCountBySpec(jpaRepository, List.of(TargetTypeSpecification.likeName(name)), pageable);
}
@Override
public Optional<TargetType> get(final long id) {
return targetTypeRepository.findById(id).map(TargetType.class::cast);
}
@Override
public List<TargetType> get(final Collection<Long> ids) {
return Collections.unmodifiableList(targetTypeRepository.findAllById(ids));
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetType update(final TargetTypeUpdate update) {
final GenericTargetTypeUpdate typeUpdate = (GenericTargetTypeUpdate) update;
final JpaTargetType type = getByIdAndThrowIfNotFound(typeUpdate.getId());
typeUpdate.getName().ifPresent((type::setName));
typeUpdate.getDescription().ifPresent(type::setDescription);
typeUpdate.getColour().ifPresent(type::setColour);
return targetTypeRepository.save(type);
public long countByName(final String name) {
return jpaRepository.count(TargetTypeSpecification.hasName(name));
}
@Override
@@ -186,10 +114,10 @@ public class JpaTargetTypeManagement implements TargetTypeManagement {
}
final JpaTargetType type = getByIdAndThrowIfNotFound(id);
assertDistributionSetTypeQuota(id, distributionSetTypeIds.size(), typeId -> type.getCompatibleDistributionSetTypes().size());
assertDistributionSetTypeQuota(id, distributionSetTypeIds.size(), typeId -> type.getDistributionSetTypes().size());
dsTypes.forEach(type::addCompatibleDistributionSetType);
return targetTypeRepository.save(type);
return jpaRepository.save(type);
}
@Override
@@ -202,7 +130,7 @@ public class JpaTargetTypeManagement implements TargetTypeManagement {
type.removeDistributionSetType(distributionSetTypeId);
return targetTypeRepository.save(type);
return jpaRepository.save(type);
}
@SuppressWarnings("java:S2201") // the idea is just to check for distribution set type existence
@@ -213,7 +141,7 @@ public class JpaTargetTypeManagement implements TargetTypeManagement {
}
private JpaTargetType getByIdAndThrowIfNotFound(final Long id) {
return targetTypeRepository
return jpaRepository
.findById(id)
.orElseThrow(() -> new EntityNotFoundException(TargetType.class, id));
}

View File

@@ -78,7 +78,7 @@ public class JpaTargetType extends AbstractJpaTypeEntity implements TargetType,
}
@Override
public Set<DistributionSetType> getCompatibleDistributionSetTypes() {
public Set<DistributionSetType> getDistributionSetTypes() {
return Collections.unmodifiableSet(distributionSetTypes);
}
@@ -88,12 +88,11 @@ public class JpaTargetType extends AbstractJpaTypeEntity implements TargetType,
* @param dsTypeId Distribution set type ID
* @return Target type
*/
public JpaTargetType removeDistributionSetType(final Long dsTypeId) {
public void removeDistributionSetType(final Long dsTypeId) {
distributionSetTypes.stream()
.filter(element -> element.getId().equals(dsTypeId))
.findAny()
.ifPresent(distributionSetTypes::remove);
return this;
}
@Override