Reduce probability of ConcurrentModification errors (#505)

* Fix unassign handling for tags.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Batch writes.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Activate retry on DB concurrency exceptions.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Code format, constants.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
Kai Zimmermann
2017-05-11 21:32:01 +02:00
committed by GitHub
parent c18e9f515e
commit 80f4761fff
22 changed files with 297 additions and 63 deletions

View File

@@ -71,9 +71,9 @@ public interface DistributionSetTagRepository
/**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety
* reasons (this is a "delete everything" query after all) we add the tenant manually to
* query even if this will by done by {@link EntityManager} anyhow. The DB
* should take care of optimizing this away.
* reasons (this is a "delete everything" query after all) we add the tenant
* manually to query even if this will by done by {@link EntityManager}
* anyhow. The DB should take care of optimizing this away.
*
* @param tenant
* to delete data from

View File

@@ -64,9 +64,9 @@ public interface DistributionSetTypeRepository
/**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety
* reasons (this is a "delete everything" query after all) we add the tenant manually to
* query even if this will by done by {@link EntityManager} anyhow. The DB
* should take care of optimizing this away.
* reasons (this is a "delete everything" query after all) we add the tenant
* manually to query even if this will by done by {@link EntityManager}
* anyhow. The DB should take care of optimizing this away.
*
* @param tenant
* to delete data from

View File

@@ -23,6 +23,7 @@ import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InvalidMD5HashException;
import org.eclipse.hawkbit.repository.exception.InvalidSHA1HashException;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.model.Artifact;
@@ -30,8 +31,11 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -71,6 +75,8 @@ public class JpaArtifactManagement implements ArtifactManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Artifact createArtifact(final InputStream stream, final Long moduleId, final String filename,
final String providedMd5Sum, final String providedSha1Sum, final boolean overrideExisting,
final String contentType) {
@@ -101,6 +107,8 @@ public class JpaArtifactManagement implements ArtifactManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public boolean clearArtifactBinary(final String sha1Hash, final Long moduleId) {
if (localArtifactRepository.existsWithSha1HashAndSoftwareModuleIdIsNot(sha1Hash, moduleId)) {
@@ -119,6 +127,8 @@ public class JpaArtifactManagement implements ArtifactManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteArtifact(final Long id) {
final JpaArtifact existing = (JpaArtifact) findArtifact(id)
.orElseThrow(() -> new EntityNotFoundException(Artifact.class, id));
@@ -186,6 +196,8 @@ public class JpaArtifactManagement implements ArtifactManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Artifact createArtifact(final InputStream inputStream, final Long moduleId, final String filename,
final boolean overrideExisting) {
return createArtifact(inputStream, moduleId, filename, null, null, overrideExisting, null);

View File

@@ -33,6 +33,7 @@ import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.ToManyAttributeEntriesException;
import org.eclipse.hawkbit.repository.exception.TooManyStatusEntriesException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaActionStatusCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
@@ -56,9 +57,12 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -126,6 +130,8 @@ public class JpaControllerManagement implements ControllerManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target updateLastTargetQuery(final String controllerId, final URI address) {
final JpaTarget target = (JpaTarget) targetRepository.findByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
@@ -195,6 +201,8 @@ public class JpaControllerManagement implements ControllerManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target findOrRegisterTargetIfItDoesNotexist(final String controllerId, final URI address) {
final Specification<JpaTarget> spec = (targetRoot, query, cb) -> cb
.equal(targetRoot.get(JpaTarget_.controllerId), controllerId);
@@ -240,6 +248,8 @@ public class JpaControllerManagement implements ControllerManagement {
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Action addCancelActionStatus(final ActionStatusCreate c) {
final JpaActionStatusCreate create = (JpaActionStatusCreate) c;
@@ -283,6 +293,8 @@ public class JpaControllerManagement implements ControllerManagement {
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Action addUpdateActionStatus(final ActionStatusCreate c) {
final JpaActionStatusCreate create = (JpaActionStatusCreate) c;
final JpaAction action = getActionAndThrowExceptionIfNotFound(create.getActionId());
@@ -381,6 +393,8 @@ public class JpaControllerManagement implements ControllerManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target updateControllerAttributes(final String controllerId, final Map<String, String> data) {
final JpaTarget target = (JpaTarget) targetRepository.findByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
@@ -401,6 +415,8 @@ public class JpaControllerManagement implements ControllerManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Action registerRetrieved(final Long actionId, final String message) {
return handleRegisterRetrieved(actionId, message);
}
@@ -460,6 +476,8 @@ public class JpaControllerManagement implements ControllerManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public ActionStatus addInformationalActionStatus(final ActionStatusCreate c) {
final JpaActionStatusCreate create = (JpaActionStatusCreate) c;
final JpaAction action = getActionAndThrowExceptionIfNotFound(create.getActionId());

View File

@@ -67,6 +67,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
@@ -74,6 +75,8 @@ import org.springframework.data.domain.PageRequest;
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;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Isolation;
@@ -136,6 +139,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
// Exception squid:S2095: see
// https://jira.sonarsource.com/browse/SONARJAVA-1478
@SuppressWarnings({ "squid:S2095" })
@@ -147,6 +152,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetAssignmentResult assignDistributionSet(final Long dsID,
final Collection<TargetWithActionType> targets) {
return assignDistributionSet(dsID, targets, null);
@@ -154,6 +161,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetAssignmentResult assignDistributionSet(final Long dsID,
final Collection<TargetWithActionType> targets, final String actionMessage) {
final JpaDistributionSet set = distributionSetRepository.findOne(dsID);
@@ -348,6 +357,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Action cancelAction(final Long actionId) {
LOG.debug("cancelAction({})", actionId);
@@ -390,6 +401,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Action forceQuitAction(final Long actionId) {
final JpaAction action = actionRepository.findById(actionId)
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
@@ -595,6 +608,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@Override
@Transactional
@Retryable(include = {
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)
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));

View File

@@ -9,7 +9,6 @@
package org.eclipse.hawkbit.repository.jpa;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -36,6 +35,7 @@ import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetCreate;
import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetTypeCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.DsMetadataCompositeKey;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
@@ -64,10 +64,13 @@ import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
@@ -140,6 +143,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetTagAssignmentResult toggleTagAssignment(final Collection<Long> dsIds, final String tagName) {
final List<JpaDistributionSet> sets = findDistributionSetListWithDetails(dsIds);
@@ -187,6 +192,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSet updateDistributionSet(final DistributionSetUpdate u) {
final GenericDistributionSetUpdate update = (GenericDistributionSetUpdate) u;
@@ -237,6 +244,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteDistributionSet(final Collection<Long> distributionSetIDs) {
final List<DistributionSet> setsFound = findDistributionSetsById(distributionSetIDs);
@@ -274,6 +283,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSet createDistributionSet(final DistributionSetCreate c) {
final JpaDistributionSetCreate create = (JpaDistributionSetCreate) c;
if (create.getType() == null) {
@@ -285,12 +296,16 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<DistributionSet> createDistributionSets(final Collection<DistributionSetCreate> creates) {
return creates.stream().map(this::createDistributionSet).collect(Collectors.toList());
}
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSet assignSoftwareModules(final Long setId, final Collection<Long> moduleIds) {
final Collection<JpaSoftwareModule> modules = softwareModuleRepository.findByIdIn(moduleIds);
@@ -310,6 +325,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSet unassignSoftwareModule(final Long setId, final Long moduleId) {
final JpaDistributionSet set = findDistributionSetAndThrowExceptionIfNotFound(setId);
final JpaSoftwareModule module = findSoftwareModuleAndThrowExceptionIfNotFound(moduleId);
@@ -323,6 +340,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetType updateDistributionSetType(final DistributionSetTypeUpdate u) {
final GenericDistributionSetTypeUpdate update = (GenericDistributionSetTypeUpdate) u;
@@ -349,6 +368,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetType assignMandatorySoftwareModuleTypes(final Long dsTypeId,
final Collection<Long> softwareModulesTypeIds) {
final Collection<JpaSoftwareModuleType> modules = softwareModuleTypeRepository
@@ -369,6 +390,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetType assignOptionalSoftwareModuleTypes(final Long dsTypeId,
final Collection<Long> softwareModulesTypeIds) {
@@ -397,6 +420,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetType unassignSoftwareModuleType(final Long dsTypeId, final Long softwareModuleTypeId) {
final JpaDistributionSetType type = findDistributionSetTypeAndThrowExceptionIfNotFound(dsTypeId);
@@ -581,6 +606,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetType createDistributionSetType(final DistributionSetTypeCreate c) {
final JpaDistributionSetTypeCreate create = (JpaDistributionSetTypeCreate) c;
@@ -589,6 +616,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteDistributionSetType(final Long typeId) {
final JpaDistributionSetType toDelete = distributionSetTypeRepository.findById(typeId)
@@ -604,6 +633,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<DistributionSetMetadata> createDistributionSetMetadata(final Long dsId, final Collection<MetaData> md) {
md.forEach(meta -> checkAndThrowAlreadyIfDistributionSetMetadataExists(
@@ -619,6 +650,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetMetadata updateDistributionSetMetadata(final Long dsId, final MetaData md) {
// check if exists otherwise throw entity not found exception
@@ -634,6 +667,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteDistributionSetMetadata(final Long distributionSetId, final String key) {
final JpaDistributionSetMetadata metadata = (JpaDistributionSetMetadata) findDistributionSetMetadata(
distributionSetId, key).orElseThrow(
@@ -818,6 +853,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<DistributionSet> assignTag(final Collection<Long> dsIds, final Long dsTagId) {
final List<JpaDistributionSet> allDs = findDistributionSetListWithDetails(dsIds);
@@ -831,41 +868,46 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
allDs.forEach(ds -> ds.addTag(distributionSetTag));
return Collections
final List<DistributionSet> result = Collections
.unmodifiableList(allDs.stream().map(distributionSetRepository::save).collect(Collectors.toList()));
// No reason to save the tag
entityManager.detach(distributionSetTag);
return result;
}
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSet unAssignTag(final Long dsId, final Long dsTagId) {
final List<JpaDistributionSet> allDs = findDistributionSetListWithDetails(Arrays.asList(dsId));
if (allDs.isEmpty()) {
throw new EntityNotFoundException(DistributionSet.class, dsId);
}
final JpaDistributionSet set = (JpaDistributionSet) findDistributionSetByIdWithDetails(dsId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, dsId));
final DistributionSetTag distributionSetTag = tagManagement.findDistributionSetTagById(dsTagId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSetTag.class, dsTagId));
final List<JpaDistributionSet> unAssignTag = unAssignTag(allDs, distributionSetTag);
return unAssignTag.isEmpty() ? null : unAssignTag.get(0);
}
private List<JpaDistributionSet> unAssignTag(final Collection<JpaDistributionSet> distributionSets,
final DistributionSetTag tag) {
distributionSets.forEach(ds -> ds.removeTag(tag));
return Collections.unmodifiableList(
distributionSets.stream().map(distributionSetRepository::save).collect(Collectors.toList()));
set.removeTag(distributionSetTag);
final JpaDistributionSet result = distributionSetRepository.save(set);
// No reason to save the tag
entityManager.detach(distributionSetTag);
return result;
}
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<DistributionSetType> createDistributionSetTypes(final Collection<DistributionSetTypeCreate> types) {
return types.stream().map(this::createDistributionSetType).collect(Collectors.toList());
}
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteDistributionSet(final Long setId) {
throwExceptionIfDistributionSetDoesNotExist(setId);

View File

@@ -40,6 +40,7 @@ import org.eclipse.hawkbit.repository.exception.ConstraintViolationException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
@@ -74,6 +75,7 @@ import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
@@ -82,6 +84,8 @@ import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.integration.support.locks.LockRegistry;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.transaction.PlatformTransactionManager;
@@ -179,6 +183,8 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Rollout createRollout(final RolloutCreate rollout, final int amountGroup,
final RolloutGroupConditions conditions) {
RolloutHelper.verifyRolloutGroupParameter(amountGroup, quotaManagement);
@@ -188,6 +194,8 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Rollout createRollout(final RolloutCreate rollout, final List<RolloutGroupCreate> groups,
final RolloutGroupConditions conditions) {
RolloutHelper.verifyRolloutGroupParameter(groups.size(), quotaManagement);
@@ -422,6 +430,8 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Rollout startRollout(final Long rolloutId) {
LOGGER.debug("startRollout called for rollout {}", rolloutId);
@@ -552,6 +562,8 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void pauseRollout(final Long rolloutId) {
final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId);
if (!RolloutStatus.RUNNING.equals(rollout.getStatus())) {
@@ -569,6 +581,8 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void resumeRollout(final Long rolloutId) {
final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId);
if (!(RolloutStatus.PAUSED.equals(rollout.getStatus()))) {
@@ -795,6 +809,8 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteRollout(final long rolloutId) {
final JpaRollout jpaRollout = rolloutRepository.findOne(rolloutId);
@@ -903,6 +919,8 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Rollout updateRollout(final RolloutUpdate u) {
final GenericRolloutUpdate update = (GenericRolloutUpdate) u;
final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(update.getId());

View File

@@ -42,6 +42,7 @@ import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleCreate;
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleTypeCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
@@ -62,6 +63,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
@@ -69,6 +71,8 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.SliceImpl;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -117,6 +121,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public SoftwareModule updateSoftwareModule(final SoftwareModuleUpdate u) {
final GenericSoftwareModuleUpdate update = (GenericSoftwareModuleUpdate) u;
@@ -131,6 +137,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public SoftwareModuleType updateSoftwareModuleType(final SoftwareModuleTypeUpdate u) {
final GenericSoftwareModuleTypeUpdate update = (GenericSoftwareModuleTypeUpdate) u;
@@ -145,6 +153,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public SoftwareModule createSoftwareModule(final SoftwareModuleCreate c) {
final JpaSoftwareModuleCreate create = (JpaSoftwareModuleCreate) c;
@@ -153,6 +163,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<SoftwareModule> createSoftwareModule(final Collection<SoftwareModuleCreate> swModules) {
return swModules.stream().map(this::createSoftwareModule).collect(Collectors.toList());
}
@@ -225,6 +237,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteSoftwareModules(final Collection<Long> ids) {
final List<JpaSoftwareModule> swModulesToDelete = softwareModuleRepository.findByIdIn(ids);
@@ -483,6 +497,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public SoftwareModuleType createSoftwareModuleType(final SoftwareModuleTypeCreate c) {
final JpaSoftwareModuleTypeCreate create = (JpaSoftwareModuleTypeCreate) c;
@@ -491,6 +507,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteSoftwareModuleType(final Long typeId) {
final JpaSoftwareModuleType toDelete = softwareModuleTypeRepository.findById(typeId)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModuleType.class, typeId));
@@ -515,6 +533,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public SoftwareModuleMetadata createSoftwareModuleMetadata(final Long moduleId, final MetaData md) {
checkAndThrowAlreadyIfSoftwareModuleMetadataExists(moduleId, md);
@@ -531,6 +551,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<SoftwareModuleMetadata> createSoftwareModuleMetadata(final Long moduleId,
final Collection<MetaData> md) {
md.forEach(meta -> checkAndThrowAlreadyIfSoftwareModuleMetadataExists(moduleId, meta));
@@ -545,6 +567,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public SoftwareModuleMetadata updateSoftwareModuleMetadata(final Long moduleId, final MetaData md) {
// check if exists otherwise throw entity not found exception
@@ -589,6 +613,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteSoftwareModuleMetadata(final Long moduleId, final String key) {
final JpaSoftwareModuleMetadata metadata = (JpaSoftwareModuleMetadata) findSoftwareModuleMetadata(moduleId, key)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModuleMetadata.class, moduleId, key));
@@ -653,12 +679,16 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteSoftwareModule(final Long moduleId) {
deleteSoftwareModules(Sets.newHashSet(moduleId));
}
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<SoftwareModuleType> createSoftwareModuleType(final Collection<SoftwareModuleTypeCreate> creates) {
return creates.stream().map(this::createSoftwareModuleType).collect(Collectors.toList());
}

View File

@@ -17,9 +17,9 @@ import java.util.stream.Collectors;
import javax.persistence.EntityManager;
import org.eclipse.hawkbit.cache.TenancyCacheManager;
import org.eclipse.hawkbit.repository.Constants;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TenantStatsManagement;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.configuration.MultiTenantJpaTransactionManager;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
@@ -34,10 +34,13 @@ import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Propagation;
@@ -211,6 +214,8 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteTenant(final String t) {
final String tenant = t.toUpperCase();
cacheManager.evictCaches(tenant);
@@ -254,6 +259,8 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TenantMetaData updateTenantMetadata(final Long defaultDsType) {
final JpaTenantMetaData data = (JpaTenantMetaData) getTenantMetadata();
@@ -264,20 +271,26 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
private DistributionSetType createStandardSoftwareDataSetup() {
final SoftwareModuleType app = softwareModuleTypeRepository
.save(new JpaSoftwareModuleType(Constants.SMT_DEFAULT_APP_KEY, Constants.SMT_DEFAULT_APP_NAME,
"Application Addons", Integer.MAX_VALUE));
.save(new JpaSoftwareModuleType(org.eclipse.hawkbit.repository.Constants.SMT_DEFAULT_APP_KEY,
org.eclipse.hawkbit.repository.Constants.SMT_DEFAULT_APP_NAME, "Application Addons",
Integer.MAX_VALUE));
final SoftwareModuleType os = softwareModuleTypeRepository.save(new JpaSoftwareModuleType(
Constants.SMT_DEFAULT_OS_KEY, Constants.SMT_DEFAULT_OS_NAME, "Core firmware or operationg system", 1));
org.eclipse.hawkbit.repository.Constants.SMT_DEFAULT_OS_KEY,
org.eclipse.hawkbit.repository.Constants.SMT_DEFAULT_OS_NAME, "Core firmware or operationg system", 1));
// make sure the module types get their IDs
entityManager.flush();
distributionSetTypeRepository.save(new JpaDistributionSetType(Constants.DST_DEFAULT_OS_ONLY_KEY,
Constants.DST_DEFAULT_OS_ONLY_NAME, "Default type with Firmware/OS only.").addMandatoryModuleType(os));
distributionSetTypeRepository
.save(new JpaDistributionSetType(org.eclipse.hawkbit.repository.Constants.DST_DEFAULT_OS_ONLY_KEY,
org.eclipse.hawkbit.repository.Constants.DST_DEFAULT_OS_ONLY_NAME,
"Default type with Firmware/OS only.").addMandatoryModuleType(os));
return distributionSetTypeRepository.save(new JpaDistributionSetType(Constants.DST_DEFAULT_OS_WITH_APPS_KEY,
Constants.DST_DEFAULT_OS_WITH_APPS_NAME, "Default type with Firmware/OS and optional app(s).")
.addMandatoryModuleType(os).addOptionalModuleType(app));
return distributionSetTypeRepository
.save(new JpaDistributionSetType(org.eclipse.hawkbit.repository.Constants.DST_DEFAULT_OS_WITH_APPS_KEY,
org.eclipse.hawkbit.repository.Constants.DST_DEFAULT_OS_WITH_APPS_NAME,
"Default type with Firmware/OS and optional app(s).").addMandatoryModuleType(os)
.addOptionalModuleType(app));
}
@Override

View File

@@ -21,6 +21,7 @@ import org.eclipse.hawkbit.repository.builder.TagCreate;
import org.eclipse.hawkbit.repository.builder.TagUpdate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTagCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
@@ -31,10 +32,13 @@ import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -68,6 +72,8 @@ public class JpaTagManagement implements TagManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetTag createTargetTag(final TagCreate c) {
final JpaTagCreate create = (JpaTagCreate) c;
@@ -76,6 +82,8 @@ public class JpaTagManagement implements TagManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<TargetTag> createTargetTags(final Collection<TagCreate> tt) {
@SuppressWarnings({ "unchecked", "rawtypes" })
final Collection<JpaTagCreate> targetTags = (Collection) tt;
@@ -86,6 +94,8 @@ public class JpaTagManagement implements TagManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteTargetTag(final String targetTagName) {
if (!targetTagRepository.existsByName(targetTagName)) {
throw new EntityNotFoundException(TargetTag.class, targetTagName);
@@ -118,6 +128,8 @@ public class JpaTagManagement implements TagManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetTag updateTargetTag(final TagUpdate u) {
final GenericTagUpdate update = (GenericTagUpdate) u;
@@ -132,8 +144,9 @@ public class JpaTagManagement implements TagManagement {
}
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetTag updateDistributionSetTag(final TagUpdate u) {
final GenericTagUpdate update = (GenericTagUpdate) u;
@@ -154,6 +167,8 @@ public class JpaTagManagement implements TagManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetTag createDistributionSetTag(final TagCreate c) {
final JpaTagCreate create = (JpaTagCreate) c;
return distributionSetTagRepository.save(create.buildDistributionSetTag());
@@ -161,6 +176,8 @@ public class JpaTagManagement implements TagManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<DistributionSetTag> createDistributionSetTags(final Collection<TagCreate> dst) {
@SuppressWarnings({ "rawtypes", "unchecked" })
@@ -173,6 +190,8 @@ public class JpaTagManagement implements TagManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteDistributionSetTag(final String tagName) {
if (!distributionSetTagRepository.existsByName(tagName)) {
throw new EntityNotFoundException(DistributionSetTag.class, tagName);

View File

@@ -22,6 +22,7 @@ import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate;
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryUpdate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetFilterQueryCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
@@ -31,11 +32,14 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.domain.Specifications;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -65,8 +69,10 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
this.distributionSetManagement = distributionSetManagement;
}
@Transactional
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetFilterQuery createTargetFilterQuery(final TargetFilterQueryCreate c) {
final JpaTargetFilterQueryCreate create = (JpaTargetFilterQueryCreate) c;
@@ -75,6 +81,8 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteTargetFilterQuery(final Long targetFilterQueryId) {
findTargetFilterQueryById(targetFilterQueryId)
.orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, targetFilterQueryId));

View File

@@ -36,6 +36,7 @@ import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetCreate;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetUpdate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
@@ -55,12 +56,15 @@ import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.SliceImpl;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -149,6 +153,8 @@ public class JpaTargetManagement implements TargetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target updateTarget(final TargetUpdate u) {
final JpaTargetUpdate update = (JpaTargetUpdate) u;
@@ -165,6 +171,8 @@ public class JpaTargetManagement implements TargetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteTargets(final Collection<Long> targetIDs) {
final List<JpaTarget> targets = targetRepository.findAll(targetIDs);
@@ -183,6 +191,8 @@ public class JpaTargetManagement implements TargetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteTarget(final String controllerID) {
final Target target = targetRepository.findByControllerId(controllerID)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerID));
@@ -322,6 +332,8 @@ public class JpaTargetManagement implements TargetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetTagAssignmentResult toggleTagAssignment(final Collection<String> controllerIds, final String tagName) {
final TargetTag tag = targetTagRepository.findByNameEquals(tagName)
.orElseThrow(() -> new EntityNotFoundException(TargetTag.class, tagName));
@@ -359,7 +371,10 @@ public class JpaTargetManagement implements TargetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<Target> assignTag(final Collection<String> controllerIds, final Long tagId) {
final List<JpaTarget> allTargets = targetRepository
.findAll(TargetSpecifications.byControllerIdWithTagsInJoin(controllerIds));
@@ -372,12 +387,19 @@ public class JpaTargetManagement implements TargetManagement {
.orElseThrow(() -> new EntityNotFoundException(TargetTag.class, tagId));
allTargets.forEach(target -> target.addTag(tag));
return Collections
final List<Target> result = Collections
.unmodifiableList(allTargets.stream().map(targetRepository::save).collect(Collectors.toList()));
// No reason to save the tag
entityManager.detach(tag);
return result;
}
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target unAssignTag(final String controllerID, final Long targetTagId) {
final JpaTarget target = (JpaTarget) targetRepository.findByControllerId(controllerID)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerID));
@@ -387,7 +409,11 @@ public class JpaTargetManagement implements TargetManagement {
target.removeTag(tag);
return targetRepository.save(target);
final Target result = targetRepository.save(target);
// No reason to save the tag
entityManager.detach(tag);
return result;
}
@Override
@@ -524,6 +550,8 @@ public class JpaTargetManagement implements TargetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target createTarget(final TargetCreate c) {
final JpaTargetCreate create = (JpaTargetCreate) c;
return targetRepository.save(create.build());
@@ -531,6 +559,8 @@ public class JpaTargetManagement implements TargetManagement {
@Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<Target> createTargets(final Collection<TargetCreate> targets) {
return targets.stream().map(this::createTarget).collect(Collectors.toList());
}

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.jpa;
import java.io.Serializable;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaTenantConfiguration;
import org.eclipse.hawkbit.repository.model.TenantConfiguration;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
@@ -23,6 +24,9 @@ import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationContext;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -122,6 +126,8 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
@Override
@CacheEvict(value = "tenantConfiguration", key = "#configurationKeyName")
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public <T extends Serializable> TenantConfigurationValue<T> addOrUpdateConfiguration(
final String configurationKeyName, final T value) {
@@ -160,6 +166,8 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
@Override
@CacheEvict(value = "tenantConfiguration", key = "#configurationKeyName")
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteConfiguration(final String configurationKeyName) {
tenantConfigurationRepository.deleteByKey(configurationKeyName);
}

View File

@@ -19,12 +19,12 @@ import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.PropertiesQuotaManagement;
import org.eclipse.hawkbit.repository.ReportManagement;
import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.PropertiesQuotaManagement;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TagManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
@@ -69,6 +69,7 @@ import org.eclipse.hawkbit.security.SecurityTokenGenerator;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -89,6 +90,7 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.integration.support.locks.LockRegistry;
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@@ -110,6 +112,7 @@ import com.google.common.collect.Maps;
@EnableConfigurationProperties({ RepositoryProperties.class, ControllerPollProperties.class,
TenantConfigurationProperties.class })
@EnableScheduling
@EnableRetry
@EntityScan("org.eclipse.hawkbit.repository.jpa.model")
public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
@@ -283,17 +286,22 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
@Override
protected Map<String, Object> getVendorProperties() {
final Map<String, Object> properties = Maps.newHashMapWithExpectedSize(5);
final Map<String, Object> properties = Maps.newHashMapWithExpectedSize(7);
// Turn off dynamic weaving to disable LTW lookup in static weaving mode
properties.put("eclipselink.weaving", "false");
properties.put(PersistenceUnitProperties.WEAVING, "false");
// needed for reports
properties.put("eclipselink.jdbc.allow-native-sql-queries", "true");
properties.put(PersistenceUnitProperties.ALLOW_NATIVE_SQL_QUERIES, "true");
// flyway
properties.put("eclipselink.ddl-generation", "none");
properties.put(PersistenceUnitProperties.DDL_GENERATION, "none");
// Embeed into hawkBit logging
properties.put("eclipselink.logging.logger", "JavaLogger");
properties.put(PersistenceUnitProperties.LOGGING_LOGGER, "JavaLogger");
// Ensure that we flush only at the end of the transaction
properties.put("eclipselink.persistence-context.flush-mode", "COMMIT");
properties.put(PersistenceUnitProperties.PERSISTENCE_CONTEXT_FLUSH_MODE, "COMMIT");
// Enable batch writing
properties.put(PersistenceUnitProperties.BATCH_WRITING, "JDBC");
// Batch size
properties.put(PersistenceUnitProperties.BATCH_WRITING_SIZE, "500");
return properties;
}

View File

@@ -53,9 +53,9 @@ public interface RolloutRepository
/**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety
* reasons (this is a "delete everything" query after all) we add the tenant manually to
* query even if this will by done by {@link EntityManager} anyhow. The DB
* should take care of optimizing this away.
* reasons (this is a "delete everything" query after all) we add the tenant
* manually to query even if this will by done by {@link EntityManager}
* anyhow. The DB should take care of optimizing this away.
*
* @param tenant
* to delete data from

View File

@@ -113,9 +113,9 @@ public interface SoftwareModuleRepository
/**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety
* reasons (this is a "delete everything" query after all) we add the tenant manually to
* query even if this will by done by {@link EntityManager} anyhow. The DB
* should take care of optimizing this away.
* reasons (this is a "delete everything" query after all) we add the tenant
* manually to query even if this will by done by {@link EntityManager}
* anyhow. The DB should take care of optimizing this away.
*
* @param tenant
* to delete data from

View File

@@ -82,9 +82,9 @@ public interface SoftwareModuleTypeRepository
/**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety
* reasons (this is a "delete everything" query after all) we add the tenant manually to
* query even if this will by done by {@link EntityManager} anyhow. The DB
* should take care of optimizing this away.
* reasons (this is a "delete everything" query after all) we add the tenant
* manually to query even if this will by done by {@link EntityManager}
* anyhow. The DB should take care of optimizing this away.
*
* @param tenant
* to delete data from

View File

@@ -57,9 +57,9 @@ public interface TargetFilterQueryRepository
/**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety
* reasons (this is a "delete everything" query after all) we add the tenant manually to
* query even if this will by done by {@link EntityManager} anyhow. The DB
* should take care of optimizing this away.
* reasons (this is a "delete everything" query after all) we add the tenant
* manually to query even if this will by done by {@link EntityManager}
* anyhow. The DB should take care of optimizing this away.
*
* @param tenant
* to delete data from

View File

@@ -217,9 +217,9 @@ public interface TargetRepository extends BaseEntityRepository<JpaTarget, Long>,
/**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety
* reasons (this is a "delete everything" query after all) we add the tenant manually to
* query even if this will by done by {@link EntityManager} anyhow. The DB
* should take care of optimizing this away.
* reasons (this is a "delete everything" query after all) we add the tenant
* manually to query even if this will by done by {@link EntityManager}
* anyhow. The DB should take care of optimizing this away.
*
* @param tenant
* to delete data from

View File

@@ -70,9 +70,9 @@ public interface TargetTagRepository
/**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety
* reasons (this is a "delete everything" query after all) we add the tenant manually to
* query even if this will by done by {@link EntityManager} anyhow. The DB
* should take care of optimizing this away.
* reasons (this is a "delete everything" query after all) we add the tenant
* manually to query even if this will by done by {@link EntityManager}
* anyhow. The DB should take care of optimizing this away.
*
* @param tenant
* to delete data from

View File

@@ -46,9 +46,9 @@ public interface TenantConfigurationRepository extends BaseEntityRepository<JpaT
/**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety
* reasons (this is a "delete everything" query after all) we add the tenant manually to
* query even if this will by done by {@link EntityManager} anyhow. The DB
* should take care of optimizing this away.
* reasons (this is a "delete everything" query after all) we add the tenant
* manually to query even if this will by done by {@link EntityManager}
* anyhow. The DB should take care of optimizing this away.
*
* @param tenant
* to delete data from

View File

@@ -8,6 +8,9 @@
*/
package org.eclipse.hawkbit.repository.jpa.configuration;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
/**
* A constant class which holds only static constants used within the SP server.
*
@@ -23,6 +26,16 @@ public final class Constants {
*/
public static final int MAX_ENTRIES_IN_STATEMENT = 999;
/**
* @see Retryable#maxAttempts()
*/
public static final int TX_RT_MAX = 10;
/**
* @see Backoff#delay()
*/
public static final long TX_RT_DELAY = 100;
/**
* Constant class only private constructor.
*/