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 * 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 * reasons (this is a "delete everything" query after all) we add the tenant
* query even if this will by done by {@link EntityManager} anyhow. The DB * manually to query even if this will by done by {@link EntityManager}
* should take care of optimizing this away. * anyhow. The DB should take care of optimizing this away.
* *
* @param tenant * @param tenant
* to delete data from * to delete data from

View File

@@ -64,9 +64,9 @@ public interface DistributionSetTypeRepository
/** /**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety * 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 * reasons (this is a "delete everything" query after all) we add the tenant
* query even if this will by done by {@link EntityManager} anyhow. The DB * manually to query even if this will by done by {@link EntityManager}
* should take care of optimizing this away. * anyhow. The DB should take care of optimizing this away.
* *
* @param tenant * @param tenant
* to delete data from * 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.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InvalidMD5HashException; import org.eclipse.hawkbit.repository.exception.InvalidMD5HashException;
import org.eclipse.hawkbit.repository.exception.InvalidSHA1HashException; 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.JpaArtifact;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.model.Artifact; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; 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.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -71,6 +75,8 @@ public class JpaArtifactManagement implements ArtifactManagement {
@Override @Override
@Transactional @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, public Artifact createArtifact(final InputStream stream, final Long moduleId, final String filename,
final String providedMd5Sum, final String providedSha1Sum, final boolean overrideExisting, final String providedMd5Sum, final String providedSha1Sum, final boolean overrideExisting,
final String contentType) { final String contentType) {
@@ -101,6 +107,8 @@ public class JpaArtifactManagement implements ArtifactManagement {
@Override @Override
@Transactional @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) { public boolean clearArtifactBinary(final String sha1Hash, final Long moduleId) {
if (localArtifactRepository.existsWithSha1HashAndSoftwareModuleIdIsNot(sha1Hash, moduleId)) { if (localArtifactRepository.existsWithSha1HashAndSoftwareModuleIdIsNot(sha1Hash, moduleId)) {
@@ -119,6 +127,8 @@ public class JpaArtifactManagement implements ArtifactManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteArtifact(final Long id) { public void deleteArtifact(final Long id) {
final JpaArtifact existing = (JpaArtifact) findArtifact(id) final JpaArtifact existing = (JpaArtifact) findArtifact(id)
.orElseThrow(() -> new EntityNotFoundException(Artifact.class, id)); .orElseThrow(() -> new EntityNotFoundException(Artifact.class, id));
@@ -186,6 +196,8 @@ public class JpaArtifactManagement implements ArtifactManagement {
@Override @Override
@Transactional @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, public Artifact createArtifact(final InputStream inputStream, final Long moduleId, final String filename,
final boolean overrideExisting) { final boolean overrideExisting) {
return createArtifact(inputStream, moduleId, filename, null, null, overrideExisting, null); 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.ToManyAttributeEntriesException;
import org.eclipse.hawkbit.repository.exception.TooManyStatusEntriesException; import org.eclipse.hawkbit.repository.exception.TooManyStatusEntriesException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaActionStatusCreate; 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.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus; 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.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification; 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.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -126,6 +130,8 @@ public class JpaControllerManagement implements ControllerManagement {
@Override @Override
@Transactional @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) { public Target updateLastTargetQuery(final String controllerId, final URI address) {
final JpaTarget target = (JpaTarget) targetRepository.findByControllerId(controllerId) final JpaTarget target = (JpaTarget) targetRepository.findByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId)); .orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
@@ -195,6 +201,8 @@ public class JpaControllerManagement implements ControllerManagement {
@Override @Override
@Transactional @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) { public Target findOrRegisterTargetIfItDoesNotexist(final String controllerId, final URI address) {
final Specification<JpaTarget> spec = (targetRoot, query, cb) -> cb final Specification<JpaTarget> spec = (targetRoot, query, cb) -> cb
.equal(targetRoot.get(JpaTarget_.controllerId), controllerId); .equal(targetRoot.get(JpaTarget_.controllerId), controllerId);
@@ -240,6 +248,8 @@ public class JpaControllerManagement implements ControllerManagement {
@Override @Override
@Transactional(isolation = Isolation.READ_COMMITTED) @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) { public Action addCancelActionStatus(final ActionStatusCreate c) {
final JpaActionStatusCreate create = (JpaActionStatusCreate) c; final JpaActionStatusCreate create = (JpaActionStatusCreate) c;
@@ -283,6 +293,8 @@ public class JpaControllerManagement implements ControllerManagement {
@Override @Override
@Transactional(isolation = Isolation.READ_COMMITTED) @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) { public Action addUpdateActionStatus(final ActionStatusCreate c) {
final JpaActionStatusCreate create = (JpaActionStatusCreate) c; final JpaActionStatusCreate create = (JpaActionStatusCreate) c;
final JpaAction action = getActionAndThrowExceptionIfNotFound(create.getActionId()); final JpaAction action = getActionAndThrowExceptionIfNotFound(create.getActionId());
@@ -381,6 +393,8 @@ public class JpaControllerManagement implements ControllerManagement {
@Override @Override
@Transactional @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) { public Target updateControllerAttributes(final String controllerId, final Map<String, String> data) {
final JpaTarget target = (JpaTarget) targetRepository.findByControllerId(controllerId) final JpaTarget target = (JpaTarget) targetRepository.findByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId)); .orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
@@ -401,6 +415,8 @@ public class JpaControllerManagement implements ControllerManagement {
@Override @Override
@Transactional @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) { public Action registerRetrieved(final Long actionId, final String message) {
return handleRegisterRetrieved(actionId, message); return handleRegisterRetrieved(actionId, message);
} }
@@ -460,6 +476,8 @@ public class JpaControllerManagement implements ControllerManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public ActionStatus addInformationalActionStatus(final ActionStatusCreate c) { public ActionStatus addInformationalActionStatus(final ActionStatusCreate c) {
final JpaActionStatusCreate create = (JpaActionStatusCreate) c; final JpaActionStatusCreate create = (JpaActionStatusCreate) c;
final JpaAction action = getActionAndThrowExceptionIfNotFound(create.getActionId()); 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.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.AuditorAware; import org.springframework.data.domain.AuditorAware;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl; 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.Pageable;
import org.springframework.data.domain.Slice; import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification; 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.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Isolation;
@@ -136,6 +139,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
// Exception squid:S2095: see // Exception squid:S2095: see
// https://jira.sonarsource.com/browse/SONARJAVA-1478 // https://jira.sonarsource.com/browse/SONARJAVA-1478
@SuppressWarnings({ "squid:S2095" }) @SuppressWarnings({ "squid:S2095" })
@@ -147,6 +152,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@Override @Override
@Transactional(isolation = Isolation.READ_COMMITTED) @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, public DistributionSetAssignmentResult assignDistributionSet(final Long dsID,
final Collection<TargetWithActionType> targets) { final Collection<TargetWithActionType> targets) {
return assignDistributionSet(dsID, targets, null); return assignDistributionSet(dsID, targets, null);
@@ -154,6 +161,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@Override @Override
@Transactional(isolation = Isolation.READ_COMMITTED) @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, public DistributionSetAssignmentResult assignDistributionSet(final Long dsID,
final Collection<TargetWithActionType> targets, final String actionMessage) { final Collection<TargetWithActionType> targets, final String actionMessage) {
final JpaDistributionSet set = distributionSetRepository.findOne(dsID); final JpaDistributionSet set = distributionSetRepository.findOne(dsID);
@@ -348,6 +357,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@Override @Override
@Transactional(isolation = Isolation.READ_COMMITTED) @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) { public Action cancelAction(final Long actionId) {
LOG.debug("cancelAction({})", actionId); LOG.debug("cancelAction({})", actionId);
@@ -390,6 +401,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@Override @Override
@Transactional(isolation = Isolation.READ_COMMITTED) @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) { public Action forceQuitAction(final Long actionId) {
final JpaAction action = actionRepository.findById(actionId) final JpaAction action = actionRepository.findById(actionId)
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId)); .orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
@@ -595,6 +608,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Action forceTargetAction(final Long actionId) { public Action forceTargetAction(final Long actionId) {
final JpaAction action = actionRepository.findById(actionId) final JpaAction action = actionRepository.findById(actionId)
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId)); .orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));

View File

@@ -9,7 +9,6 @@
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; 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.exception.EntityReadOnlyException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetCreate; import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetCreate;
import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetTypeCreate; 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.DsMetadataCompositeKey;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata; 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.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification; 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.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -140,6 +143,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override @Override
@Transactional @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) { public DistributionSetTagAssignmentResult toggleTagAssignment(final Collection<Long> dsIds, final String tagName) {
final List<JpaDistributionSet> sets = findDistributionSetListWithDetails(dsIds); final List<JpaDistributionSet> sets = findDistributionSetListWithDetails(dsIds);
@@ -187,6 +192,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSet updateDistributionSet(final DistributionSetUpdate u) { public DistributionSet updateDistributionSet(final DistributionSetUpdate u) {
final GenericDistributionSetUpdate update = (GenericDistributionSetUpdate) u; final GenericDistributionSetUpdate update = (GenericDistributionSetUpdate) u;
@@ -237,6 +244,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteDistributionSet(final Collection<Long> distributionSetIDs) { public void deleteDistributionSet(final Collection<Long> distributionSetIDs) {
final List<DistributionSet> setsFound = findDistributionSetsById(distributionSetIDs); final List<DistributionSet> setsFound = findDistributionSetsById(distributionSetIDs);
@@ -274,6 +283,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSet createDistributionSet(final DistributionSetCreate c) { public DistributionSet createDistributionSet(final DistributionSetCreate c) {
final JpaDistributionSetCreate create = (JpaDistributionSetCreate) c; final JpaDistributionSetCreate create = (JpaDistributionSetCreate) c;
if (create.getType() == null) { if (create.getType() == null) {
@@ -285,12 +296,16 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override @Override
@Transactional @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) { public List<DistributionSet> createDistributionSets(final Collection<DistributionSetCreate> creates) {
return creates.stream().map(this::createDistributionSet).collect(Collectors.toList()); return creates.stream().map(this::createDistributionSet).collect(Collectors.toList());
} }
@Override @Override
@Transactional @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) { public DistributionSet assignSoftwareModules(final Long setId, final Collection<Long> moduleIds) {
final Collection<JpaSoftwareModule> modules = softwareModuleRepository.findByIdIn(moduleIds); final Collection<JpaSoftwareModule> modules = softwareModuleRepository.findByIdIn(moduleIds);
@@ -310,6 +325,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override @Override
@Transactional @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) { public DistributionSet unassignSoftwareModule(final Long setId, final Long moduleId) {
final JpaDistributionSet set = findDistributionSetAndThrowExceptionIfNotFound(setId); final JpaDistributionSet set = findDistributionSetAndThrowExceptionIfNotFound(setId);
final JpaSoftwareModule module = findSoftwareModuleAndThrowExceptionIfNotFound(moduleId); final JpaSoftwareModule module = findSoftwareModuleAndThrowExceptionIfNotFound(moduleId);
@@ -323,6 +340,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetType updateDistributionSetType(final DistributionSetTypeUpdate u) { public DistributionSetType updateDistributionSetType(final DistributionSetTypeUpdate u) {
final GenericDistributionSetTypeUpdate update = (GenericDistributionSetTypeUpdate) u; final GenericDistributionSetTypeUpdate update = (GenericDistributionSetTypeUpdate) u;
@@ -349,6 +368,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetType assignMandatorySoftwareModuleTypes(final Long dsTypeId, public DistributionSetType assignMandatorySoftwareModuleTypes(final Long dsTypeId,
final Collection<Long> softwareModulesTypeIds) { final Collection<Long> softwareModulesTypeIds) {
final Collection<JpaSoftwareModuleType> modules = softwareModuleTypeRepository final Collection<JpaSoftwareModuleType> modules = softwareModuleTypeRepository
@@ -369,6 +390,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetType assignOptionalSoftwareModuleTypes(final Long dsTypeId, public DistributionSetType assignOptionalSoftwareModuleTypes(final Long dsTypeId,
final Collection<Long> softwareModulesTypeIds) { final Collection<Long> softwareModulesTypeIds) {
@@ -397,6 +420,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override @Override
@Transactional @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) { public DistributionSetType unassignSoftwareModuleType(final Long dsTypeId, final Long softwareModuleTypeId) {
final JpaDistributionSetType type = findDistributionSetTypeAndThrowExceptionIfNotFound(dsTypeId); final JpaDistributionSetType type = findDistributionSetTypeAndThrowExceptionIfNotFound(dsTypeId);
@@ -581,6 +606,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetType createDistributionSetType(final DistributionSetTypeCreate c) { public DistributionSetType createDistributionSetType(final DistributionSetTypeCreate c) {
final JpaDistributionSetTypeCreate create = (JpaDistributionSetTypeCreate) c; final JpaDistributionSetTypeCreate create = (JpaDistributionSetTypeCreate) c;
@@ -589,6 +616,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteDistributionSetType(final Long typeId) { public void deleteDistributionSetType(final Long typeId) {
final JpaDistributionSetType toDelete = distributionSetTypeRepository.findById(typeId) final JpaDistributionSetType toDelete = distributionSetTypeRepository.findById(typeId)
@@ -604,6 +633,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override @Override
@Transactional @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) { public List<DistributionSetMetadata> createDistributionSetMetadata(final Long dsId, final Collection<MetaData> md) {
md.forEach(meta -> checkAndThrowAlreadyIfDistributionSetMetadataExists( md.forEach(meta -> checkAndThrowAlreadyIfDistributionSetMetadataExists(
@@ -619,6 +650,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override @Override
@Transactional @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) { public DistributionSetMetadata updateDistributionSetMetadata(final Long dsId, final MetaData md) {
// check if exists otherwise throw entity not found exception // check if exists otherwise throw entity not found exception
@@ -634,6 +667,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override @Override
@Transactional @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) { public void deleteDistributionSetMetadata(final Long distributionSetId, final String key) {
final JpaDistributionSetMetadata metadata = (JpaDistributionSetMetadata) findDistributionSetMetadata( final JpaDistributionSetMetadata metadata = (JpaDistributionSetMetadata) findDistributionSetMetadata(
distributionSetId, key).orElseThrow( distributionSetId, key).orElseThrow(
@@ -818,6 +853,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override @Override
@Transactional @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) { public List<DistributionSet> assignTag(final Collection<Long> dsIds, final Long dsTagId) {
final List<JpaDistributionSet> allDs = findDistributionSetListWithDetails(dsIds); final List<JpaDistributionSet> allDs = findDistributionSetListWithDetails(dsIds);
@@ -831,41 +868,46 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
allDs.forEach(ds -> ds.addTag(distributionSetTag)); allDs.forEach(ds -> ds.addTag(distributionSetTag));
return Collections final List<DistributionSet> result = Collections
.unmodifiableList(allDs.stream().map(distributionSetRepository::save).collect(Collectors.toList())); .unmodifiableList(allDs.stream().map(distributionSetRepository::save).collect(Collectors.toList()));
// No reason to save the tag
entityManager.detach(distributionSetTag);
return result;
} }
@Override @Override
@Transactional @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) { public DistributionSet unAssignTag(final Long dsId, final Long dsTagId) {
final List<JpaDistributionSet> allDs = findDistributionSetListWithDetails(Arrays.asList(dsId)); final JpaDistributionSet set = (JpaDistributionSet) findDistributionSetByIdWithDetails(dsId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, dsId));
if (allDs.isEmpty()) {
throw new EntityNotFoundException(DistributionSet.class, dsId);
}
final DistributionSetTag distributionSetTag = tagManagement.findDistributionSetTagById(dsTagId) final DistributionSetTag distributionSetTag = tagManagement.findDistributionSetTagById(dsTagId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSetTag.class, 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, set.removeTag(distributionSetTag);
final DistributionSetTag tag) {
distributionSets.forEach(ds -> ds.removeTag(tag)); final JpaDistributionSet result = distributionSetRepository.save(set);
return Collections.unmodifiableList(
distributionSets.stream().map(distributionSetRepository::save).collect(Collectors.toList())); // No reason to save the tag
entityManager.detach(distributionSetTag);
return result;
} }
@Override @Override
@Transactional @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) { public List<DistributionSetType> createDistributionSetTypes(final Collection<DistributionSetTypeCreate> types) {
return types.stream().map(this::createDistributionSetType).collect(Collectors.toList()); return types.stream().map(this::createDistributionSetType).collect(Collectors.toList());
} }
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteDistributionSet(final Long setId) { public void deleteDistributionSet(final Long setId) {
throwExceptionIfDistributionSetDoesNotExist(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.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException; import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException; 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.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout; 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.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable; 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.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
import org.springframework.integration.support.locks.LockRegistry; 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.Async;
import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
@@ -179,6 +183,8 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
@Override @Override
@Transactional @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, public Rollout createRollout(final RolloutCreate rollout, final int amountGroup,
final RolloutGroupConditions conditions) { final RolloutGroupConditions conditions) {
RolloutHelper.verifyRolloutGroupParameter(amountGroup, quotaManagement); RolloutHelper.verifyRolloutGroupParameter(amountGroup, quotaManagement);
@@ -188,6 +194,8 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
@Override @Override
@Transactional @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, public Rollout createRollout(final RolloutCreate rollout, final List<RolloutGroupCreate> groups,
final RolloutGroupConditions conditions) { final RolloutGroupConditions conditions) {
RolloutHelper.verifyRolloutGroupParameter(groups.size(), quotaManagement); RolloutHelper.verifyRolloutGroupParameter(groups.size(), quotaManagement);
@@ -422,6 +430,8 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Rollout startRollout(final Long rolloutId) { public Rollout startRollout(final Long rolloutId) {
LOGGER.debug("startRollout called for rollout {}", rolloutId); LOGGER.debug("startRollout called for rollout {}", rolloutId);
@@ -552,6 +562,8 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void pauseRollout(final Long rolloutId) { public void pauseRollout(final Long rolloutId) {
final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId); final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId);
if (!RolloutStatus.RUNNING.equals(rollout.getStatus())) { if (!RolloutStatus.RUNNING.equals(rollout.getStatus())) {
@@ -569,6 +581,8 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void resumeRollout(final Long rolloutId) { public void resumeRollout(final Long rolloutId) {
final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId); final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId);
if (!(RolloutStatus.PAUSED.equals(rollout.getStatus()))) { if (!(RolloutStatus.PAUSED.equals(rollout.getStatus()))) {
@@ -795,6 +809,8 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteRollout(final long rolloutId) { public void deleteRollout(final long rolloutId) {
final JpaRollout jpaRollout = rolloutRepository.findOne(rolloutId); final JpaRollout jpaRollout = rolloutRepository.findOne(rolloutId);
@@ -903,6 +919,8 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Rollout updateRollout(final RolloutUpdate u) { public Rollout updateRollout(final RolloutUpdate u) {
final GenericRolloutUpdate update = (GenericRolloutUpdate) u; final GenericRolloutUpdate update = (GenericRolloutUpdate) u;
final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(update.getId()); 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.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleCreate; import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleCreate;
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleTypeCreate; 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.JpaDistributionSet_; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; 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.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer; import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.AuditorAware; import org.springframework.data.domain.AuditorAware;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl; 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.Slice;
import org.springframework.data.domain.SliceImpl; import org.springframework.data.domain.SliceImpl;
import org.springframework.data.jpa.domain.Specification; 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.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -117,6 +121,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public SoftwareModule updateSoftwareModule(final SoftwareModuleUpdate u) { public SoftwareModule updateSoftwareModule(final SoftwareModuleUpdate u) {
final GenericSoftwareModuleUpdate update = (GenericSoftwareModuleUpdate) u; final GenericSoftwareModuleUpdate update = (GenericSoftwareModuleUpdate) u;
@@ -131,6 +137,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public SoftwareModuleType updateSoftwareModuleType(final SoftwareModuleTypeUpdate u) { public SoftwareModuleType updateSoftwareModuleType(final SoftwareModuleTypeUpdate u) {
final GenericSoftwareModuleTypeUpdate update = (GenericSoftwareModuleTypeUpdate) u; final GenericSoftwareModuleTypeUpdate update = (GenericSoftwareModuleTypeUpdate) u;
@@ -145,6 +153,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public SoftwareModule createSoftwareModule(final SoftwareModuleCreate c) { public SoftwareModule createSoftwareModule(final SoftwareModuleCreate c) {
final JpaSoftwareModuleCreate create = (JpaSoftwareModuleCreate) c; final JpaSoftwareModuleCreate create = (JpaSoftwareModuleCreate) c;
@@ -153,6 +163,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override @Override
@Transactional @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) { public List<SoftwareModule> createSoftwareModule(final Collection<SoftwareModuleCreate> swModules) {
return swModules.stream().map(this::createSoftwareModule).collect(Collectors.toList()); return swModules.stream().map(this::createSoftwareModule).collect(Collectors.toList());
} }
@@ -225,6 +237,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteSoftwareModules(final Collection<Long> ids) { public void deleteSoftwareModules(final Collection<Long> ids) {
final List<JpaSoftwareModule> swModulesToDelete = softwareModuleRepository.findByIdIn(ids); final List<JpaSoftwareModule> swModulesToDelete = softwareModuleRepository.findByIdIn(ids);
@@ -483,6 +497,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public SoftwareModuleType createSoftwareModuleType(final SoftwareModuleTypeCreate c) { public SoftwareModuleType createSoftwareModuleType(final SoftwareModuleTypeCreate c) {
final JpaSoftwareModuleTypeCreate create = (JpaSoftwareModuleTypeCreate) c; final JpaSoftwareModuleTypeCreate create = (JpaSoftwareModuleTypeCreate) c;
@@ -491,6 +507,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteSoftwareModuleType(final Long typeId) { public void deleteSoftwareModuleType(final Long typeId) {
final JpaSoftwareModuleType toDelete = softwareModuleTypeRepository.findById(typeId) final JpaSoftwareModuleType toDelete = softwareModuleTypeRepository.findById(typeId)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModuleType.class, typeId)); .orElseThrow(() -> new EntityNotFoundException(SoftwareModuleType.class, typeId));
@@ -515,6 +533,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override @Override
@Transactional @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) { public SoftwareModuleMetadata createSoftwareModuleMetadata(final Long moduleId, final MetaData md) {
checkAndThrowAlreadyIfSoftwareModuleMetadataExists(moduleId, md); checkAndThrowAlreadyIfSoftwareModuleMetadataExists(moduleId, md);
@@ -531,6 +551,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<SoftwareModuleMetadata> createSoftwareModuleMetadata(final Long moduleId, public List<SoftwareModuleMetadata> createSoftwareModuleMetadata(final Long moduleId,
final Collection<MetaData> md) { final Collection<MetaData> md) {
md.forEach(meta -> checkAndThrowAlreadyIfSoftwareModuleMetadataExists(moduleId, meta)); md.forEach(meta -> checkAndThrowAlreadyIfSoftwareModuleMetadataExists(moduleId, meta));
@@ -545,6 +567,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override @Override
@Transactional @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) { public SoftwareModuleMetadata updateSoftwareModuleMetadata(final Long moduleId, final MetaData md) {
// check if exists otherwise throw entity not found exception // check if exists otherwise throw entity not found exception
@@ -589,6 +613,8 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override @Override
@Transactional @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) { public void deleteSoftwareModuleMetadata(final Long moduleId, final String key) {
final JpaSoftwareModuleMetadata metadata = (JpaSoftwareModuleMetadata) findSoftwareModuleMetadata(moduleId, key) final JpaSoftwareModuleMetadata metadata = (JpaSoftwareModuleMetadata) findSoftwareModuleMetadata(moduleId, key)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModuleMetadata.class, moduleId, key)); .orElseThrow(() -> new EntityNotFoundException(SoftwareModuleMetadata.class, moduleId, key));
@@ -653,12 +679,16 @@ public class JpaSoftwareManagement implements SoftwareManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteSoftwareModule(final Long moduleId) { public void deleteSoftwareModule(final Long moduleId) {
deleteSoftwareModules(Sets.newHashSet(moduleId)); deleteSoftwareModules(Sets.newHashSet(moduleId));
} }
@Override @Override
@Transactional @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) { public List<SoftwareModuleType> createSoftwareModuleType(final Collection<SoftwareModuleTypeCreate> creates) {
return creates.stream().map(this::createSoftwareModuleType).collect(Collectors.toList()); 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 javax.persistence.EntityManager;
import org.eclipse.hawkbit.cache.TenancyCacheManager; import org.eclipse.hawkbit.cache.TenancyCacheManager;
import org.eclipse.hawkbit.repository.Constants;
import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TenantStatsManagement; 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.configuration.MultiTenantJpaTransactionManager;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType; 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.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable; 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.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
@@ -211,6 +214,8 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteTenant(final String t) { public void deleteTenant(final String t) {
final String tenant = t.toUpperCase(); final String tenant = t.toUpperCase();
cacheManager.evictCaches(tenant); cacheManager.evictCaches(tenant);
@@ -254,6 +259,8 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TenantMetaData updateTenantMetadata(final Long defaultDsType) { public TenantMetaData updateTenantMetadata(final Long defaultDsType) {
final JpaTenantMetaData data = (JpaTenantMetaData) getTenantMetadata(); final JpaTenantMetaData data = (JpaTenantMetaData) getTenantMetadata();
@@ -264,20 +271,26 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
private DistributionSetType createStandardSoftwareDataSetup() { private DistributionSetType createStandardSoftwareDataSetup() {
final SoftwareModuleType app = softwareModuleTypeRepository final SoftwareModuleType app = softwareModuleTypeRepository
.save(new JpaSoftwareModuleType(Constants.SMT_DEFAULT_APP_KEY, Constants.SMT_DEFAULT_APP_NAME, .save(new JpaSoftwareModuleType(org.eclipse.hawkbit.repository.Constants.SMT_DEFAULT_APP_KEY,
"Application Addons", Integer.MAX_VALUE)); org.eclipse.hawkbit.repository.Constants.SMT_DEFAULT_APP_NAME, "Application Addons",
Integer.MAX_VALUE));
final SoftwareModuleType os = softwareModuleTypeRepository.save(new JpaSoftwareModuleType( 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 // make sure the module types get their IDs
entityManager.flush(); entityManager.flush();
distributionSetTypeRepository.save(new JpaDistributionSetType(Constants.DST_DEFAULT_OS_ONLY_KEY, distributionSetTypeRepository
Constants.DST_DEFAULT_OS_ONLY_NAME, "Default type with Firmware/OS only.").addMandatoryModuleType(os)); .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, return distributionSetTypeRepository
Constants.DST_DEFAULT_OS_WITH_APPS_NAME, "Default type with Firmware/OS and optional app(s).") .save(new JpaDistributionSetType(org.eclipse.hawkbit.repository.Constants.DST_DEFAULT_OS_WITH_APPS_KEY,
.addMandatoryModuleType(os).addOptionalModuleType(app)); 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 @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.builder.TagUpdate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTagCreate; 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.JpaDistributionSetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; 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.model.TargetTag;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer; import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification; 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.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -68,6 +72,8 @@ public class JpaTagManagement implements TagManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetTag createTargetTag(final TagCreate c) { public TargetTag createTargetTag(final TagCreate c) {
final JpaTagCreate create = (JpaTagCreate) c; final JpaTagCreate create = (JpaTagCreate) c;
@@ -76,6 +82,8 @@ public class JpaTagManagement implements TagManagement {
@Override @Override
@Transactional @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) { public List<TargetTag> createTargetTags(final Collection<TagCreate> tt) {
@SuppressWarnings({ "unchecked", "rawtypes" }) @SuppressWarnings({ "unchecked", "rawtypes" })
final Collection<JpaTagCreate> targetTags = (Collection) tt; final Collection<JpaTagCreate> targetTags = (Collection) tt;
@@ -86,6 +94,8 @@ public class JpaTagManagement implements TagManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteTargetTag(final String targetTagName) { public void deleteTargetTag(final String targetTagName) {
if (!targetTagRepository.existsByName(targetTagName)) { if (!targetTagRepository.existsByName(targetTagName)) {
throw new EntityNotFoundException(TargetTag.class, targetTagName); throw new EntityNotFoundException(TargetTag.class, targetTagName);
@@ -118,6 +128,8 @@ public class JpaTagManagement implements TagManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetTag updateTargetTag(final TagUpdate u) { public TargetTag updateTargetTag(final TagUpdate u) {
final GenericTagUpdate update = (GenericTagUpdate) u; final GenericTagUpdate update = (GenericTagUpdate) u;
@@ -132,8 +144,9 @@ public class JpaTagManagement implements TagManagement {
} }
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetTag updateDistributionSetTag(final TagUpdate u) { public DistributionSetTag updateDistributionSetTag(final TagUpdate u) {
final GenericTagUpdate update = (GenericTagUpdate) u; final GenericTagUpdate update = (GenericTagUpdate) u;
@@ -154,6 +167,8 @@ public class JpaTagManagement implements TagManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetTag createDistributionSetTag(final TagCreate c) { public DistributionSetTag createDistributionSetTag(final TagCreate c) {
final JpaTagCreate create = (JpaTagCreate) c; final JpaTagCreate create = (JpaTagCreate) c;
return distributionSetTagRepository.save(create.buildDistributionSetTag()); return distributionSetTagRepository.save(create.buildDistributionSetTag());
@@ -161,6 +176,8 @@ public class JpaTagManagement implements TagManagement {
@Override @Override
@Transactional @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) { public List<DistributionSetTag> createDistributionSetTags(final Collection<TagCreate> dst) {
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
@@ -173,6 +190,8 @@ public class JpaTagManagement implements TagManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteDistributionSetTag(final String tagName) { public void deleteDistributionSetTag(final String tagName) {
if (!distributionSetTagRepository.existsByName(tagName)) { if (!distributionSetTagRepository.existsByName(tagName)) {
throw new EntityNotFoundException(DistributionSetTag.class, 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.builder.TargetFilterQueryUpdate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetFilterQueryCreate; 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.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; 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.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer; import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.domain.Specifications; 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.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -65,8 +69,10 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
this.distributionSetManagement = distributionSetManagement; this.distributionSetManagement = distributionSetManagement;
} }
@Transactional
@Override @Override
@Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetFilterQuery createTargetFilterQuery(final TargetFilterQueryCreate c) { public TargetFilterQuery createTargetFilterQuery(final TargetFilterQueryCreate c) {
final JpaTargetFilterQueryCreate create = (JpaTargetFilterQueryCreate) c; final JpaTargetFilterQueryCreate create = (JpaTargetFilterQueryCreate) c;
@@ -75,6 +81,8 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteTargetFilterQuery(final Long targetFilterQueryId) { public void deleteTargetFilterQuery(final Long targetFilterQueryId) {
findTargetFilterQueryById(targetFilterQueryId) findTargetFilterQueryById(targetFilterQueryId)
.orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, 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.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetCreate; import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetCreate;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetUpdate; 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.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag; 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.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice; import org.springframework.data.domain.Slice;
import org.springframework.data.domain.SliceImpl; import org.springframework.data.domain.SliceImpl;
import org.springframework.data.jpa.domain.Specification; 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.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -149,6 +153,8 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target updateTarget(final TargetUpdate u) { public Target updateTarget(final TargetUpdate u) {
final JpaTargetUpdate update = (JpaTargetUpdate) u; final JpaTargetUpdate update = (JpaTargetUpdate) u;
@@ -165,6 +171,8 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteTargets(final Collection<Long> targetIDs) { public void deleteTargets(final Collection<Long> targetIDs) {
final List<JpaTarget> targets = targetRepository.findAll(targetIDs); final List<JpaTarget> targets = targetRepository.findAll(targetIDs);
@@ -183,6 +191,8 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteTarget(final String controllerID) { public void deleteTarget(final String controllerID) {
final Target target = targetRepository.findByControllerId(controllerID) final Target target = targetRepository.findByControllerId(controllerID)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerID)); .orElseThrow(() -> new EntityNotFoundException(Target.class, controllerID));
@@ -322,6 +332,8 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
@Transactional @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) { public TargetTagAssignmentResult toggleTagAssignment(final Collection<String> controllerIds, final String tagName) {
final TargetTag tag = targetTagRepository.findByNameEquals(tagName) final TargetTag tag = targetTagRepository.findByNameEquals(tagName)
.orElseThrow(() -> new EntityNotFoundException(TargetTag.class, tagName)); .orElseThrow(() -> new EntityNotFoundException(TargetTag.class, tagName));
@@ -359,7 +371,10 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
@Transactional @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) { public List<Target> assignTag(final Collection<String> controllerIds, final Long tagId) {
final List<JpaTarget> allTargets = targetRepository final List<JpaTarget> allTargets = targetRepository
.findAll(TargetSpecifications.byControllerIdWithTagsInJoin(controllerIds)); .findAll(TargetSpecifications.byControllerIdWithTagsInJoin(controllerIds));
@@ -372,12 +387,19 @@ public class JpaTargetManagement implements TargetManagement {
.orElseThrow(() -> new EntityNotFoundException(TargetTag.class, tagId)); .orElseThrow(() -> new EntityNotFoundException(TargetTag.class, tagId));
allTargets.forEach(target -> target.addTag(tag)); allTargets.forEach(target -> target.addTag(tag));
return Collections
final List<Target> result = Collections
.unmodifiableList(allTargets.stream().map(targetRepository::save).collect(Collectors.toList())); .unmodifiableList(allTargets.stream().map(targetRepository::save).collect(Collectors.toList()));
// No reason to save the tag
entityManager.detach(tag);
return result;
} }
@Override @Override
@Transactional @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) { public Target unAssignTag(final String controllerID, final Long targetTagId) {
final JpaTarget target = (JpaTarget) targetRepository.findByControllerId(controllerID) final JpaTarget target = (JpaTarget) targetRepository.findByControllerId(controllerID)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerID)); .orElseThrow(() -> new EntityNotFoundException(Target.class, controllerID));
@@ -387,7 +409,11 @@ public class JpaTargetManagement implements TargetManagement {
target.removeTag(tag); 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 @Override
@@ -524,6 +550,8 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target createTarget(final TargetCreate c) { public Target createTarget(final TargetCreate c) {
final JpaTargetCreate create = (JpaTargetCreate) c; final JpaTargetCreate create = (JpaTargetCreate) c;
return targetRepository.save(create.build()); return targetRepository.save(create.build());
@@ -531,6 +559,8 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
@Transactional @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) { public List<Target> createTargets(final Collection<TargetCreate> targets) {
return targets.stream().map(this::createTarget).collect(Collectors.toList()); 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 java.io.Serializable;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; 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.jpa.model.JpaTenantConfiguration;
import org.eclipse.hawkbit.repository.model.TenantConfiguration; import org.eclipse.hawkbit.repository.model.TenantConfiguration;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; 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.context.ApplicationContext;
import org.springframework.core.convert.support.ConfigurableConversionService; import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService; 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.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -122,6 +126,8 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
@Override @Override
@CacheEvict(value = "tenantConfiguration", key = "#configurationKeyName") @CacheEvict(value = "tenantConfiguration", key = "#configurationKeyName")
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public <T extends Serializable> TenantConfigurationValue<T> addOrUpdateConfiguration( public <T extends Serializable> TenantConfigurationValue<T> addOrUpdateConfiguration(
final String configurationKeyName, final T value) { final String configurationKeyName, final T value) {
@@ -160,6 +166,8 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
@Override @Override
@CacheEvict(value = "tenantConfiguration", key = "#configurationKeyName") @CacheEvict(value = "tenantConfiguration", key = "#configurationKeyName")
@Transactional @Transactional
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteConfiguration(final String configurationKeyName) { public void deleteConfiguration(final String configurationKeyName) {
tenantConfigurationRepository.deleteByKey(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.DeploymentManagement;
import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.PropertiesQuotaManagement;
import org.eclipse.hawkbit.repository.ReportManagement; import org.eclipse.hawkbit.repository.ReportManagement;
import org.eclipse.hawkbit.repository.RepositoryProperties; import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.RolloutGroupManagement; import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.repository.RolloutManagement; import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.SoftwareManagement; import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.PropertiesQuotaManagement;
import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TagManagement; import org.eclipse.hawkbit.repository.TagManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement; 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.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 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.integration.support.locks.LockRegistry;
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter; import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter; import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.EnableTransactionManagement;
@@ -110,6 +112,7 @@ import com.google.common.collect.Maps;
@EnableConfigurationProperties({ RepositoryProperties.class, ControllerPollProperties.class, @EnableConfigurationProperties({ RepositoryProperties.class, ControllerPollProperties.class,
TenantConfigurationProperties.class }) TenantConfigurationProperties.class })
@EnableScheduling @EnableScheduling
@EnableRetry
@EntityScan("org.eclipse.hawkbit.repository.jpa.model") @EntityScan("org.eclipse.hawkbit.repository.jpa.model")
public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
@@ -283,17 +286,22 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
@Override @Override
protected Map<String, Object> getVendorProperties() { 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 // 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 // needed for reports
properties.put("eclipselink.jdbc.allow-native-sql-queries", "true"); properties.put(PersistenceUnitProperties.ALLOW_NATIVE_SQL_QUERIES, "true");
// flyway // flyway
properties.put("eclipselink.ddl-generation", "none"); properties.put(PersistenceUnitProperties.DDL_GENERATION, "none");
// Embeed into hawkBit logging // 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 // 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; return properties;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,6 +8,9 @@
*/ */
package org.eclipse.hawkbit.repository.jpa.configuration; 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. * 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; 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. * Constant class only private constructor.
*/ */