Refactor Management interfaces: find/get pattern (#2609)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-08-15 16:18:32 +03:00
committed by GitHub
parent fa4dea75a3
commit b4edde8cc3
100 changed files with 713 additions and 986 deletions

View File

@@ -16,7 +16,6 @@ import java.util.concurrent.locks.Lock;
import io.micrometer.core.instrument.MeterRegistry;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.ContextAware;
import org.eclipse.hawkbit.repository.RolloutExecutor;
import org.eclipse.hawkbit.repository.RolloutHandler;
import org.eclipse.hawkbit.repository.RolloutManagement;
@@ -108,7 +107,7 @@ public class JpaRolloutHandler implements RolloutHandler {
final long startNano = System.nanoTime();
DeploymentHelper.runInNewTransaction(txManager, handlerId + "-" + rolloutId, status -> {
rolloutManagement.get(rolloutId).ifPresentOrElse(
rolloutManagement.find(rolloutId).ifPresentOrElse(
rolloutExecutor::execute,
() -> log.error("Could not retrieve rollout with id {}. Will not continue with execution.", rolloutId));
return 0L;

View File

@@ -15,12 +15,16 @@ import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_M
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -31,11 +35,13 @@ import jakarta.persistence.criteria.CriteriaUpdate;
import jakarta.persistence.criteria.Root;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.Identifiable;
import org.eclipse.hawkbit.repository.RepositoryManagement;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
import org.eclipse.hawkbit.repository.jpa.Jpa;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.JpaRepositoryConfiguration;
@@ -48,7 +54,6 @@ import org.eclipse.hawkbit.utils.ObjectCopyUtil;
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.data.jpa.domain.Specification;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
@@ -73,6 +78,7 @@ import org.springframework.validation.annotation.Validated;
@HandleAuthorizationDenied(handlerClass = JpaRepositoryConfiguration.ManagementExceptionThrowingMethodAuthorizationDeniedHandler.class)
@Transactional(readOnly = true)
@Validated
@Slf4j
abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity, C, U extends Identifiable<Long>, R extends BaseEntityRepository<T>, A extends Enum<A> & RsqlQueryField>
implements RepositoryManagement<T, C, U> {
@@ -81,6 +87,7 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
protected final R jpaRepository;
protected final EntityManager entityManager;
private final Supplier<T> jpaEntityCreator;
private final Function<T, Boolean> isValid;
protected AbstractJpaRepositoryManagement(final R jpaRepository, final EntityManager entityManager) {
this.jpaRepository = jpaRepository;
@@ -100,6 +107,25 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
throw new IllegalStateException("Must NEVER happen!", e);
}
};
Method isValidMethod = null;
try {
isValidMethod = jpaRepository.getDomainClass().getMethod("isValid");
} catch (final NoSuchMethodException e) {
// if there is no isValid method, then it is always valid
}
final Method isValidMethodF = isValidMethod;
isValid = jpaEntity -> {
if (isValidMethodF == null) {
return true; // if there is no isValid method, then it is always valid
} else {
try {
return (Boolean) isValidMethodF.invoke(jpaEntity);
} catch (final IllegalAccessException | InvocationTargetException e) {
log.error(e.getMessage(), e);
return false; // if it fails, then it is not valid
}
}
};
}
@Override
@@ -117,13 +143,23 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
}
@Override
public Optional<T> get(final long id) {
public T get(final long id) {
return jpaRepository.getById(id);
}
@Override
public Optional<T> find(final long id) {
return jpaRepository.findById(id);
}
@Override
public List<T> get(final Collection<Long> ids) {
return findAllById(ids);
return findAllById(ids, true);
}
@Override
public List<T> find(final Collection<Long> ids) {
return findAllById(ids, false);
}
@Override
@@ -157,13 +193,8 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
@SuppressWarnings("java:S1066") // javaS1066 - better readable that way
public T update(final U update) {
final T entity = jpaRepository
.findById(update.getId())
.orElseThrow(() -> new EntityNotFoundException(managementClass(), update.getId()));
return update(update, entity);
}
protected T update(final Identifiable<Long> update, final T entity) {
final T entity = getValid(update.getId());
checkUpdate(update, entity);
// update getId has no setter in target JPA entity but shall have getter and the value shall be the same
// otherwise the Utils will throw an exception that there is no counterpart setter for getId
if (ObjectCopyUtil.copy(update, entity, false, this::attach)) {
@@ -173,6 +204,43 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
}
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
@SuppressWarnings("java:S1066") // javaS1066 - better readable that way
public Map<Long, T> update(final Collection<U> update) {
final Map<Long, T> toUpdate = findAllById(update.stream().map(Identifiable::getId).toList(), true)
.stream()
.filter(entity -> {
if (Boolean.FALSE.equals(isValid.apply(entity))) {
throw new InvalidDistributionSetException(
jpaRepository.getManagementClass().getSimpleName() + " " + entity.getId() + " is invalid");
}
return true;
})
.collect(Collectors.toMap(Identifiable::getId, Function.identity()));
final List<T> toSave = new ArrayList<>(toUpdate.values());
for (final U u : update) {
final T entity = toUpdate.get(u.getId());
checkUpdate(u, entity);
// update getId has no setter in target JPA entity but shall have getter and the value shall be the same
// otherwise the Utils will throw an exception that there is no counterpart setter for getId
if (ObjectCopyUtil.copy(u, entity, false, this::attach)) {
toSave.add(entity);
}
}
if (toSave.isEmpty()) {
return toUpdate;
} else {
final List<T> savedEntities = jpaRepository.saveAll(toSave);
final Map<Long, T> result = new HashMap<>(toUpdate);
for (final T savedEntity : savedEntities) {
result.put(savedEntity.getId(), savedEntity);
}
return result;
}
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
@@ -194,18 +262,28 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
.orElseGet(() -> List.of(RsqlUtility.getInstance().buildRsqlSpecification(rsql, fieldsClass())));
}
protected void checkUpdate(final U update, final T distributionSet) {}
// return which are for soft deletion
@SuppressWarnings("java:S1172") // java:S1172 - it is intended to be used by subclasses
protected Collection<T> softDelete(final Collection<T> toDelete) {
return Collections.emptyList();
}
protected T getValid(final Long id) {
final T jpaEntity = jpaRepository.getById(id);
if (Boolean.FALSE.equals(isValid.apply(jpaEntity))) {
throw new InvalidDistributionSetException(jpaRepository.getManagementClass().getSimpleName() + " " + id + " is invalid");
}
return jpaEntity;
}
protected void delete0(final Collection<Long> ids) {
if (ObjectUtils.isEmpty(ids)) {
return;
}
final List<T> toDelete = findAllById(ids); // throws EntityNotFoundException if any of these does not exist
final List<T> toDelete = findAllById(ids, true); // throws EntityNotFoundException if any of these does not exist
jpaRepository.getAccessController().ifPresent(ac -> {
for (final T entity : toDelete) {
ac.assertOperationAllowed(AccessController.Operation.DELETE, entity);
@@ -240,9 +318,9 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
}
}
private List<T> findAllById(final Collection<Long> ids) {
private List<T> findAllById(final Collection<Long> ids, final boolean throwIfNotFound) {
final List<T> foundDs = jpaRepository.findAllById(ids);
if (foundDs.size() != ids.size()) {
if (throwIfNotFound && foundDs.size() != ids.size()) {
throw new EntityNotFoundException(managementClass(), ids, foundDs.stream().map(T::getId).toList());
}
return foundDs;

View File

@@ -121,14 +121,8 @@ abstract class AbstractJpaRepositoryWithMetadataManagement<T extends AbstractJpa
@Override
public Map<String, MV> getMetadata(final Long id) {
return jpaRepository
.findById(id)
.map(T::getMetadata)
.map(metadata -> metadata.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> (MV) e.getValue())))
.orElseThrow(() -> new EntityNotFoundException(jpaRepository.getManagementClass(), id));
final T entity = jpaRepository.getById(id);
return entity.getMetadata().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
@Override
@@ -146,16 +140,6 @@ abstract class AbstractJpaRepositoryWithMetadataManagement<T extends AbstractJpa
protected abstract void assertMetadataQuota(final long requested);
private T getValid(final Long id) {
final T jpaEntity = jpaRepository
.findById(id)
.orElseThrow(() -> new EntityNotFoundException(jpaRepository.getManagementClass(), id));
if (!jpaEntity.isValid()) {
throw new InvalidDistributionSetException(jpaRepository.getManagementClass().getSimpleName() + " " + id + " is invalid");
}
return jpaEntity;
}
@SuppressWarnings("unchecked")
private boolean setMetadataValue(final String key, final MV newValue, final MVI existingValue, final Map<String, MVI> metadataValueMap) {
if (useCopy) {

View File

@@ -21,7 +21,6 @@ import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
@@ -80,7 +79,7 @@ public class JpaActionManagement {
protected Action addActionStatus(final ActionStatusCreate create) {
final Long actionId = create.getActionId();
final JpaAction action = getActionAndThrowExceptionIfNotFound(actionId);
final JpaAction action = actionRepository.getById(actionId);
if (isUpdatingActionStatusAllowed(action, create)) {
return handleAddUpdateActionStatus(create, action);
@@ -92,10 +91,6 @@ public class JpaActionManagement {
return action;
}
protected JpaAction getActionAndThrowExceptionIfNotFound(final Long actionId) {
return actionRepository.findById(actionId).orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
}
protected void onActionStatusUpdate(final JpaActionStatus newActionStatus, final JpaAction action) {
// can be overwritten to intercept the persistence of the action status
}

View File

@@ -13,24 +13,22 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
import jakarta.annotation.Nullable;
import jakarta.persistence.EntityManager;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.artifact.ArtifactRepository;
import org.eclipse.hawkbit.repository.artifact.encryption.ArtifactEncryptionService;
import org.eclipse.hawkbit.repository.artifact.exception.ArtifactBinaryNotFoundException;
import org.eclipse.hawkbit.repository.artifact.exception.ArtifactDeleteFailedException;
import org.eclipse.hawkbit.repository.artifact.exception.ArtifactStoreException;
import org.eclipse.hawkbit.repository.artifact.exception.ArtifactUploadFailedException;
import org.eclipse.hawkbit.repository.artifact.exception.HashNotMatchException;
import org.eclipse.hawkbit.repository.artifact.model.AbstractDbArtifact;
import org.eclipse.hawkbit.repository.artifact.model.DbArtifact;
import org.eclipse.hawkbit.repository.artifact.model.DbArtifactHash;
import org.eclipse.hawkbit.repository.artifact.encryption.ArtifactEncryptionService;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException;
import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.exception.InvalidMD5HashException;
import org.eclipse.hawkbit.repository.exception.InvalidSHA1HashException;
import org.eclipse.hawkbit.repository.exception.InvalidSHA256HashException;
@@ -53,8 +51,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.tenancy.TenantAware;
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.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
@@ -72,49 +68,49 @@ import org.springframework.validation.annotation.Validated;
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "artifact-management" }, matchIfMissing = true)
public class JpaArtifactManagement implements ArtifactManagement {
private final LocalArtifactRepository localArtifactRepository;
private final ArtifactRepository artifactRepository;
private final SoftwareModuleRepository softwareModuleRepository;
private final EntityManager entityManager;
private final PlatformTransactionManager txManager;
private final LocalArtifactRepository localArtifactRepository;
private final SoftwareModuleRepository softwareModuleRepository;
@Nullable
private final ArtifactRepository artifactRepository;
private final TenantAware tenantAware;
private final QuotaManagement quotaManagement;
protected JpaArtifactManagement(
final LocalArtifactRepository localArtifactRepository,
final Optional<ArtifactRepository> artifactRepository,
final SoftwareModuleRepository softwareModuleRepository,
final EntityManager entityManager,
final PlatformTransactionManager txManager,
final LocalArtifactRepository localArtifactRepository,
final SoftwareModuleRepository softwareModuleRepository, @Nullable final ArtifactRepository artifactRepository,
final QuotaManagement quotaManagement,
final TenantAware tenantAware) {
this.localArtifactRepository = localArtifactRepository;
this.artifactRepository = artifactRepository.orElse(null);
this.softwareModuleRepository = softwareModuleRepository;
this.entityManager = entityManager;
this.txManager = txManager;
this.localArtifactRepository = localArtifactRepository;
this.softwareModuleRepository = softwareModuleRepository;
this.artifactRepository = artifactRepository;
this.quotaManagement = quotaManagement;
this.tenantAware = tenantAware;
}
@Override
public long count() {
return localArtifactRepository.count();
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Artifact create(final ArtifactUpload artifactUpload) {
assertArtifactRepositoryAvailable();
if (artifactRepository == null) {
throw new UnsupportedOperationException();
}
final long moduleId = artifactUpload.getModuleId();
assertArtifactQuota(moduleId, 1);
final JpaSoftwareModule softwareModule =
softwareModuleRepository
.findById(moduleId)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, moduleId));
QuotaHelper.assertAssignmentQuota(
moduleId, 1, quotaManagement.getMaxArtifactsPerSoftwareModule(),
Artifact.class, SoftwareModule.class,
// get all artifacts without user context
softwareModuleId -> localArtifactRepository
.count(null, ArtifactSpecifications.bySoftwareModuleId(softwareModuleId)));
final JpaSoftwareModule softwareModule = softwareModuleRepository.getById(moduleId);
final String filename = artifactUpload.getFilename();
final Artifact existing = softwareModule.getArtifactByFilename(filename).orElse(null);
@@ -139,12 +135,34 @@ public class JpaArtifactManagement implements ArtifactManagement {
}
}
@SuppressWarnings("java:S2201") // java:S2201 - the idea is to just check if the artifact exists
@Override
public DbArtifact loadArtifactBinary(final String sha1Hash, final long softwareModuleId, final boolean isEncrypted) {
if (artifactRepository == null) {
throw new UnsupportedOperationException();
}
final String tenant = tenantAware.getCurrentTenant();
// check access to the software module and if artifact belongs to it
for (final Artifact artifact : softwareModuleRepository.getById(softwareModuleId).getArtifacts()) {
if (artifact.getSha1Hash().equals(sha1Hash)) {
final DbArtifact dbArtifact = artifactRepository.getBySha1(tenant, sha1Hash);
return isEncrypted ? wrapInEncryptionAwareDbArtifact(softwareModuleId, dbArtifact) : dbArtifact;
}
}
throw new ArtifactBinaryNotFoundException(sha1Hash);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final long id) {
final JpaArtifact toDelete = (JpaArtifact) get(id).orElseThrow(() -> new EntityNotFoundException(Artifact.class, id));
if (artifactRepository == null) {
throw new UnsupportedOperationException();
}
final JpaArtifact toDelete = localArtifactRepository.getById(id);
final JpaSoftwareModule softwareModule = toDelete.getSoftwareModule();
// clearArtifactBinary checks (unconditionally) software module UPDATE access
@@ -159,66 +177,6 @@ public class JpaArtifactManagement implements ArtifactManagement {
AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(() -> clearArtifactBinary(sha1Hash));
}
@Override
public Optional<Artifact> get(final long id) {
return localArtifactRepository.findById(id).map(Artifact.class::cast);
}
@Override
public Optional<Artifact> getByFilenameAndSoftwareModule(final String filename, final long softwareModuleId) {
assertSoftwareModuleExists(softwareModuleId);
return localArtifactRepository.findFirstByFilenameAndSoftwareModuleId(filename, softwareModuleId);
}
@Override
public Optional<Artifact> findFirstBySHA1(final String sha1Hash) {
return localArtifactRepository.findFirstBySha1Hash(sha1Hash);
}
@Override
public Optional<Artifact> getByFilename(final String filename) {
return localArtifactRepository.findFirstByFilename(filename);
}
@Override
public Page<Artifact> findBySoftwareModule(final long softwareModuleId, final Pageable pageable) {
assertSoftwareModuleExists(softwareModuleId);
return localArtifactRepository
.findAll(ArtifactSpecifications.bySoftwareModuleId(softwareModuleId), pageable)
.map(Artifact.class::cast);
}
@Override
public long countBySoftwareModule(final long softwareModuleId) {
assertSoftwareModuleExists(softwareModuleId);
return localArtifactRepository.count(ArtifactSpecifications.bySoftwareModuleId(softwareModuleId));
}
@SuppressWarnings("java:S2201") // java:S2201 - the idea is to just check if the artifact exists
@Override
public Optional<DbArtifact> loadArtifactBinary(final String sha1Hash, final long softwareModuleId, final boolean isEncrypted) {
assertArtifactRepositoryAvailable();
assertSoftwareModuleExists(softwareModuleId);
final String tenant = tenantAware.getCurrentTenant();
if (artifactRepository.existsByTenantAndSha1(tenant, sha1Hash)) {
// assert artifact exists and belongs to the software module
findFirstBySHA1(sha1Hash)
// if not found no assertOperationAllowed shall fail
.orElseThrow(InsufficientPermissionException::new);
final DbArtifact dbArtifact = artifactRepository.getArtifactBySha1(tenant, sha1Hash);
return Optional.ofNullable(
isEncrypted ? wrapInEncryptionAwareDbArtifact(softwareModuleId, dbArtifact) : dbArtifact);
}
return Optional.empty();
}
/**
* Garbage collects artifact binaries if only referenced by given {@link SoftwareModule#getId()} or {@link SoftwareModule}'s that are
* marked as deleted.
@@ -231,11 +189,10 @@ public class JpaArtifactManagement implements ArtifactManagement {
* @param sha1Hash no longer needed
*/
void clearArtifactBinary(final String sha1Hash) {
assertArtifactRepositoryAvailable();
DeploymentHelper.runInNewTransaction(txManager, "clearArtifactBinary", status -> {
// countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse will skip ACM checks and will return total count as it should be
if (localArtifactRepository.countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse(sha1Hash, tenantAware.getCurrentTenant()) <= 0) { // 1 artifact is the one being deleted!
if (localArtifactRepository.countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse(sha1Hash,
tenantAware.getCurrentTenant()) <= 0) { // 1 artifact is the one being deleted!
// removes the real artifact ONLY AFTER the delete of artifact or software module
// in local history has passed successfully (caller has permission and no errors)
AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(() -> {
@@ -252,19 +209,17 @@ public class JpaArtifactManagement implements ArtifactManagement {
}
private AbstractDbArtifact storeArtifact(final ArtifactUpload artifactUpload, final boolean isSmEncrypted) {
final String tenant = tenantAware.getCurrentTenant();
final long smId = artifactUpload.getModuleId();
final InputStream stream = artifactUpload.getInputStream();
final String fileName = artifactUpload.getFilename();
final String contentType = artifactUpload.getContentType();
final String providedSha1 = artifactUpload.getProvidedSha1Sum();
final String providedMd5 = artifactUpload.getProvidedMd5Sum();
final String providedSha256 = artifactUpload.getProvidedSha256Sum();
try (final InputStream wrappedStream = wrapInQuotaStream(
isSmEncrypted ? wrapInEncryptionStream(smId, stream) : stream)) {
return artifactRepository.store(tenant, wrappedStream, fileName, contentType,
new DbArtifactHash(providedSha1, providedMd5, providedSha256));
isSmEncrypted
? wrapInEncryptionStream(artifactUpload.getModuleId(), stream)
: stream)) {
return artifactRepository.store(
tenantAware.getCurrentTenant(), wrappedStream, artifactUpload.getFilename(), artifactUpload.getContentType(),
new DbArtifactHash(
artifactUpload.getProvidedSha1Sum(),
artifactUpload.getProvidedMd5Sum(),
artifactUpload.getProvidedSha256Sum()));
} catch (final ArtifactStoreException | IOException e) {
throw new ArtifactUploadFailedException(e);
} catch (final HashNotMatchException e) {
@@ -282,15 +237,6 @@ public class JpaArtifactManagement implements ArtifactManagement {
return ArtifactEncryptionService.getInstance().encryptArtifact(smId, stream);
}
private void assertArtifactQuota(final long moduleId, final int requested) {
QuotaHelper.assertAssignmentQuota(
moduleId, requested, quotaManagement.getMaxArtifactsPerSoftwareModule(),
Artifact.class, SoftwareModule.class,
// get all artifacts without user context
softwareModuleId -> localArtifactRepository
.count(null, ArtifactSpecifications.bySoftwareModuleId(softwareModuleId)));
}
private InputStream wrapInQuotaStream(final InputStream in) {
final long maxArtifactSize = quotaManagement.getMaxArtifactSize();
@@ -311,8 +257,9 @@ public class JpaArtifactManagement implements ArtifactManagement {
encryptionService.encryptionSizeOverhead());
}
private Artifact storeArtifactMetadata(final SoftwareModule softwareModule, final String providedFilename,
final AbstractDbArtifact result, final Artifact existing) {
private Artifact storeArtifactMetadata(
final SoftwareModule softwareModule, final String providedFilename, final AbstractDbArtifact result,
final Artifact existing) {
final JpaArtifact artifact;
if (existing == null) {
artifact = new JpaArtifact(result.getHashes().getSha1(), providedFilename, softwareModule);
@@ -327,16 +274,4 @@ public class JpaArtifactManagement implements ArtifactManagement {
log.debug("storing new artifact into repository {}", artifact);
return localArtifactRepository.save(AccessController.Operation.CREATE, artifact);
}
private void assertSoftwareModuleExists(final long softwareModuleId) {
if (!softwareModuleRepository.existsById(softwareModuleId)) {
throw new EntityNotFoundException(SoftwareModule.class, softwareModuleId);
}
}
private void assertArtifactRepositoryAvailable() {
if (artifactRepository == null) {
throw new UnsupportedOperationException("ArtifactRepository is unavailable");
}
}
}

View File

@@ -108,7 +108,7 @@ public class JpaConfirmationManagement extends JpaActionManagement implements Co
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Action confirmAction(final long actionId, final Integer code, final Collection<String> deviceMessages) {
log.trace("Action with id {} confirm request is triggered.", actionId);
final Action action = getActionAndThrowExceptionIfNotFound(actionId);
final Action action = actionRepository.getById(actionId);
assertActionCanAcceptFeedback(action);
final List<String> messages = new ArrayList<>();
if (deviceMessages != null) {
@@ -124,7 +124,7 @@ public class JpaConfirmationManagement extends JpaActionManagement implements Co
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Action denyAction(final long actionId, final Integer code, final Collection<String> deviceMessages) {
log.trace("Action with id {} deny request is triggered.", actionId);
final Action action = getActionAndThrowExceptionIfNotFound(actionId);
final Action action = actionRepository.getById(actionId);
assertActionCanAcceptFeedback(action);
final List<String> messages = new ArrayList<>();
if (deviceMessages != null) {

View File

@@ -223,7 +223,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
}
case DOWNLOADED: {
handleDownloadedActionStatus(action).ifPresent(controllerId ->
requestControllerAttributes(getByControllerId(controllerId)
requestControllerAttributes(findByControllerId(controllerId)
.map(JpaTarget.class::cast)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId))));
break;
@@ -239,7 +239,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Action addCancelActionStatus(final ActionStatusCreate create) {
final JpaAction action = getActionAndThrowExceptionIfNotFound(create.getActionId());
final JpaAction action = actionRepository.getById(create.getActionId());
if (!action.isCancelingOrCanceled()) {
throw new CancelActionNotAllowedException("The action is not in canceling state.");
}
@@ -284,7 +284,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public ActionStatus addInformationalActionStatus(final ActionStatusCreate create) {
final JpaAction action = getActionAndThrowExceptionIfNotFound(create.getActionId());
final JpaAction action = actionRepository.getById(create.getActionId());
assertActionStatusQuota(create, action);
final JpaActionStatus actionStatus = buildJpaActionStatus(create);
@@ -476,13 +476,13 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
}
@Override
public Optional<Target> getByControllerId(final String controllerId) {
return targetRepository.findByControllerId(controllerId).map(Target.class::cast);
public Optional<Target> find(final long targetId) {
return targetRepository.findById(targetId).map(Target.class::cast);
}
@Override
public Optional<Target> get(final long targetId) {
return targetRepository.findById(targetId).map(Target.class::cast);
public Optional<Target> findByControllerId(final String controllerId) {
return targetRepository.findByControllerId(controllerId).map(Target.class::cast);
}
@Override
@@ -538,10 +538,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
targetRepository.getAccessController().ifPresent(
accessController -> accessController.assertOperationAllowed(
AccessController.Operation.UPDATE,
actionRepository
.findById(actionId)
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId))
.getTarget()));
actionRepository.getById(actionId).getTarget()));
actionRepository.updateExternalRef(actionId, externalRef);
}
@@ -579,13 +576,14 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
public boolean updateOfflineAssignedVersion(@NotEmpty final String controllerId, final String distributionName, final String version) {
List<DistributionSetAssignmentResult> distributionSetAssignmentResults =
systemSecurityContext.runAsSystem(() ->
distributionSetManagement.findByNameAndVersion(distributionName, version).map(
distributionSet -> deploymentManagement.offlineAssignedDistributionSets(
controllerId, List.of(Map.entry(controllerId, distributionSet.getId()))))
distributionSetManagement.findByNameAndVersion(distributionName, version)
.map(distributionSet -> deploymentManagement.offlineAssignedDistributionSets(
controllerId, List.of(Map.entry(controllerId, distributionSet.getId()))))
.orElseThrow(() ->
new EntityNotFoundException(DistributionSet.class, Map.entry(distributionName, version))));
return distributionSetAssignmentResults.stream().findFirst()
return distributionSetAssignmentResults.stream()
.findFirst()
.map(result -> result.getAlreadyAssigned() == 0)
.orElseThrow();
}
@@ -901,7 +899,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
* {@link Status#RETRIEVED}
*/
private Action handleRegisterRetrieved(final Long actionId, final String message) {
final JpaAction action = getActionAndThrowExceptionIfNotFound(actionId);
final JpaAction action = actionRepository.getById(actionId);
// do a manual query with CriteriaBuilder to avoid unnecessary field queries and an extra
// count query made by spring-data when using pageable requests, we don't need an extra count
// query, we just want to check if the last action status is a retrieved or not.

View File

@@ -222,7 +222,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
private Action cancelAction0(final long actionId) {
log.debug("cancelAction({})", actionId);
final JpaAction action = actionRepository.findById(actionId).orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
final JpaAction action = actionRepository.getById(actionId);
if (action.isCancelingOrCanceled()) {
throw new CancelActionNotAllowedException("Actions in canceling or canceled state cannot be canceled");
@@ -378,7 +378,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
}
private Action forceQuitAction0(final long actionId) {
final JpaAction action = actionRepository.findById(actionId).orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
final JpaAction action = actionRepository.getById(actionId);
if (!action.isCancelingOrCanceled()) {
throw new ForceQuitActionNotAllowedException(action.getId() + " is not canceled yet and cannot be force quit");
@@ -406,7 +406,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
@Retryable(retryFor = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Action forceTargetAction(final long actionId) {
final JpaAction action = actionRepository.findById(actionId).map(this::assertTargetUpdateAllowed)
final JpaAction action = actionRepository.findById(actionId)
.map(this::assertTargetUpdateAllowed)
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
if (!action.isForcedOrTimeForced()) {
@@ -997,9 +998,4 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
throw new EntityNotFoundException(Action.class, actionId);
}
}
private Page<JpaAction> findActiveActionsForRollout(long rolloutId, Pageable pageable) {
return actionRepository
.findAll(ActionSpecifications.byRolloutIdAndActive(rolloutId), pageable);
}
}

View File

@@ -9,7 +9,6 @@
*/
package org.eclipse.hawkbit.repository.jpa.management;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
@@ -22,13 +21,10 @@ import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
import org.eclipse.hawkbit.repository.exception.StopRolloutException;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionCancellationType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation;
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidationCount;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
@@ -115,7 +111,7 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet
}
private void invalidateDistributionSet(final long setId, final ActionCancellationType cancelationType) {
final DistributionSet distributionSet = distributionSetManagement.getOrElseThrowException(setId);
final DistributionSet distributionSet = distributionSetManagement.get(setId);
if (!distributionSet.isComplete()) {
throw new IncompleteDistributionSetException(
"Distribution set of type " + distributionSet.getType().getKey() + " is incomplete: " + distributionSet.getId());

View File

@@ -105,28 +105,32 @@ public class JpaDistributionSetManagement
this.repositoryProperties = repositoryProperties;
}
@SuppressWarnings("java:S1066") // java:S1066 better readable without merging the if statements
@Override
public JpaDistributionSet update(final Update update) {
final JpaDistributionSet distributionSet = getValid0(update.getId());
// lock/unlock ONLY if locked flag is present!
final JpaDistributionSet updated = super.update(update);
if (Boolean.TRUE.equals(update.getLocked())) {
if (!distributionSet.isLocked()) {
lockSoftwareModules(distributionSet);
distributionSet.setLocked(true);
}
} else if (Boolean.FALSE.equals(update.getLocked())) {
if (distributionSet.isLocked()) {
distributionSet.setLocked(false);
lockSoftwareModules(updated);
}
return updated;
}
@Override
public Map<Long, JpaDistributionSet> update(final Collection<Update> updates) {
final Map<Long, JpaDistributionSet> updated = super.update(updates);
for (final Update update : updates) {
final JpaDistributionSet updatedSet = updated.get(update.getId());
if (Boolean.TRUE.equals(update.getLocked())) {
lockSoftwareModules(updatedSet);
}
}
return updated;
}
@Override
protected void checkUpdate(final Update update, final JpaDistributionSet distributionSet) {
if (update.getRequiredMigrationStep() != null && !update.getRequiredMigrationStep().equals(distributionSet.isRequiredMigrationStep())) {
assertDistributionSetIsNotAssignedToTargets(update.getId());
}
return super.update(update, distributionSet);
}
@Override
@@ -154,11 +158,6 @@ public class JpaDistributionSetManagement
return jpaRepository.findOne(jpaRepository.byIdSpec(id), JpaDistributionSet_.GRAPH_DISTRIBUTION_SET_DETAIL);
}
@Override
public JpaDistributionSet getOrElseThrowException(final long id) {
return getById(id);
}
// implicitly lock a distribution set if not already locked and implicit lock is enabled and not to skip
@Override
@Transactional
@@ -256,7 +255,7 @@ public class JpaDistributionSetManagement
final JpaDistributionSet set = getValid0(id);
assertDistributionSetIsNotAssignedToTargets(id);
final JpaSoftwareModule module = findSoftwareModuleAndThrowExceptionIfNotFound(moduleId);
final JpaSoftwareModule module = softwareModuleRepository.getById(moduleId);
set.removeModule(module);
return jpaRepository.save(set);
@@ -364,7 +363,7 @@ public class JpaDistributionSetManagement
}
private JpaDistributionSet getValid0(final long id) {
final JpaDistributionSet distributionSet = getById(id);
final JpaDistributionSet distributionSet = jpaRepository.getById(id);
if (!distributionSet.isValid()) {
throw new InvalidDistributionSetException(
"Distribution set of type " + distributionSet.getType().getKey() + " is invalid: " + distributionSet.getId());
@@ -375,8 +374,7 @@ public class JpaDistributionSetManagement
private List<JpaDistributionSet> updateTag(
final Collection<Long> dsIds, final long dsTagId,
final BiFunction<DistributionSetTag, JpaDistributionSet, JpaDistributionSet> updater) {
final DistributionSetTag tag = distributionSetTagManagement.get(dsTagId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSetTag.class, dsTagId));
final DistributionSetTag tag = distributionSetTagManagement.get(dsTagId);
final List<JpaDistributionSet> allDs = dsIds.size() == 1 ?
jpaRepository.findById(dsIds.iterator().next())
.map(List::of)
@@ -395,11 +393,6 @@ public class JpaDistributionSetManagement
}
}
private JpaSoftwareModule findSoftwareModuleAndThrowExceptionIfNotFound(final Long softwareModuleId) {
return softwareModuleRepository.findById(softwareModuleId)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, softwareModuleId));
}
private void assertSoftwareModuleQuota(final Long id, final int requested) {
QuotaHelper.assertAssignmentQuota(id, requested, quotaManagement.getMaxSoftwareModulesPerDistributionSet(),
SoftwareModule.class, DistributionSet.class, softwareModuleRepository::countByAssignedToId);
@@ -422,17 +415,11 @@ public class JpaDistributionSetManagement
});
}
private JpaDistributionSet getById(final long id) {
return jpaRepository
.findById(id)
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, id));
}
private JpaDistributionSet toJpaDistributionSet(final DistributionSet distributionSet) {
if (distributionSet instanceof JpaDistributionSet jpaDistributionSet) {
return jpaDistributionSet;
} else {
return getById(distributionSet.getId());
return jpaRepository.getById(distributionSet.getId());
}
}

View File

@@ -71,8 +71,7 @@ public class JpaDistributionSetTypeManagement
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public void delete(final long id) {
final JpaDistributionSetType toDelete = jpaRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, id));
final JpaDistributionSetType toDelete = jpaRepository.getById(id);
unassignDsTypeFromTargetTypes(id);
@@ -118,7 +117,7 @@ public class JpaDistributionSetTypeManagement
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
public JpaDistributionSetType unassignSoftwareModuleType(final long id, final long softwareModuleTypeId) {
final JpaDistributionSetType type = findDistributionSetTypeAndThrowExceptionIfNotFound(id);
final JpaDistributionSetType type = jpaRepository.getById(id);
checkDistributionSetTypeNotAssigned(id);
type.removeModuleType(softwareModuleTypeRepository.getById(softwareModuleTypeId));
return jpaRepository.save(type);
@@ -132,7 +131,7 @@ public class JpaDistributionSetTypeManagement
SoftwareModuleType.class, softwareModulesTypeIds, foundModules.stream().map(SoftwareModuleType::getId).toList());
}
final JpaDistributionSetType type = findDistributionSetTypeAndThrowExceptionIfNotFound(dsTypeId);
final JpaDistributionSetType type = jpaRepository.getById(dsTypeId);
checkDistributionSetTypeNotAssigned(dsTypeId);
assertSoftwareModuleTypeQuota(dsTypeId, softwareModulesTypeIds.size());
@@ -164,10 +163,6 @@ public class JpaDistributionSetTypeManagement
});
}
private JpaDistributionSetType findDistributionSetTypeAndThrowExceptionIfNotFound(final Long id) {
return jpaRepository.findById(id).orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, id));
}
private void checkDistributionSetTypeNotAssigned(final Long id) {
if (distributionSetRepository.countByTypeId(id) > 0) {
throw new EntityReadOnlyException(String.format(

View File

@@ -162,8 +162,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
@Override
public Page<Target> findTargetsOfRolloutGroup(final long rolloutGroupId, final Pageable page) {
final JpaRolloutGroup rolloutGroup = rolloutGroupRepository.findById(rolloutGroupId)
.orElseThrow(() -> new EntityNotFoundException(RolloutGroup.class, rolloutGroupId));
final JpaRolloutGroup rolloutGroup = rolloutGroupRepository.getById(rolloutGroupId);
if (isRolloutStatusReady(rolloutGroup)) {
// in case of status ready the action has not been created yet and

View File

@@ -305,7 +305,7 @@ public class JpaRolloutManagement implements RolloutManagement {
}
@Override
public Optional<Rollout> get(final long rolloutId) {
public Optional<Rollout> find(final long rolloutId) {
return rolloutRepository.findById(rolloutId).map(Rollout.class::cast);
}
@@ -316,7 +316,7 @@ public class JpaRolloutManagement implements RolloutManagement {
@Override
public Optional<Rollout> getWithDetailedStatus(final long rolloutId) {
final Optional<Rollout> rollout = get(rolloutId);
final Optional<Rollout> rollout = find(rolloutId);
if (rollout.isEmpty()) {
return rollout;
}
@@ -344,7 +344,7 @@ public class JpaRolloutManagement implements RolloutManagement {
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void pauseRollout(final long rolloutId) {
final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(rolloutId);
final JpaRollout rollout = rolloutRepository.getById(rolloutId);
if (RolloutStatus.RUNNING != rollout.getStatus()) {
throw new RolloutIllegalStateException("Rollout can only be paused in state running but current state is " +
rollout.getStatus().name().toLowerCase());
@@ -361,7 +361,7 @@ public class JpaRolloutManagement implements RolloutManagement {
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void resumeRollout(final long rolloutId) {
final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(rolloutId);
final JpaRollout rollout = rolloutRepository.getById(rolloutId);
if (RolloutStatus.PAUSED != rollout.getStatus()) {
throw new RolloutIllegalStateException("Rollout can only be resumed in state paused but current state is " +
rollout.getStatus().name().toLowerCase());
@@ -388,7 +388,7 @@ public class JpaRolloutManagement implements RolloutManagement {
private Rollout approveOrDeny0(final long rolloutId, final Rollout.ApprovalDecision decision, final String remark) {
log.debug("approveOrDeny rollout called for rollout {} with decision {}", rolloutId, decision);
final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(rolloutId);
final JpaRollout rollout = rolloutRepository.getById(rolloutId);
RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.WAITING_FOR_APPROVAL);
switch (decision) {
case APPROVED: {
@@ -417,7 +417,7 @@ public class JpaRolloutManagement implements RolloutManagement {
public Rollout start(final long rolloutId) {
log.debug("startRollout called for rollout {}", rolloutId);
final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(rolloutId);
final JpaRollout rollout = rolloutRepository.getById(rolloutId);
RolloutHelper.checkIfRolloutCanStarted(rollout, rollout);
rollout.setStatus(RolloutStatus.STARTING);
rollout.setLastCheck(0);
@@ -429,7 +429,7 @@ public class JpaRolloutManagement implements RolloutManagement {
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Rollout update(final Update update) {
final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(update.getId());
final JpaRollout rollout = rolloutRepository.getById(update.getId());
checkIfDeleted(update.getId(), rollout.getStatus());
ObjectCopyUtil.copy(update, rollout, false, UnaryOperator.identity());
@@ -441,8 +441,7 @@ public class JpaRolloutManagement implements RolloutManagement {
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Rollout stop(long rolloutId) {
final JpaRollout jpaRollout = rolloutRepository.findById(rolloutId)
.orElseThrow(() -> new EntityNotFoundException(Rollout.class, rolloutId));
final JpaRollout jpaRollout = rolloutRepository.getById(rolloutId);
if (!ROLLOUT_STATUS_STOPPABLE.contains(jpaRollout.getStatus())) {
log.debug("Failed to stop rollout {} because it is in {} status.", rolloutId, jpaRollout.getStatus());
@@ -459,9 +458,7 @@ public class JpaRolloutManagement implements RolloutManagement {
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final long rolloutId) {
final JpaRollout jpaRollout = rolloutRepository.findById(rolloutId)
.orElseThrow(() -> new EntityNotFoundException(Rollout.class, rolloutId));
this.delete0(jpaRollout);
this.delete0(rolloutRepository.getById(rolloutId));
}
@Override
@@ -490,7 +487,7 @@ public class JpaRolloutManagement implements RolloutManagement {
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void triggerNextGroup(final long rolloutId) {
final JpaRollout rollout = getRolloutOrThrowExceptionIfNotFound(rolloutId);
final JpaRollout rollout = rolloutRepository.getById(rolloutId);
if (RolloutStatus.RUNNING != rollout.getStatus()) {
throw new RolloutIllegalStateException("Rollout is not in running state");
}
@@ -891,11 +888,6 @@ public class JpaRolloutManagement implements RolloutManagement {
group.setErrorActionExp(errorActionExp);
}
private JpaRollout getRolloutOrThrowExceptionIfNotFound(final Long rolloutId) {
return rolloutRepository.findById(rolloutId)
.orElseThrow(() -> new EntityNotFoundException(Rollout.class, rolloutId));
}
private @NotNull Map<Long, List<TotalTargetCountActionStatus>> getStatusCountItemForRollout(final List<Long> rollouts) {
if (rollouts.isEmpty()) {
return Collections.emptyMap();

View File

@@ -101,21 +101,6 @@ public class JpaSoftwareModuleManagement
return createdModule;
}
@Override
public JpaSoftwareModule update(final Update update) {
final JpaSoftwareModule module = jpaRepository.findById(update.getId())
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, update.getId()));
// lock/unlock ONLY if locked flag is present!
if (Boolean.TRUE.equals(update.getLocked())) {
module.lock();
} else if (Boolean.FALSE.equals(update.getLocked())) {
module.unlock();
}
return super.update(update, module);
}
@Override
protected List<JpaSoftwareModule> softDelete(final Collection<JpaSoftwareModule> toDelete) {
return toDelete.stream()
@@ -213,16 +198,10 @@ public class JpaSoftwareModuleManagement
if (softwareModule instanceof JpaSoftwareModule jpaSoftwareModule) {
return jpaSoftwareModule;
} else {
return getById(softwareModule.getId());
return jpaRepository.getById(softwareModule.getId());
}
}
private JpaSoftwareModule getById(final long id) {
return jpaRepository
.findById(id)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id));
}
private void deleteGridFsArtifacts(final JpaSoftwareModule swModule) {
jpaRepository.getAccessController().ifPresent(accessController ->
accessController.assertOperationAllowed(AccessController.Operation.DELETE, swModule));

View File

@@ -267,15 +267,13 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TenantMetaData updateTenantMetadata(final long defaultDsType) {
final JpaTenantMetaData data = (JpaTenantMetaData) getTenantMetadataWithoutDetails();
data.setDefaultDsType(distributionSetTypeRepository.findById(defaultDsType)
.orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, defaultDsType)));
data.setDefaultDsType(distributionSetTypeRepository.getById(defaultDsType));
return tenantMetaDataRepository.save(data);
}
@Override
public TenantMetaData getTenantMetadata(final long tenantId) {
return tenantMetaDataRepository.findById(tenantId)
.orElseThrow(() -> new EntityNotFoundException(TenantMetaData.class, tenantId));
return tenantMetaDataRepository.findById(tenantId).orElseThrow(() -> new EntityNotFoundException(TenantMetaData.class, tenantId));
}
private static boolean isPostgreSql(final JpaProperties properties) {

View File

@@ -27,7 +27,6 @@ import org.eclipse.hawkbit.repository.TargetFilterQueryFields;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException;
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
@@ -132,7 +131,7 @@ class JpaTargetFilterQueryManagement
@Override
public Page<TargetFilterQuery> findByAutoAssignDSAndRsql(final long setId, final String rsql, final Pageable pageable) {
final DistributionSet distributionSet = distributionSetManagement.getOrElseThrowException(setId);
final DistributionSet distributionSet = distributionSetManagement.get(setId);
final List<Specification<JpaTargetFilterQuery>> specList = new ArrayList<>(2);
specList.add(TargetFilterQuerySpecification.byAutoAssignDS(distributionSet));
@@ -152,7 +151,7 @@ class JpaTargetFilterQueryManagement
@Override
@Transactional
public TargetFilterQuery updateAutoAssignDS(final AutoAssignDistributionSetUpdate update) {
final JpaTargetFilterQuery targetFilterQuery = findTargetFilterQueryOrThrowExceptionIfNotFound(update.targetFilterId());
final JpaTargetFilterQuery targetFilterQuery = jpaRepository.getById(update.targetFilterId());
if (update.dsId() == null) {
targetFilterQuery.setAccessControlContext(null);
targetFilterQuery.setAutoAssignDistributionSet(null);
@@ -201,12 +200,7 @@ class JpaTargetFilterQueryManagement
}
private boolean isConfirmationFlowEnabled() {
return TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement)
.isConfirmationFlowEnabled();
}
private JpaTargetFilterQuery findTargetFilterQueryOrThrowExceptionIfNotFound(final Long queryId) {
return jpaRepository.findById(queryId).orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, queryId));
return TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).isConfirmationFlowEnabled();
}
private void assertMaxTargetsQuota(final String query, final String filterName, final long dsId) {
@@ -245,7 +239,7 @@ class JpaTargetFilterQueryManagement
}
private void validate(final Update update) {
final JpaTargetFilterQuery targetFilterQuery = findTargetFilterQueryOrThrowExceptionIfNotFound(update.getId());
final JpaTargetFilterQuery targetFilterQuery = jpaRepository.getById(update.getId());
Optional.ofNullable(update.getQuery()).ifPresent(query -> {
// validate the RSQL query syntax
RsqlUtility.getInstance().validateRsqlFor(query, TargetFields.class, JpaTarget.class);

View File

@@ -113,8 +113,7 @@ public class JpaTargetManagement
public boolean isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
final String controllerId, final long distributionSetId, final String targetFilterQuery) {
RsqlUtility.getInstance().validateRsqlFor(targetFilterQuery, TargetFields.class, JpaTarget.class);
final DistributionSet ds = distributionSetManagement.get(distributionSetId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, distributionSetId));
final DistributionSet ds = distributionSetManagement.get(distributionSetId);
final Long distSetTypeId = ds.getType().getId();
final List<Specification<JpaTarget>> specList = List.of(
RsqlUtility.getInstance().buildRsqlSpecification(targetFilterQuery, TargetFields.class),
@@ -130,7 +129,7 @@ public class JpaTargetManagement
@Override
public Slice<Target> findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(
final long distributionSetId, final String rsql, final Pageable pageable) {
final DistributionSet jpaDistributionSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
final DistributionSet jpaDistributionSet = distributionSetManagement.get(distributionSetId);
final Long distSetTypeId = jpaDistributionSet.getType().getId();
return jpaRepository
@@ -205,7 +204,7 @@ public class JpaTargetManagement
@Override
public Page<Target> findByAssignedDistributionSet(final long distributionSetId, final Pageable pageable) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
final DistributionSet validDistSet = distributionSetManagement.get(distributionSetId);
return JpaManagementHelper.findAllWithCountBySpec(
jpaRepository,
@@ -214,7 +213,7 @@ public class JpaTargetManagement
@Override
public Page<Target> findByAssignedDistributionSetAndRsql(final long distributionSetId, final String rsql, final Pageable pageable) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
final DistributionSet validDistSet = distributionSetManagement.get(distributionSetId);
final List<Specification<JpaTarget>> specList = List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
@@ -225,7 +224,7 @@ public class JpaTargetManagement
@Override
public Page<Target> findByInstalledDistributionSet(final long distributionSetId, final Pageable pageReq) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
final DistributionSet validDistSet = distributionSetManagement.get(distributionSetId);
return JpaManagementHelper.findAllWithCountBySpec(
jpaRepository, List.of(TargetSpecifications.hasInstalledDistributionSet(validDistSet.getId())), pageReq);
@@ -233,7 +232,7 @@ public class JpaTargetManagement
@Override
public Page<Target> findByInstalledDistributionSetAndRsql(final long distributionSetId, final String rsql, final Pageable pageable) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
final DistributionSet validDistSet = distributionSetManagement.get(distributionSetId);
final List<Specification<JpaTarget>> specList = List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
@@ -275,7 +274,7 @@ public class JpaTargetManagement
@Override
public long countByRsqlAndNonDsAndCompatibleAndUpdatable(final long distributionSetId, final String rsql) {
final DistributionSet jpaDistributionSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
final DistributionSet jpaDistributionSet = distributionSetManagement.get(distributionSetId);
final Long distSetTypeId = jpaDistributionSet.getType().getId();
return jpaRepository.count(
@@ -314,7 +313,7 @@ public class JpaTargetManagement
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteByControllerId(final String controllerId) {
jpaRepository.delete(getByControllerIdAndThrowIfNotFound(controllerId));
jpaRepository.delete(jpaRepository.getByControllerId(controllerId));
}
@Override
@@ -386,12 +385,12 @@ public class JpaTargetManagement
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target assignType(final String controllerId, final Long targetTypeId) {
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
final JpaTarget target = jpaRepository.getByControllerId(controllerId);
jpaRepository.getAccessController().ifPresent(acm ->
acm.assertOperationAllowed(AccessController.Operation.UPDATE, target));
final JpaTargetType targetType = getTargetTypeByIdAndThrowIfNotFound(targetTypeId);
final JpaTargetType targetType = targetTypeRepository.getById(targetTypeId);
target.setTargetType(targetType);
return jpaRepository.save(target);
}
@@ -401,7 +400,7 @@ public class JpaTargetManagement
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target unassignType(final String controllerId) {
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
final JpaTarget target = jpaRepository.getByControllerId(controllerId);
target.setTargetType(null);
return jpaRepository.save(target);
}
@@ -460,7 +459,7 @@ public class JpaTargetManagement
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void createMetadata(final String controllerId, final String key, final String value) {
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
final JpaTarget target = jpaRepository.getByControllerId(controllerId);
// get the modifiable metadata map
final Map<String, String> metadata = target.getMetadata();
@@ -477,7 +476,7 @@ public class JpaTargetManagement
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void createMetadata(final String controllerId, final Map<String, String> md) {
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
final JpaTarget target = jpaRepository.getByControllerId(controllerId);
// get the modifiable metadata map
final Map<String, String> metadata = target.getMetadata();
@@ -507,7 +506,7 @@ public class JpaTargetManagement
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteMetadata(final String controllerId, final String key) {
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
final JpaTarget target = jpaRepository.getByControllerId(controllerId);
// get the modifiable metadata map
final Map<String, String> metadata = target.getMetadata();
@@ -519,7 +518,7 @@ public class JpaTargetManagement
}
private Map<String, String> getMap(final String controllerId, final MapAttribute<JpaTarget, String, String> mapAttribute) {
getByControllerIdAndThrowIfNotFound(controllerId);
jpaRepository.getByControllerId(controllerId);
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
@@ -544,14 +543,6 @@ public class JpaTargetManagement
return controllerIds.stream().filter(id -> !foundTargetMap.containsKey(id)).toList();
}
private JpaTarget getByControllerIdAndThrowIfNotFound(final String controllerId) {
return jpaRepository.getByControllerId(controllerId);
}
private JpaTargetType getTargetTypeByIdAndThrowIfNotFound(final long id) {
return targetTypeRepository.findById(id).orElseThrow(() -> new EntityNotFoundException(TargetType.class, id));
}
private void assertMetadataQuota(final Long targetId, final int requested) {
final int limit = quotaManagement.getMaxMetaDataEntriesPerTarget();
QuotaHelper.assertAssignmentQuota(targetId, requested, limit, "Metadata", Target.class.getSimpleName(), null);
@@ -560,8 +551,7 @@ public class JpaTargetManagement
private List<Target> updateTag(
final Collection<String> controllerIds, final long targetTagId, final Consumer<Collection<String>> notFoundHandler,
final BiFunction<JpaTargetTag, JpaTarget, Target> updater) {
final JpaTargetTag tag = targetTagRepository.findById(targetTagId)
.orElseThrow(() -> new EntityNotFoundException(TargetTag.class, targetTagId));
final JpaTargetTag tag = targetTagRepository.getById(targetTagId);
final List<JpaTarget> targets = controllerIds.size() == 1 ?
jpaRepository.findByControllerId(controllerIds.iterator().next())
.map(List::of)

View File

@@ -99,7 +99,7 @@ public class JpaTargetTypeManagement
dsTypes.stream().map(DistributionSetType::getId).toList());
}
final JpaTargetType type = getByIdAndThrowIfNotFound(id);
final JpaTargetType type = jpaRepository.getById(id);
assertDistributionSetTypeQuota(id, distributionSetTypeIds.size(), typeId -> type.getDistributionSetTypes().size());
dsTypes.forEach(type::addCompatibleDistributionSetType);
@@ -111,27 +111,16 @@ public class JpaTargetTypeManagement
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetType unassignDistributionSetType(final long id, final long distributionSetTypeId) {
final JpaTargetType type = getByIdAndThrowIfNotFound(id);
assertDistributionSetTypeExists(distributionSetTypeId);
final JpaTargetType type = jpaRepository.getById(id);
if (!distributionSetTypeRepository.existsById(distributionSetTypeId)) {
throw new EntityNotFoundException(DistributionSetType.class, distributionSetTypeId);
}
type.removeDistributionSetType(distributionSetTypeId);
return jpaRepository.save(type);
}
@SuppressWarnings("java:S2201") // the idea is just to check for distribution set type existence
private void assertDistributionSetTypeExists(final Long typeId) {
distributionSetTypeRepository
.findById(typeId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, typeId));
}
private JpaTargetType getByIdAndThrowIfNotFound(final Long id) {
return jpaRepository
.findById(id)
.orElseThrow(() -> new EntityNotFoundException(TargetType.class, id));
}
/**
* Enforces the quota specifying the maximum number of
* {@link DistributionSetType}s per {@link TargetType}.

View File

@@ -32,7 +32,6 @@ import org.springframework.transaction.annotation.Transactional;
/**
* Command repository operations for all {@link TenantAwareBaseEntity}s.
*
* @param <T> type if the entity type
*/
@NoRepositoryBean

View File

@@ -56,6 +56,12 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
return repository.save(entity);
}
// override because the default implementation is not protected by ACM proxy
@Override
public T getById(final Long id) {
return findOne(byIdSpec(id)).orElseThrow(() -> new EntityNotFoundException(getManagementClass(), id));
}
@Override
@NonNull
public Optional<T> findById(@NonNull final Long id) {

View File

@@ -55,14 +55,6 @@ public interface LocalArtifactRepository extends BaseEntityRepository<JpaArtifac
*/
long countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse(@Param("sha1") String sha1, @Param("tenant") String tenant);
/**
* Searches for a {@link Artifact} based on given gridFsFileName.
*
* @param sha1Hash to search
* @return {@link Artifact} the first in the result list
*/
Optional<Artifact> findFirstBySha1Hash(String sha1Hash);
/**
* Searches for a {@link Artifact} based user provided filename at upload.
*

View File

@@ -10,6 +10,7 @@
package org.eclipse.hawkbit.repository.jpa.rollout.condition;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.model.Rollout;
@@ -55,7 +56,8 @@ public class PauseRolloutGroupAction implements RolloutGroupActionEvaluator<Roll
and this one tries to pause the rollout too but throws an exception
and rollbacks rollout processing transaction
*/
final Rollout refreshedRollout = rolloutManagement.get(rollout.getId()).orElseThrow();
final Rollout refreshedRollout = rolloutManagement.find(rollout.getId())
.orElseThrow(() -> new EntityNotFoundException(Rollout.class, rollout.getId()));
if (Rollout.RolloutStatus.PAUSED != refreshedRollout.getStatus()) {
// if only the latest state is != paused then pause
rolloutManagement.pauseRollout(rollout.getId());