Remove unlimited collections from the repository API (#496)

* Started to get rid of unlimited collections

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

* Align API usage.

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

* fix compile issues.

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

* Fix tests.

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

* Remove comments

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

* Performance optimizations.

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

* Remove dead code.

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

* Allign method names

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

* Wait until the action update event is processed

Conflicts:
	hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageHandlerServiceIntegrationTest.java

Signed-off-by: SirWayne <dennis.melzer@bosch-si.com>

* Started new tag APIs

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

* Quotas into central interface. Tag tests added. Event names fixed.

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

* Simplified consumer run for every tenant.

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

* remove unused fields.

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

* Alligned beans.

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

* Deprecated client methods for old resources.

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

* Fix new foreach method.

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

* Fix transaction for foreach.

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

* Extended DS creating to handle larger volumes. Fix on Readme.

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

* Fixed simulator bug and cleaned up tests.

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

* Fix in sorting.

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

* Remove configuration processor.

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

* Fix wrong usage of sanitize.

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

* Missing brackets.

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

* Fix README API compatability.

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

* Fix misinterpretation of pessimistic locking exceptions.

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

* Fix stability sentence.

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

* Code cleanup.

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

* Fixed page calculation

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
Kai Zimmermann
2017-05-09 16:40:49 +02:00
committed by GitHub
parent aca87464bd
commit c18e9f515e
199 changed files with 3502 additions and 1607 deletions

View File

@@ -172,7 +172,8 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
*/
@EntityGraph(value = "Action.ds", type = EntityGraphType.LOAD)
@Query("Select a from JpaAction a where a.target.controllerId = :target and a.active= :active order by a.id")
List<Action> findByActiveAndTarget(@Param("target") String target, @Param("active") boolean active);
Page<Action> findByActiveAndTarget(Pageable pageable, @Param("target") String target,
@Param("active") boolean active);
/**
* Switches the status of actions from one specific status into another,

View File

@@ -17,11 +17,12 @@ import javax.persistence.EntityManager;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Tag;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
@@ -38,13 +39,29 @@ public interface DistributionSetRepository
/**
* Finds {@link DistributionSet}s by assigned {@link Tag}.
*
* @param pageable
* paging and sorting information
*
* @param tag
* @param tagId
* to be found
* @return list of found {@link DistributionSet}s
*/
@Query(value = "Select Distinct ds from JpaDistributionSet ds join ds.tags dst where dst = :tag")
List<JpaDistributionSet> findByTag(@Param("tag") final DistributionSetTag tag);
@Query(value = "Select Distinct ds from JpaDistributionSet ds join ds.tags dst where dst.id = :tag")
Page<JpaDistributionSet> findByTag(Pageable pageable, @Param("tag") final Long tagId);
/**
* Finds {@link DistributionSet}s by assigned {@link Tag}.
*
* @param pageable
* paging and sorting information
*
* @param tagId
* to be found
* @return page of found {@link DistributionSet}s
*/
@Query(value = "Select Distinct ds from JpaDistributionSet ds join ds.tags dst where dst.id = :tagId")
Page<JpaDistributionSet> findByTagId(Pageable pageable, @Param("tagId") final Long tagId);
/**
* deletes the {@link DistributionSet}s with the given IDs.
@@ -147,9 +164,9 @@ public interface DistributionSetRepository
/**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety
* reasons (this is a "delete everything" query after all) we add the tenant manually to
* query even if this will by done by {@link EntityManager} anyhow. The DB
* should take care of optimizing this away.
* reasons (this is a "delete everything" query after all) we add the tenant
* manually to query even if this will by done by {@link EntityManager}
* anyhow. The DB should take care of optimizing this away.
*
* @param tenant
* to delete data from

View File

@@ -51,6 +51,16 @@ public interface DistributionSetTagRepository
*/
Optional<DistributionSetTag> findByNameEquals(final String tagName);
/**
* Checks if tag with given name exists.
*
* @param tagName
* to check for
* @return <code>true</code> is tag with given name exists
*/
@Query("SELECT CASE WHEN COUNT(t)>0 THEN 'true' ELSE 'false' END FROM JpaDistributionSetTag t WHERE t.name=:tagName")
boolean existsByName(@Param("tagName") String tagName);
/**
* Returns all instances of the type.
*

View File

@@ -20,6 +20,7 @@ import javax.persistence.criteria.Root;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.RepositoryConstants;
import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.TargetManagement;
@@ -47,7 +48,6 @@ import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
@@ -92,7 +92,7 @@ public class JpaControllerManagement implements ControllerManagement {
private ActionStatusRepository actionStatusRepository;
@Autowired
private HawkbitSecurityProperties securityProperties;
private QuotaManagement quotaManagement;
@Autowired
private RepositoryProperties repositoryProperties;
@@ -313,15 +313,14 @@ public class JpaControllerManagement implements ControllerManagement {
private Action handleAddUpdateActionStatus(final JpaActionStatus actionStatus, final JpaAction action) {
LOG.debug("addUpdateActionStatus for action {}", action.getId());
JpaTarget target = (JpaTarget) action.getTarget();
switch (actionStatus.getStatus()) {
case ERROR:
target = DeploymentHelper.updateTargetInfo(target, TargetUpdateStatus.ERROR, false);
final JpaTarget target = DeploymentHelper.updateTargetInfo((JpaTarget) action.getTarget(),
TargetUpdateStatus.ERROR, false);
handleErrorOnAction(action, target);
break;
case FINISHED:
handleFinishedAndStoreInTargetStatus(target, action);
handleFinishedAndStoreInTargetStatus(action);
break;
default:
// information status entry - check for a potential DOS attack
@@ -332,7 +331,7 @@ public class JpaControllerManagement implements ControllerManagement {
actionStatus.setAction(action);
actionStatusRepository.save(actionStatus);
LOG.debug("addUpdateActionStatus {} for target {} is finished.", action, target.getId());
LOG.debug("addUpdateActionStatus for action {} isfinished.", action.getId());
return actionRepository.save(action);
}
@@ -346,21 +345,21 @@ public class JpaControllerManagement implements ControllerManagement {
}
private void checkForToManyStatusEntries(final JpaAction action) {
if (securityProperties.getDos().getMaxStatusEntriesPerAction() > 0) {
if (quotaManagement.getMaxStatusEntriesPerAction() > 0) {
final Long statusCount = actionStatusRepository.countByAction(action);
if (statusCount >= securityProperties.getDos().getMaxStatusEntriesPerAction()) {
if (statusCount >= quotaManagement.getMaxStatusEntriesPerAction()) {
LOG_DOS.error(
"Potential denial of service (DOS) attack identfied. More status entries in the system than permitted ({})!",
securityProperties.getDos().getMaxStatusEntriesPerAction());
throw new TooManyStatusEntriesException(
String.valueOf(securityProperties.getDos().getMaxStatusEntriesPerAction()));
quotaManagement.getMaxStatusEntriesPerAction());
throw new TooManyStatusEntriesException(String.valueOf(quotaManagement.getMaxStatusEntriesPerAction()));
}
}
}
private void handleFinishedAndStoreInTargetStatus(final JpaTarget target, final JpaAction action) {
private void handleFinishedAndStoreInTargetStatus(final JpaAction action) {
final JpaTarget target = (JpaTarget) action.getTarget();
action.setActive(false);
action.setStatus(Status.FINISHED);
final JpaDistributionSet ds = (JpaDistributionSet) entityManager.merge(action.getDistributionSet());
@@ -388,11 +387,11 @@ public class JpaControllerManagement implements ControllerManagement {
target.getControllerAttributes().putAll(data);
if (target.getControllerAttributes().size() > securityProperties.getDos().getMaxAttributeEntriesPerTarget()) {
if (target.getControllerAttributes().size() > quotaManagement.getMaxAttributeEntriesPerTarget()) {
LOG_DOS.info("Target tries to insert more than the allowed number of entries ({}). DOS attack anticipated!",
securityProperties.getDos().getMaxAttributeEntriesPerTarget());
quotaManagement.getMaxAttributeEntriesPerTarget());
throw new ToManyAttributeEntriesException(
String.valueOf(securityProperties.getDos().getMaxAttributeEntriesPerTarget()));
String.valueOf(quotaManagement.getMaxAttributeEntriesPerTarget()));
}
target.setRequestControllerAttributes(false);

View File

@@ -21,7 +21,6 @@ import java.util.stream.Collectors;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.ListJoin;
import javax.persistence.criteria.Root;
@@ -43,13 +42,10 @@ import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecuto
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus_;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionWithStatusCount;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
@@ -58,7 +54,6 @@ import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.ActionWithStatusCount;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
@@ -538,31 +533,6 @@ public class JpaDeploymentManagement implements DeploymentManagement {
return actionRepository.findByTargetControllerId(pageable, controllerId);
}
@Override
public List<ActionWithStatusCount> findActionsWithStatusCountByTargetOrderByIdDesc(final String controllerId) {
throwExceptionIfTargetDoesNotExist(controllerId);
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<JpaActionWithStatusCount> query = cb.createQuery(JpaActionWithStatusCount.class);
final Root<JpaAction> actionRoot = query.from(JpaAction.class);
final ListJoin<JpaAction, JpaActionStatus> actionStatusJoin = actionRoot.join(JpaAction_.actionStatus,
JoinType.LEFT);
final Join<JpaAction, JpaDistributionSet> actionDsJoin = actionRoot.join(JpaAction_.distributionSet);
final Join<JpaAction, JpaRollout> actionRolloutJoin = actionRoot.join(JpaAction_.rollout, JoinType.LEFT);
final CriteriaQuery<JpaActionWithStatusCount> multiselect = query.distinct(true).multiselect(
actionRoot.get(JpaAction_.id), actionRoot.get(JpaAction_.actionType), actionRoot.get(JpaAction_.active),
actionRoot.get(JpaAction_.forcedTime), actionRoot.get(JpaAction_.status),
actionRoot.get(JpaAction_.createdAt), actionRoot.get(JpaAction_.lastModifiedAt),
actionDsJoin.get(JpaDistributionSet_.id), actionDsJoin.get(JpaDistributionSet_.name),
actionDsJoin.get(JpaDistributionSet_.version), cb.count(actionStatusJoin),
actionRolloutJoin.get(JpaRollout_.name));
multiselect.where(cb.equal(actionRoot.get(JpaAction_.target).get(JpaTarget_.controllerId), controllerId));
multiselect.orderBy(cb.desc(actionRoot.get(JpaAction_.id)));
multiselect.groupBy(actionRoot.get(JpaAction_.id));
return Collections.unmodifiableList(entityManager.createQuery(multiselect).getResultList());
}
@Override
public Page<Action> findActionsByTarget(final String rsqlParam, final String controllerId,
final Pageable pageable) {
@@ -584,17 +554,17 @@ public class JpaDeploymentManagement implements DeploymentManagement {
}
@Override
public List<Action> findActiveActionsByTarget(final String controllerId) {
public Page<Action> findActiveActionsByTarget(final Pageable pageable, final String controllerId) {
throwExceptionIfTargetDoesNotExist(controllerId);
return actionRepository.findByActiveAndTarget(controllerId, true);
return actionRepository.findByActiveAndTarget(pageable, controllerId, true);
}
@Override
public List<Action> findInActiveActionsByTarget(final String controllerId) {
public Page<Action> findInActiveActionsByTarget(final Pageable pageable, final String controllerId) {
throwExceptionIfTargetDoesNotExist(controllerId);
return actionRepository.findByActiveAndTarget(controllerId, false);
return actionRepository.findByActiveAndTarget(pageable, controllerId, false);
}
@Override
@@ -645,15 +615,6 @@ public class JpaDeploymentManagement implements DeploymentManagement {
return actionStatusRepository.findByActionId(pageReq, actionId);
}
@Override
public Page<ActionStatus> findActionStatusByActionWithMessages(final Pageable pageReq, final Long actionId) {
if (!actionRepository.exists(actionId)) {
throw new EntityNotFoundException(Action.class, actionId);
}
return actionStatusRepository.getByActionId(pageReq, actionId);
}
@Override
public Page<String> findMessagesByActionStatusId(final Pageable pageable, final Long actionStatusId) {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();

View File

@@ -125,6 +125,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Autowired
private SoftwareModuleTypeRepository softwareModuleTypeRepository;
@Autowired
private DistributionSetTagRepository distributionSetTagRepository;
@Override
public Optional<DistributionSet> findDistributionSetByIdWithDetails(final Long distid) {
return Optional.ofNullable(distributionSetRepository.findOne(DistributionSetSpecification.byId(distid)));
@@ -235,7 +238,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Override
@Transactional
public void deleteDistributionSet(final Collection<Long> distributionSetIDs) {
final List<DistributionSet> setsFound = findDistributionSetAllById(distributionSetIDs);
final List<DistributionSet> setsFound = findDistributionSetsById(distributionSetIDs);
if (setsFound.size() < distributionSetIDs.size()) {
throw new EntityNotFoundException(DistributionSet.class, distributionSetIDs,
@@ -485,6 +488,17 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
return convertDsPage(findByCriteriaAPI(pageReq, specList), pageReq);
}
@Override
public Page<DistributionSet> findDistributionSetsAll(final Pageable pageReq, final Boolean deleted) {
final List<Specification<JpaDistributionSet>> specList = new ArrayList<>(1);
if (deleted != null) {
specList.add(DistributionSetSpecification.isDeleted(deleted));
}
return convertDsPage(findByCriteriaAPI(pageReq, specList), pageReq);
}
@Override
public Page<DistributionSet> findDistributionSetsAllOrderedByLinkTarget(final Pageable pageable,
final DistributionSetFilterBuilder distributionSetFilterBuilder, final String assignedOrInstalled) {
@@ -536,12 +550,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
}
@Override
public List<DistributionSet> findDistributionSetsAll(final Collection<Long> dist) {
return Collections
.unmodifiableList(distributionSetRepository.findAll(DistributionSetSpecification.byIds(dist)));
}
@Override
public Long countDistributionSetsAll() {
final Specification<JpaDistributionSet> spec = DistributionSetSpecification.isDeleted(Boolean.FALSE);
@@ -827,20 +835,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
.unmodifiableList(allDs.stream().map(distributionSetRepository::save).collect(Collectors.toList()));
}
@Override
@Transactional
public List<DistributionSet> unAssignAllDistributionSetsByTag(final Long dsTagId) {
final DistributionSetTag distributionSetTag = tagManagement.findDistributionSetTagById(dsTagId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSetTag.class, dsTagId));
@SuppressWarnings({ "unchecked", "rawtypes" })
final Collection<JpaDistributionSet> distributionSets = (Collection) distributionSetTag
.getAssignedToDistributionSet();
return Collections.unmodifiableList(unAssignTag(distributionSets, distributionSetTag));
}
@Override
@Transactional
public DistributionSet unAssignTag(final Long dsId, final Long dsTagId) {
@@ -894,8 +888,36 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
}
@Override
public List<DistributionSet> findDistributionSetAllById(final Collection<Long> ids) {
public List<DistributionSet> findDistributionSetsById(final Collection<Long> ids) {
return Collections.unmodifiableList(distributionSetRepository.findAll(ids));
}
@Override
public Page<DistributionSet> findDistributionSetsByTag(final Pageable pageable, final Long tagId) {
throwEntityNotFoundExceptionIfDsTagDoesNotExist(tagId);
return convertDsPage(distributionSetRepository.findByTag(pageable, tagId), pageable);
}
private void throwEntityNotFoundExceptionIfDsTagDoesNotExist(final Long tagId) {
if (!distributionSetTagRepository.exists(tagId)) {
throw new EntityNotFoundException(DistributionSetTag.class, tagId);
}
}
@Override
public Page<DistributionSet> findDistributionSetsByTag(final Pageable pageable, final String rsqlParam,
final Long tagId) {
throwEntityNotFoundExceptionIfDsTagDoesNotExist(tagId);
final Specification<JpaDistributionSet> spec = RSQLUtility.parse(rsqlParam, DistributionSetFields.class,
virtualPropertyReplacer);
return convertDsPage(distributionSetRepository.findAll((Specification<JpaDistributionSet>) (root, query,
cb) -> cb.and(DistributionSetSpecification.hasTag(tagId).toPredicate(root, query, cb),
spec.toPredicate(root, query, cb)),
pageable), pageable);
}
}

View File

@@ -86,6 +86,8 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
@Override
public Page<RolloutGroup> findRolloutGroupsByRolloutId(final Long rolloutId, final Pageable pageable) {
throwEntityNotFoundExceptionIfRolloutDoesNotExist(rolloutId);
return convertPage(rolloutGroupRepository.findByRolloutId(rolloutId, pageable), pageable);
}
@@ -100,6 +102,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
@Override
public Page<RolloutGroup> findRolloutGroupsAll(final Long rolloutId, final String rsqlParam,
final Pageable pageable) {
throwEntityNotFoundExceptionIfRolloutDoesNotExist(rolloutId);
final Specification<JpaRolloutGroup> specification = RSQLUtility.parse(rsqlParam, RolloutGroupFields.class,
virtualPropertyReplacer);
@@ -115,11 +118,15 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
pageable);
}
@Override
public Page<RolloutGroup> findAllRolloutGroupsWithDetailedStatus(final Long rolloutId, final Pageable pageable) {
private void throwEntityNotFoundExceptionIfRolloutDoesNotExist(final Long rolloutId) {
if (!rolloutRepository.exists(rolloutId)) {
throw new EntityNotFoundException(Rollout.class, rolloutId);
}
}
@Override
public Page<RolloutGroup> findAllRolloutGroupsWithDetailedStatus(final Long rolloutId, final Pageable pageable) {
throwEntityNotFoundExceptionIfRolloutDoesNotExist(rolloutId);
final Page<JpaRolloutGroup> rolloutGroups = rolloutGroupRepository.findByRolloutId(rolloutId, pageable);
final List<Long> rolloutGroupIds = rolloutGroups.getContent().stream().map(RolloutGroup::getId)
@@ -173,6 +180,8 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
public Page<Target> findRolloutGroupTargets(final Long rolloutGroupId, final String rsqlParam,
final Pageable pageable) {
throwExceptionIfRolloutGroupDoesNotExist(rolloutGroupId);
final Specification<JpaTarget> rsqlSpecification = RSQLUtility.parse(rsqlParam, TargetFields.class,
virtualPropertyReplacer);
@@ -253,4 +262,11 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
}
}
@Override
public long countRolloutGroupsByRolloutId(final Long rolloutId) {
throwEntityNotFoundExceptionIfRolloutDoesNotExist(rolloutId);
return rolloutGroupRepository.countByRolloutId(rolloutId);
}
}

View File

@@ -24,6 +24,7 @@ import org.apache.commons.lang3.StringUtils;
import org.eclipse.hawkbit.repository.AbstractRolloutManagement;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.RolloutFields;
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.repository.RolloutHelper;
@@ -77,6 +78,8 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.integration.support.locks.LockRegistry;
import org.springframework.scheduling.annotation.Async;
@@ -129,6 +132,9 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
@Autowired
private EntityManager entityManager;
@Autowired
private QuotaManagement quotaManagement;
JpaRolloutManagement(final TargetManagement targetManagement, final DeploymentManagement deploymentManagement,
final RolloutGroupManagement rolloutGroupManagement,
final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
@@ -175,7 +181,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
@Transactional
public Rollout createRollout(final RolloutCreate rollout, final int amountGroup,
final RolloutGroupConditions conditions) {
RolloutHelper.verifyRolloutGroupParameter(amountGroup);
RolloutHelper.verifyRolloutGroupParameter(amountGroup, quotaManagement);
final JpaRollout savedRollout = createRollout((JpaRollout) rollout.build());
return createRolloutGroups(amountGroup, conditions, savedRollout);
}
@@ -184,7 +190,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
@Transactional
public Rollout createRollout(final RolloutCreate rollout, final List<RolloutGroupCreate> groups,
final RolloutGroupConditions conditions) {
RolloutHelper.verifyRolloutGroupParameter(groups.size());
RolloutHelper.verifyRolloutGroupParameter(groups.size(), quotaManagement);
final JpaRollout savedRollout = createRollout((JpaRollout) rollout.build());
return createRolloutGroups(groups, conditions, savedRollout);
}
@@ -297,7 +303,10 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
private void handleCreateRollout(final JpaRollout rollout) {
LOGGER.debug("handleCreateRollout called for rollout {}", rollout.getId());
final List<RolloutGroup> rolloutGroups = RolloutHelper.getOrderedGroups(rollout);
final List<RolloutGroup> rolloutGroups = rolloutGroupManagement.findRolloutGroupsByRolloutId(rollout.getId(),
new PageRequest(0, quotaManagement.getMaxRolloutGroupsPerRollout(), new Sort(Direction.ASC, "id")))
.getContent();
int readyGroups = 0;
int totalTargets = 0;
for (final RolloutGroup group : rolloutGroups) {
@@ -325,7 +334,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
}
}
private RolloutGroup fillRolloutGroupWithTargets(final Rollout rollout, final RolloutGroup group1) {
private RolloutGroup fillRolloutGroupWithTargets(final JpaRollout rollout, final RolloutGroup group1) {
RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.CREATING);
final JpaRolloutGroup group = (JpaRolloutGroup) group1;
@@ -338,8 +347,8 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
groupTargetFilter = baseFilter + ";" + group.getTargetFilterQuery();
}
final List<Long> readyGroups = RolloutHelper.getGroupsByStatusIncludingGroup(rollout, RolloutGroupStatus.READY,
group);
final List<Long> readyGroups = RolloutHelper.getGroupsByStatusIncludingGroup(rollout.getRolloutGroups(),
RolloutGroupStatus.READY, group);
final long targetsInGroupFilter = runInNewTransaction("countAllTargetsByTargetFilterQueryAndNotInRolloutGroups",
count -> targetManagement.countAllTargetsByTargetFilterQueryAndNotInRolloutGroups(readyGroups,
@@ -376,12 +385,12 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
}
}
private Long assignTargetsToGroupInNewTransaction(final Rollout rollout, final RolloutGroup group,
private Long assignTargetsToGroupInNewTransaction(final JpaRollout rollout, final RolloutGroup group,
final String targetFilter, final long limit) {
return runInNewTransaction("assignTargetsToRolloutGroup", status -> {
final PageRequest pageRequest = new PageRequest(0, Math.toIntExact(limit));
final List<Long> readyGroups = RolloutHelper.getGroupsByStatusIncludingGroup(rollout,
final List<Long> readyGroups = RolloutHelper.getGroupsByStatusIncludingGroup(rollout.getRolloutGroups(),
RolloutGroupStatus.READY, group);
final Page<Target> targets = targetManagement
.findAllTargetsByTargetFilterQueryAndNotInRolloutGroups(pageRequest, readyGroups, targetFilter);

View File

@@ -622,7 +622,7 @@ public class JpaSoftwareManagement implements SoftwareManagement {
return convertSmMdPage(
softwareModuleMetadataRepository
.findAll(
(Specification<JpaSoftwareModuleMetadata>) (root, query, cb) -> cb.and(
(root, query, cb) -> cb.and(
cb.equal(root.get(JpaSoftwareModuleMetadata_.softwareModule)
.get(JpaSoftwareModule_.id), softwareModuleId),
spec.toPredicate(root, query, cb)),
@@ -631,13 +631,13 @@ public class JpaSoftwareManagement implements SoftwareManagement {
}
@Override
public List<SoftwareModuleMetadata> findSoftwareModuleMetadataBySoftwareModuleId(final Long softwareModuleId) {
public Page<SoftwareModuleMetadata> findSoftwareModuleMetadataBySoftwareModuleId(final Pageable pageable,
final Long softwareModuleId) {
throwExceptionIfSoftwareModuleDoesNotExist(softwareModuleId);
return Collections.unmodifiableList(softwareModuleMetadataRepository
.findAll((Specification<JpaSoftwareModuleMetadata>) (root, query, cb) -> cb
.and(cb.equal(root.get(JpaSoftwareModuleMetadata_.softwareModule).get(JpaSoftwareModule_.id),
softwareModuleId))));
return convertSmMdPage(softwareModuleMetadataRepository.findAll((root, query, cb) -> cb.equal(
root.get(JpaSoftwareModuleMetadata_.softwareModule).get(JpaSoftwareModule_.id), softwareModuleId),
pageable), pageable);
}
@Override

View File

@@ -9,7 +9,9 @@
package org.eclipse.hawkbit.repository.jpa;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import javax.persistence.EntityManager;
@@ -32,6 +34,10 @@ import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Propagation;
@@ -47,6 +53,8 @@ import org.springframework.validation.annotation.Validated;
@Transactional(readOnly = true)
@Validated
public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, SystemManagement {
private static final int MAX_TENANTS_QUERY = 500;
@Autowired
private EntityManager entityManager;
@@ -135,7 +143,7 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
}
private void usageStatsPerTenant(final SystemUsageReport report) {
final List<String> tenants = findTenants();
final List<String> tenants = findTenants(new PageRequest(0, MAX_TENANTS_QUERY)).getContent();
tenants.forEach(tenant -> tenantAware.runAsTenant(tenant, () -> {
report.addTenantData(systemStatsManagement.getStatsOfTenant());
@@ -192,8 +200,13 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
}
@Override
public List<String> findTenants() {
return tenantMetaDataRepository.findAll().stream().map(TenantMetaData::getTenant).collect(Collectors.toList());
public Page<String> findTenants(final Pageable pageable) {
final Page<JpaTenantMetaData> result = tenantMetaDataRepository.findAll(pageable);
return new PageImpl<>(
Collections.unmodifiableList(
result.getContent().stream().map(TenantMetaData::getTenant).collect(Collectors.toList())),
pageable, result.getTotalElements());
}
@Override
@@ -271,4 +284,20 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
public TenantMetaData getTenantMetadata(final Long tenantId) {
return tenantMetaDataRepository.findOne(tenantId);
}
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void forEachTenant(final Consumer<String> consumer) {
Page<String> tenants;
Pageable query = new PageRequest(0, MAX_TENANTS_QUERY);
do {
tenants = findTenants(query);
tenants.forEach(tenant -> tenantAware.runAsTenant(tenant, () -> {
consumer.accept(tenant);
return null;
}));
} while (tenants.hasNext() && (query = tenants.nextPageable()) != null);
}
}

View File

@@ -25,7 +25,9 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.TagSpecification;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.springframework.beans.factory.annotation.Autowired;
@@ -85,17 +87,12 @@ public class JpaTagManagement implements TagManagement {
@Override
@Transactional
public void deleteTargetTag(final String targetTagName) {
final TargetTag tag = targetTagRepository.findByNameEquals(targetTagName)
.orElseThrow(() -> new EntityNotFoundException(TargetTag.class, targetTagName));
targetRepository.findByTag(tag.getId()).forEach(set -> {
set.removeTag(tag);
targetRepository.save(set);
});
if (!targetTagRepository.existsByName(targetTagName)) {
throw new EntityNotFoundException(TargetTag.class, targetTagName);
}
// finally delete the tag itself
targetTagRepository.deleteByName(targetTagName);
}
@Override
@@ -177,22 +174,13 @@ public class JpaTagManagement implements TagManagement {
@Override
@Transactional
public void deleteDistributionSetTag(final String tagName) {
final DistributionSetTag tag = distributionSetTagRepository.findByNameEquals(tagName)
.orElseThrow(() -> new EntityNotFoundException(DistributionSetTag.class, tagName));
distributionSetRepository.findByTag(tag).forEach(set -> {
set.removeTag(tag);
distributionSetRepository.save(set);
});
if (!distributionSetTagRepository.existsByName(tagName)) {
throw new EntityNotFoundException(DistributionSetTag.class, tagName);
}
distributionSetTagRepository.deleteByName(tagName);
}
@Override
public List<DistributionSetTag> findAllDistributionSetTags() {
return Collections.unmodifiableList(distributionSetTagRepository.findAll());
}
@Override
public Optional<TargetTag> findTargetTagById(final Long id) {
return Optional.ofNullable(targetTagRepository.findOne(id));
@@ -223,7 +211,21 @@ public class JpaTagManagement implements TagManagement {
@Override
public Page<TargetTag> findAllTargetTags(final Pageable pageable, final String controllerId) {
if (!targetRepository.existsByControllerId(controllerId)) {
throw new EntityNotFoundException(Target.class, controllerId);
}
return convertTPage(targetTagRepository.findAll(TagSpecification.ofTarget(controllerId), pageable), pageable);
}
@Override
public Page<DistributionSetTag> findDistributionSetTagsByDistributionSet(final Pageable pageable,
final Long setId) {
if (!distributionSetRepository.exists(setId)) {
throw new EntityNotFoundException(DistributionSet.class, setId);
}
return convertDsPage(distributionSetTagRepository.findAll(TagSpecification.ofDistributionSet(setId), pageable),
pageable);
}
}

View File

@@ -113,7 +113,7 @@ public class JpaTargetManagement implements TargetManagement {
}
@Override
public List<Target> findTargetByControllerID(final Collection<String> controllerIDs) {
public List<Target> findTargetsByControllerID(final Collection<String> controllerIDs) {
return Collections.unmodifiableList(
targetRepository.findAll(TargetSpecifications.byControllerIdWithAssignedDsInJoin(controllerIDs)));
}
@@ -376,41 +376,18 @@ public class JpaTargetManagement implements TargetManagement {
.unmodifiableList(allTargets.stream().map(targetRepository::save).collect(Collectors.toList()));
}
private List<Target> unAssignTag(final Collection<Target> targets, final TargetTag tag) {
@SuppressWarnings({ "unchecked", "rawtypes" })
final Collection<JpaTarget> toUnassign = (Collection) targets;
toUnassign.forEach(target -> target.removeTag(tag));
return Collections
.unmodifiableList(toUnassign.stream().map(targetRepository::save).collect(Collectors.toList()));
}
@Override
@Transactional
public List<Target> unAssignAllTargetsByTag(final Long targetTagId) {
final TargetTag tag = targetTagRepository.findById(targetTagId)
.orElseThrow(() -> new EntityNotFoundException(TargetTag.class, targetTagId));
if (tag.getAssignedToTargets().isEmpty()) {
return Collections.emptyList();
}
return unAssignTag(tag.getAssignedToTargets(), tag);
}
@Override
@Transactional
public Target unAssignTag(final String controllerID, final Long targetTagId) {
final Target target = targetRepository.findByControllerId(controllerID)
final JpaTarget target = (JpaTarget) targetRepository.findByControllerId(controllerID)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerID));
final TargetTag tag = targetTagRepository.findById(targetTagId)
.orElseThrow(() -> new EntityNotFoundException(TargetTag.class, targetTagId));
final List<Target> unAssignTag = unAssignTag(Lists.newArrayList(target), tag);
return unAssignTag.isEmpty() ? null : unAssignTag.get(0);
target.removeTag(tag);
return targetRepository.save(target);
}
@Override
@@ -559,13 +536,28 @@ public class JpaTargetManagement implements TargetManagement {
}
@Override
public List<Target> findTargetsByTag(final String tagName) {
final Optional<TargetTag> tag = targetTagRepository.findByNameEquals(tagName);
if (!tag.isPresent()) {
return Collections.emptyList();
}
public Page<Target> findTargetsByTag(final Pageable pageable, final Long tagId) {
throwEntityNotFoundExceptionIfTagDoesNotExist(tagId);
return Collections.unmodifiableList(targetRepository.findByTag(tag.get().getId()));
return convertPage(targetRepository.findByTag(pageable, tagId), pageable);
}
private void throwEntityNotFoundExceptionIfTagDoesNotExist(final Long tagId) {
if (!targetTagRepository.exists(tagId)) {
throw new EntityNotFoundException(TargetTag.class, tagId);
}
}
@Override
public Page<Target> findTargetsByTag(final Pageable pageable, final String rsqlParam, final Long tagId) {
throwEntityNotFoundExceptionIfTagDoesNotExist(tagId);
final Specification<JpaTarget> spec = RSQLUtility.parse(rsqlParam, TargetFields.class, virtualPropertyReplacer);
return convertPage(targetRepository.findAll((Specification<JpaTarget>) (root, query, cb) -> cb.and(
TargetSpecifications.hasTag(tagId).toPredicate(root, query, cb), spec.toPredicate(root, query, cb)),
pageable), pageable);
}
@Override
@@ -594,7 +586,7 @@ public class JpaTargetManagement implements TargetManagement {
}
@Override
public List<Target> findTargetAllById(final Collection<Long> ids) {
public List<Target> findTargetsById(final Collection<Long> ids) {
return Collections.unmodifiableList(targetRepository.findAll(ids));
}

View File

@@ -24,6 +24,7 @@ import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.PropertiesQuotaManagement;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TagManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
@@ -63,6 +64,7 @@ import org.eclipse.hawkbit.repository.model.helper.SystemManagementHolder;
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
import org.eclipse.hawkbit.repository.rsql.RsqlValidationOracle;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.security.SecurityTokenGenerator;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
@@ -123,6 +125,11 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
return new RsqlParserValidationOracle();
}
@Bean
PropertiesQuotaManagement staticQuotaManagement(final HawkbitSecurityProperties securityProperties) {
return new PropertiesQuotaManagement(securityProperties);
}
/**
* @param distributionSetManagement
* to loading the {@link DistributionSetType}
@@ -557,8 +564,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
AutoAssignScheduler autoAssignScheduler(final TenantAware tenantAware, final SystemManagement systemManagement,
final SystemSecurityContext systemSecurityContext, final AutoAssignChecker autoAssignChecker,
final LockRegistry lockRegistry) {
return new AutoAssignScheduler(tenantAware, systemManagement, systemSecurityContext, autoAssignChecker,
lockRegistry);
return new AutoAssignScheduler(systemManagement, systemSecurityContext, autoAssignChecker, lockRegistry);
}
/**
@@ -567,8 +573,6 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
* Note: does not activate in test profile, otherwise it is hard to test the
* rollout handling functionality.
*
* @param tenantAware
* to run as specific tenant
* @param systemManagement
* to find all tenants
* @param rolloutManagement
@@ -583,6 +587,6 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
@ConditionalOnProperty(prefix = "hawkbit.rollout.scheduler", name = "enabled", matchIfMissing = true)
RolloutScheduler rolloutScheduler(final TenantAware tenantAware, final SystemManagement systemManagement,
final RolloutManagement rolloutManagement, final SystemSecurityContext systemSecurityContext) {
return new RolloutScheduler(tenantAware, systemManagement, rolloutManagement, systemSecurityContext);
return new RolloutScheduler(systemManagement, rolloutManagement, systemSecurityContext);
}
}

View File

@@ -146,6 +146,16 @@ public interface RolloutGroupRepository
*/
Page<JpaRolloutGroup> findByRolloutId(final Long rolloutId, Pageable page);
/**
* Counts all {@link RolloutGroup} for a specific rollout.
*
* @param rolloutId
* the ID of the rollout to find the rollout groups
*
* @return the amount of found {@link RolloutGroup}s.
*/
long countByRolloutId(final Long rolloutId);
@Modifying
@Query("DELETE FROM JpaRolloutGroup g where g.id in :rolloutGroupIds")
void deleteByIds(@Param("rolloutGroupIds") List<Long> rolloutGroups);

View File

@@ -94,13 +94,16 @@ public interface TargetRepository extends BaseEntityRepository<JpaTarget, Long>,
/**
* Finds {@link Target}s by assigned {@link Tag}.
*
* @param page
* pages query and sorting information
*
* @param tagId
* to be found
* @return list of found targets
* @return page of found targets
*/
@Query(value = "SELECT DISTINCT t FROM JpaTarget t JOIN t.tags tt WHERE tt.id = :tag")
List<JpaTarget> findByTag(@Param("tag") final Long tagId);
Page<JpaTarget> findByTag(Pageable page, @Param("tag") final Long tagId);
/**
* Finds all {@link Target}s based on given {@link Target#getControllerId()}

View File

@@ -50,6 +50,16 @@ public interface TargetTagRepository
*/
Optional<TargetTag> findByNameEquals(String tagName);
/**
* Checks if tag with given name exists.
*
* @param tagName
* to check for
* @return <code>true</code> is tag with given name exists
*/
@Query("SELECT CASE WHEN COUNT(t)>0 THEN 'true' ELSE 'false' END FROM JpaTargetTag t WHERE t.name=:tagName")
boolean existsByName(@Param("tagName") String tagName);
/**
* Returns all instances of the type.
*

View File

@@ -9,7 +9,6 @@
package org.eclipse.hawkbit.repository.jpa.aspects;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -25,16 +24,17 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.transaction.TransactionSystemException;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/**
@@ -54,8 +54,7 @@ public class ExceptionMappingAspectHandler implements Ordered {
* most specific mappable exception according to the type hierarchy of the
* exception.
*/
private static final List<Class<?>> MAPPED_EXCEPTION_ORDER = new ArrayList<>(4);
private static final List<Class<?>> MAPPED_EXCEPTION_ORDER = Lists.newArrayListWithExpectedSize(4);
@Autowired
private JpaVendorAdapter jpaVendorAdapter;
@@ -65,14 +64,14 @@ public class ExceptionMappingAspectHandler implements Ordered {
MAPPED_EXCEPTION_ORDER.add(DuplicateKeyException.class);
MAPPED_EXCEPTION_ORDER.add(DataIntegrityViolationException.class);
MAPPED_EXCEPTION_ORDER.add(ConcurrencyFailureException.class);
MAPPED_EXCEPTION_ORDER.add(OptimisticLockingFailureException.class);
MAPPED_EXCEPTION_ORDER.add(AccessDeniedException.class);
EXCEPTION_MAPPING.put(DuplicateKeyException.class.getName(), EntityAlreadyExistsException.class.getName());
EXCEPTION_MAPPING.put(DataIntegrityViolationException.class.getName(),
EntityAlreadyExistsException.class.getName());
EXCEPTION_MAPPING.put(ConcurrencyFailureException.class.getName(),
EXCEPTION_MAPPING.put(OptimisticLockingFailureException.class.getName(),
ConcurrentModificationException.class.getName());
EXCEPTION_MAPPING.put(AccessDeniedException.class.getName(), InsufficientPermissionException.class.getName());
}
@@ -86,10 +85,7 @@ public class ExceptionMappingAspectHandler implements Ordered {
* @throws Throwable
*/
@AfterThrowing(pointcut = "( execution( * org.springframework.transaction..*.*(..)) "
+ " || execution( * org.eclipse.hawkbit.repository.*.*(..)) "
+ " || execution( * org.eclipse.hawkbit.controller.*.*(..)) "
+ " || execution( * org.eclipse.hawkbit.rest.resource.*.*(..)) "
+ " || execution( * org.eclipse.hawkbit.service.*.*(..)) )", throwing = "ex")
+ " || execution( * org.eclipse.hawkbit.repository.*.*(..)) )", throwing = "ex")
// Exception for squid:S00112, squid:S1162
// It is a AspectJ proxy which deals with exceptions.
@SuppressWarnings({ "squid:S00112", "squid:S1162" })

View File

@@ -8,12 +8,10 @@
*/
package org.eclipse.hawkbit.repository.jpa.autoassign;
import java.util.List;
import java.util.concurrent.locks.Lock;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.integration.support.locks.LockRegistry;
@@ -27,8 +25,6 @@ public class AutoAssignScheduler {
private static final String PROP_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.autoassign.scheduler.fixedDelay:2000}";
private final TenantAware tenantAware;
private final SystemManagement systemManagement;
private final SystemSecurityContext systemSecurityContext;
@@ -40,8 +36,6 @@ public class AutoAssignScheduler {
/**
* Instantiates a new AutoAssignScheduler
*
* @param tenantAware
* to run as specific tenant
* @param systemManagement
* to find all tenants
* @param systemSecurityContext
@@ -51,10 +45,9 @@ public class AutoAssignScheduler {
* @param lockRegistry
* to acquire a lock per tenant
*/
public AutoAssignScheduler(final TenantAware tenantAware, final SystemManagement systemManagement,
public AutoAssignScheduler(final SystemManagement systemManagement,
final SystemSecurityContext systemSecurityContext, final AutoAssignChecker autoAssignChecker,
final LockRegistry lockRegistry) {
this.tenantAware = tenantAware;
this.systemManagement = systemManagement;
this.systemSecurityContext = systemSecurityContext;
this.autoAssignChecker = autoAssignChecker;
@@ -82,24 +75,17 @@ public class AutoAssignScheduler {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So
// iterate through all tenants and execute the rollout check for
// each tenant separately.
final List<String> tenants = systemManagement.findTenants();
LOGGER.info("Checking target filter queries for tenants: {}", tenants.size());
final Lock lock = lockRegistry.obtain("autoassign");
if (!lock.tryLock()) {
return null;
}
try {
for (final String tenant : tenants) {
tenantAware.runAsTenant(tenant, () -> {
autoAssignChecker.check();
return null;
});
}
systemManagement.forEachTenant(tenant -> autoAssignChecker.check());
} finally {
lock.unlock();
}
return null;
}
}

View File

@@ -131,7 +131,6 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
return actionType;
}
@Override
public List<ActionStatus> getActionStatus() {
if (actionStatus == null) {
return Collections.emptyList();

View File

@@ -142,7 +142,6 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements
}
}
@Override
public List<String> getMessages() {
if (messages == null) {
messages = Collections.emptyList();

View File

@@ -1,112 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa.model;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.ActionWithStatusCount;
/**
* Custom JPA Model for querying {@link Action} include the count of the
* action's {@link ActionStatus}.
*
*/
public class JpaActionWithStatusCount implements ActionWithStatusCount {
private final Long actionStatusCount;
private final Long dsId;
private final String dsName;
private final String dsVersion;
private final JpaAction action;
private final String rolloutName;
/**
* JPA constructor, the parameter are the result set columns of the custom
* query.
*
* @param actionId
* the ID of the action
* @param actionType
* the action type
* @param active
* the active attribute of the action
* @param forcedTime
* the forced time attribute of the action
* @param status
* the status attribute of the action
* @param actionCreatedAt
* the createdAt timestamp of the action
* @param actionLastModifiedAt
* the last modified timestamp of the action
* @param dsId
* the ID of the distributionset
* @param dsName
* the name of the distributionset
* @param dsVersion
* the version of the distributionset
* @param actionStatusCount
* the count of the action status for this action
* @param rolloutName
* the rollout name
*/
// Exception squid:S00107 - needed this way for JPA to fill the view
@SuppressWarnings("squid:S00107")
public JpaActionWithStatusCount(final Long actionId, final ActionType actionType, final boolean active,
final Long forcedTime, final Status status, final Long actionCreatedAt, final Long actionLastModifiedAt,
final Long dsId, final String dsName, final String dsVersion, final Long actionStatusCount,
final String rolloutName) {
this.dsId = dsId;
this.dsName = dsName;
this.dsVersion = dsVersion;
this.actionStatusCount = actionStatusCount;
this.rolloutName = rolloutName;
action = new JpaAction();
action.setActionType(actionType);
action.setActive(active);
action.setForcedTime(forcedTime);
action.setStatus(status);
action.setId(actionId);
action.setActionType(actionType);
action.setCreatedAt(actionCreatedAt);
action.setLastModifiedAt(actionLastModifiedAt);
}
@Override
public Action getAction() {
return action;
}
@Override
public Long getDsId() {
return dsId;
}
@Override
public String getDsName() {
return dsName;
}
@Override
public String getDsVersion() {
return dsVersion;
}
@Override
public Long getActionStatusCount() {
return actionStatusCount;
}
@Override
public String getRolloutName() {
return rolloutName;
}
}

View File

@@ -35,7 +35,7 @@ import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.repository.event.remote.DistributionSetDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdateEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.DistributionSetTypeUndefinedException;
import org.eclipse.hawkbit.repository.exception.UnsupportedSoftwareModuleForThisDistributionSetException;
import org.eclipse.hawkbit.repository.model.Action;
@@ -174,7 +174,6 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
this(name, version, description, type, moduleList, false);
}
@Override
public Set<DistributionSetTag> getTags() {
if (tags == null) {
return Collections.emptySet();
@@ -339,7 +338,7 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
publishEventWithEventPublisher(
new DistributionSetUpdateEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
new DistributionSetUpdatedEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
if (isSoftDeleted(descriptorEvent)) {
publishEventWithEventPublisher(new DistributionSetDeletedEvent(getTenant(), getId(), getClass().getName(),

View File

@@ -20,7 +20,7 @@ import javax.persistence.UniqueConstraint;
import org.eclipse.hawkbit.repository.event.remote.DistributionSetTagDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTagCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTagUpdateEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTagUpdatedEvent;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
@@ -64,7 +64,6 @@ public class JpaDistributionSetTag extends JpaTag implements DistributionSetTag,
// Default constructor for JPA.
}
@Override
public List<DistributionSet> getAssignedToDistributionSet() {
if (assignedToDistributionSet == null) {
return Collections.emptyList();
@@ -82,7 +81,7 @@ public class JpaDistributionSetTag extends JpaTag implements DistributionSetTag,
@Override
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(
new DistributionSetTagUpdateEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
new DistributionSetTagUpdatedEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
}

View File

@@ -130,7 +130,6 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
this.distributionSet = (JpaDistributionSet) distributionSet;
}
@Override
public List<RolloutGroup> getRolloutGroups() {
if (rolloutGroups == null) {
return Collections.emptyList();

View File

@@ -20,7 +20,7 @@ import javax.persistence.UniqueConstraint;
import org.eclipse.hawkbit.repository.event.remote.TargetTagDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTagCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTagUpdateEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTagUpdatedEvent;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
@@ -61,7 +61,6 @@ public class JpaTargetTag extends JpaTag implements TargetTag, EventAwareEntity
// Default constructor for JPA.
}
@Override
public List<Target> getAssignedToTargets() {
if (assignedToTargets == null) {
return Collections.emptyList();
@@ -80,7 +79,7 @@ public class JpaTargetTag extends JpaTag implements TargetTag, EventAwareEntity
@Override
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher()
.publishEvent(new TargetTagUpdateEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
.publishEvent(new TargetTagUpdatedEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
}
@Override

View File

@@ -8,12 +8,9 @@
*/
package org.eclipse.hawkbit.repository.jpa.rollout;
import java.util.List;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
@@ -29,8 +26,6 @@ public class RolloutScheduler {
private static final String PROP_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.rollout.scheduler.fixedDelay:2000}";
private final TenantAware tenantAware;
private final SystemManagement systemManagement;
private final RolloutManagement rolloutManagement;
@@ -40,8 +35,6 @@ public class RolloutScheduler {
/**
* Constructor.
*
* @param tenantAware
* to run as specific tenant
* @param systemManagement
* to find all tenants
* @param rolloutManagement
@@ -49,9 +42,8 @@ public class RolloutScheduler {
* @param systemSecurityContext
* to run as system
*/
public RolloutScheduler(final TenantAware tenantAware, final SystemManagement systemManagement,
final RolloutManagement rolloutManagement, final SystemSecurityContext systemSecurityContext) {
this.tenantAware = tenantAware;
public RolloutScheduler(final SystemManagement systemManagement, final RolloutManagement rolloutManagement,
final SystemSecurityContext systemSecurityContext) {
this.systemManagement = systemManagement;
this.rolloutManagement = rolloutManagement;
this.systemSecurityContext = systemSecurityContext;
@@ -76,22 +68,9 @@ public class RolloutScheduler {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So
// iterate through all tenants and execute the rollout check for
// each tenant seperately.
final List<String> tenants = systemManagement.findTenants();
LOGGER.info("Checking rollouts for {} tenants", tenants.size());
for (final String tenant : tenants) {
tenantAware.runAsTenant(tenant, () -> {
try {
rolloutManagement.handleRollouts();
// We catch all potential runtime exceptions here to
// ensure that not all tenants are blocked if we have a
// problem with a rollout.
} catch (@SuppressWarnings("squid:S1166") final RuntimeException e) {
LOGGER.error("Failed to handle rollouts for tenant {}. I will move on to next tenant.", tenant,
e);
}
return null;
});
}
systemManagement.forEachTenant(tenant -> rolloutManagement.handleRollouts());
return null;
});
}

View File

@@ -78,7 +78,6 @@ public final class DistributionSetSpecification {
return (targetRoot, query, cb) -> {
final Predicate predicate = cb.equal(targetRoot.<Long> get(JpaDistributionSet_.id), distid);
targetRoot.fetch(JpaDistributionSet_.modules, JoinType.LEFT);
targetRoot.fetch(JpaDistributionSet_.tags, JoinType.LEFT);
targetRoot.fetch(JpaDistributionSet_.type, JoinType.LEFT);
query.distinct(true);
@@ -218,4 +217,20 @@ public final class DistributionSetSpecification {
};
}
/**
* {@link Specification} for retrieving {@link DistributionSet}s by tag.
*
* @param tagId
* the ID of the distribution set which must be assigned
* @return the {@link DistributionSet} {@link Specification}
*/
public static Specification<JpaDistributionSet> hasTag(final Long tagId) {
return (targetRoot, query, cb) -> {
final SetJoin<JpaDistributionSet, JpaDistributionSetTag> tags = targetRoot.join(JpaDistributionSet_.tags,
JoinType.LEFT);
return cb.equal(tags.get(JpaDistributionSetTag_.id), tagId);
};
}
}

View File

@@ -8,9 +8,6 @@
*/
package org.eclipse.hawkbit.repository.jpa.specifications;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType_;
@@ -28,24 +25,6 @@ public final class SoftwareModuleSpecification {
// utility class
}
/**
* {@link Specification} for retrieving {@link SoftwareModule}s by its ID
* attribute.
*
* @param moduleId
* to search for
* @return the {@link SoftwareModule} {@link Specification}
*/
public static Specification<JpaSoftwareModule> byId(final Long moduleId) {
return (targetRoot, query, cb) -> {
final Predicate predicate = cb.equal(targetRoot.<Long> get(JpaSoftwareModule_.id), moduleId);
targetRoot.fetch(JpaSoftwareModule_.type);
targetRoot.fetch(JpaSoftwareModule_.metadata, JoinType.LEFT);
query.distinct(true);
return predicate;
};
}
/**
* {@link Specification} for retrieving {@link SoftwareModule}s where its
* DELETED attribute is false.
@@ -57,8 +36,8 @@ public final class SoftwareModuleSpecification {
}
/**
* {@link Specification} for retrieving {@link SoftwareModule}s by
* "like name or like version".
* {@link Specification} for retrieving {@link SoftwareModule}s by "like
* name or like version".
*
* @param subString
* to be filtered on
@@ -71,8 +50,8 @@ public final class SoftwareModuleSpecification {
}
/**
* {@link Specification} for retrieving {@link SoftwareModule}s by
* "like name or like version".
* {@link Specification} for retrieving {@link SoftwareModule}s by "like
* name or like version".
*
* @param type
* to be filtered on

View File

@@ -10,11 +10,18 @@ package org.eclipse.hawkbit.repository.jpa.specifications;
import javax.persistence.criteria.Join;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.springframework.data.jpa.domain.Specification;
/**
@@ -28,13 +35,13 @@ public final class TagSpecification {
}
/**
* {@link Specification} for retrieving {@link DistributionSet}s by its
* DELETED attribute.
* {@link Specification} for retrieving {@link TargetTag}s by assigned
* {@link Target}.
*
* @param isDeleted
* TRUE/FALSE are compared to the attribute DELETED. If NULL the
* attribute is ignored
* @return the {@link DistributionSet} {@link Specification}
* @param controllerId
* of the target
*
* @return the {@link JpaTargetTag} {@link Specification}
*/
public static Specification<JpaTargetTag> ofTarget(final String controllerId) {
return (targetRoot, query, criteriaBuilder) -> {
@@ -47,4 +54,25 @@ public final class TagSpecification {
}
/**
* {@link Specification} for retrieving {@link DistributionSetTag}s by
* assigned {@link DistributionSet}.
*
* @param dsId
* of the distribution set
*
* @return the {@link JpaDistributionSetTag} {@link Specification}
*/
public static Specification<JpaDistributionSetTag> ofDistributionSet(final Long dsId) {
return (dsRoot, query, criteriaBuilder) -> {
final Join<JpaDistributionSetTag, JpaDistributionSet> tagJoin = dsRoot
.join(JpaDistributionSetTag_.assignedToDistributionSet);
query.distinct(true);
return criteriaBuilder.equal(tagJoin.get(JpaDistributionSet_.id), dsId);
};
}
}

View File

@@ -287,4 +287,19 @@ public final class TargetSpecifications {
return (targetRoot, query, cb) -> cb.equal(
targetRoot.get(JpaTarget_.installedDistributionSet).get(JpaDistributionSet_.id), distributionSetId);
}
/**
* {@link Specification} for retrieving {@link Target}s by tag.
*
* @param tagId
* the ID of the distribution set which must be assigned
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasTag(final Long tagId) {
return (targetRoot, query, cb) -> {
final SetJoin<JpaTarget, JpaTargetTag> tags = targetRoot.join(JpaTarget_.tags, JoinType.LEFT);
return cb.equal(tags.get(JpaTargetTag_.id), tagId);
};
}
}