Remove TargetInfo entity (#453)

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
Kai Zimmermann
2017-03-17 12:18:43 +01:00
committed by GitHub
parent 67d17fe661
commit 602fb78168
89 changed files with 1166 additions and 1462 deletions

View File

@@ -321,8 +321,8 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
* rolloutgroup in a specific status
*/
@EntityGraph(attributePaths = { "target" }, type = EntityGraphType.LOAD)
Page<Action> findByRolloutAndRolloutGroupParentAndStatus(Pageable pageable, JpaRollout rollout,
JpaRolloutGroup rolloutGroupParent, Status actionStatus);
Page<Action> findByRolloutIdAndRolloutGroupParentIdAndStatus(Pageable pageable, Long rollout,
Long rolloutGroupParent, Status actionStatus);
/**
* Retrieving all actions referring to the first group of a rollout.
@@ -337,7 +337,7 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
* rolloutgroup in a specific status
*/
@EntityGraph(attributePaths = { "target" }, type = EntityGraphType.LOAD)
Page<Action> findByRolloutAndRolloutGroupParentIsNullAndStatus(Pageable pageable, JpaRollout rollout,
Page<Action> findByRolloutIdAndRolloutGroupParentIsNullAndStatus(Pageable pageable, Long rollout,
Status actionStatus);
/**

View File

@@ -11,12 +11,10 @@ package org.eclipse.hawkbit.repository.jpa;
import java.util.List;
import java.util.stream.Collectors;
import javax.persistence.EntityManager;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
@@ -41,24 +39,19 @@ public final class DeploymentHelper {
* of the target
* @param setInstalledDate
* to set
* @param entityManager
* for the operation
* @param targetInfoRepository
* for the operation
*
* @return updated target
*/
static JpaTarget updateTargetInfo(@NotNull final JpaTarget target, @NotNull final TargetUpdateStatus status,
final boolean setInstalledDate, final TargetInfoRepository targetInfoRepository,
final EntityManager entityManager) {
final JpaTargetInfo ts = (JpaTargetInfo) target.getTargetInfo();
ts.setUpdateStatus(status);
final boolean setInstalledDate) {
target.setUpdateStatus(status);
if (setInstalledDate) {
ts.setInstallationDate(System.currentTimeMillis());
target.setInstallationDate(System.currentTimeMillis());
}
targetInfoRepository.save(ts);
return entityManager.merge(target);
return target;
}
/**
@@ -72,14 +65,11 @@ public final class DeploymentHelper {
* for the operation
* @param targetRepository
* for the operation
* @param entityManager
* for the operation
* @param targetInfoRepository
* for the operation
*/
static void successCancellation(final JpaAction action, final ActionRepository actionRepository,
final TargetRepository targetRepository, final TargetInfoRepository targetInfoRepository,
final EntityManager entityManager) {
final TargetRepository targetRepository) {
// set action inactive
action.setActive(false);
@@ -90,12 +80,11 @@ public final class DeploymentHelper {
.filter(a -> !a.getId().equals(action.getId())).collect(Collectors.toList());
if (nextActiveActions.isEmpty()) {
target.setAssignedDistributionSet(target.getTargetInfo().getInstalledDistributionSet());
updateTargetInfo(target, TargetUpdateStatus.IN_SYNC, false, targetInfoRepository, entityManager);
target.setAssignedDistributionSet(target.getInstalledDistributionSet());
updateTargetInfo(target, TargetUpdateStatus.IN_SYNC, false);
} else {
target.setAssignedDistributionSet(nextActiveActions.get(0).getDistributionSet());
}
target.setNew(false);
targetRepository.save(target);
}

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.model.Action;
@@ -109,6 +110,12 @@ public interface DistributionSetRepository
@Query("select DISTINCT d from JpaDistributionSet d join fetch d.modules m join d.actions a where a.id = :action")
JpaDistributionSet findByActionId(@Param("action") Long action);
@Query("select DISTINCT ds from JpaDistributionSet ds join fetch ds.modules join ds.assignedToTargets t where t.controllerId = :controllerId")
Optional<DistributionSet> findAssignedToTarget(@Param("controllerId") String controllerId);
@Query("select DISTINCT ds from JpaDistributionSet ds join fetch ds.modules join ds.installedAtTargets t where t.controllerId = :controllerId")
Optional<DistributionSet> findInstalledAtTarget(@Param("controllerId") String controllerId);
/**
* Counts {@link DistributionSet} instances of given type in the repository.
*

View File

@@ -1,60 +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;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
/**
* Custom repository implementation as standard spring repository fails as of
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=415027 .
*
*/
@Service
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public class EclipseLinkTargetInfoRepository implements TargetInfoRepository {
@Autowired
private EntityManager entityManager;
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void setTargetUpdateStatus(final TargetUpdateStatus status, final List<Long> targets) {
final Query query = entityManager.createQuery(
"update JpaTargetInfo ti set ti.updateStatus = :status where ti.targetId in :targets and ti.updateStatus != :status");
query.setParameter("targets", targets);
query.setParameter("status", status);
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public <S extends JpaTargetInfo> S save(final S entity) {
if (entity.isNew()) {
entityManager.persist(entity);
return entity;
} else {
return entityManager.merge(entity);
}
}
}

View File

@@ -27,7 +27,6 @@ import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.builder.ActionStatusCreate;
import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.ToManyAttributeEntriesException;
@@ -40,7 +39,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus_;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications;
import org.eclipse.hawkbit.repository.model.Action;
@@ -48,7 +46,6 @@ import org.eclipse.hawkbit.repository.model.Action.Status;
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.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.security.SystemSecurityContext;
@@ -92,9 +89,6 @@ public class JpaControllerManagement implements ControllerManagement {
@Autowired
private TargetManagement targetManagement;
@Autowired
private TargetInfoRepository targetInfoRepository;
@Autowired
private ActionStatusRepository actionStatusRepository;
@@ -135,10 +129,10 @@ public class JpaControllerManagement implements ControllerManagement {
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public Target updateLastTargetQuery(final String controllerId, final URI address) {
final Target target = targetRepository.findByControllerId(controllerId)
final JpaTarget target = (JpaTarget) targetRepository.findByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
return updateTargetStatus(target.getTargetInfo(), null, System.currentTimeMillis(), address).getTarget();
return updateTargetStatus(target, null, System.currentTimeMillis(), address);
}
@Override
@@ -222,32 +216,29 @@ public class JpaControllerManagement implements ControllerManagement {
return result;
}
return updateTargetStatus(target.getTargetInfo(), null, System.currentTimeMillis(), address).getTarget();
return updateTargetStatus(target, null, System.currentTimeMillis(), address);
}
private TargetInfo updateTargetStatus(final TargetInfo targetInfo, final TargetUpdateStatus status,
private Target updateTargetStatus(final JpaTarget toUpdate, final TargetUpdateStatus status,
final Long lastTargetQuery, final URI address) {
final JpaTargetInfo mtargetInfo = (JpaTargetInfo) entityManager.merge(targetInfo);
if (status != null) {
mtargetInfo.setUpdateStatus(status);
toUpdate.setUpdateStatus(status);
}
if (lastTargetQuery != null) {
mtargetInfo.setLastTargetQuery(lastTargetQuery);
toUpdate.setLastTargetQuery(lastTargetQuery);
}
if (mtargetInfo.getUpdateStatus() == TargetUpdateStatus.UNKNOWN) {
mtargetInfo.setUpdateStatus(TargetUpdateStatus.REGISTERED);
afterCommit.afterCommit(() -> eventPublisher
.publishEvent(new TargetUpdatedEvent(mtargetInfo.getTarget(), applicationContext.getId())));
if (TargetUpdateStatus.UNKNOWN.equals(toUpdate.getUpdateStatus())) {
toUpdate.setUpdateStatus(TargetUpdateStatus.REGISTERED);
}
if (address != null) {
mtargetInfo.setAddress(address.toString());
afterCommit.afterCommit(() -> eventPublisher
.publishEvent(new TargetPollEvent(mtargetInfo.getTarget(), applicationContext.getId())));
toUpdate.setAddress(address.toString());
afterCommit.afterCommit(
() -> eventPublisher.publishEvent(new TargetPollEvent(toUpdate, applicationContext.getId())));
}
return targetInfoRepository.save(mtargetInfo);
return targetRepository.save(toUpdate);
}
@Override
@@ -291,8 +282,7 @@ public class JpaControllerManagement implements ControllerManagement {
// the canceled action itself.
actionStatus.addMessage(
RepositoryConstants.SERVER_MESSAGE_PREFIX + "Cancellation completion is finished sucessfully.");
DeploymentHelper.successCancellation(action, actionRepository, targetRepository, targetInfoRepository,
entityManager);
DeploymentHelper.successCancellation(action, actionRepository, targetRepository);
}
@Override
@@ -332,8 +322,7 @@ public class JpaControllerManagement implements ControllerManagement {
switch (actionStatus.getStatus()) {
case ERROR:
target = DeploymentHelper.updateTargetInfo(target, TargetUpdateStatus.ERROR, false, targetInfoRepository,
entityManager);
target = DeploymentHelper.updateTargetInfo(target, TargetUpdateStatus.ERROR, false);
handleErrorOnAction(action, target);
break;
case FINISHED:
@@ -358,7 +347,6 @@ public class JpaControllerManagement implements ControllerManagement {
mergedAction.setStatus(Status.ERROR);
mergedTarget.setAssignedDistributionSet(null);
mergedTarget.setNew(false);
targetRepository.save(mergedTarget);
}
@@ -380,23 +368,19 @@ public class JpaControllerManagement implements ControllerManagement {
private void handleFinishedAndStoreInTargetStatus(final JpaTarget target, final JpaAction action) {
action.setActive(false);
action.setStatus(Status.FINISHED);
final JpaTargetInfo targetInfo = (JpaTargetInfo) target.getTargetInfo();
final JpaDistributionSet ds = (JpaDistributionSet) entityManager.merge(action.getDistributionSet());
targetInfo.setInstalledDistributionSet(ds);
targetInfo.setInstallationDate(System.currentTimeMillis());
target.setInstalledDistributionSet(ds);
target.setInstallationDate(System.currentTimeMillis());
// check if the assigned set is equal to the installed set (not
// necessarily the case as another update might be pending already).
if (target.getAssignedDistributionSet() != null && target.getAssignedDistributionSet().getId()
.equals(targetInfo.getInstalledDistributionSet().getId())) {
targetInfo.setUpdateStatus(TargetUpdateStatus.IN_SYNC);
if (target.getAssignedDistributionSet() != null
&& target.getAssignedDistributionSet().getId().equals(target.getInstalledDistributionSet().getId())) {
target.setUpdateStatus(TargetUpdateStatus.IN_SYNC);
}
targetInfoRepository.save(targetInfo);
afterCommit.afterCommit(
() -> eventPublisher.publishEvent(new TargetUpdatedEvent(target, applicationContext.getId())));
targetRepository.save(target);
entityManager.detach(ds);
}
@@ -408,26 +392,19 @@ public class JpaControllerManagement implements ControllerManagement {
final JpaTarget target = (JpaTarget) targetRepository.findByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
final JpaTargetInfo targetInfo = (JpaTargetInfo) target.getTargetInfo();
targetInfo.getControllerAttributes().putAll(data);
target.getControllerAttributes().putAll(data);
if (targetInfo.getControllerAttributes().size() > securityProperties.getDos()
.getMaxAttributeEntriesPerTarget()) {
if (target.getControllerAttributes().size() > securityProperties.getDos().getMaxAttributeEntriesPerTarget()) {
LOG_DOS.info("Target tries to insert more than the allowed number of entries ({}). DOS attack anticipated!",
securityProperties.getDos().getMaxAttributeEntriesPerTarget());
throw new ToManyAttributeEntriesException(
String.valueOf(securityProperties.getDos().getMaxAttributeEntriesPerTarget()));
}
targetInfo.setLastTargetQuery(System.currentTimeMillis());
targetInfo.setRequestControllerAttributes(false);
target.setLastTargetQuery(System.currentTimeMillis());
target.setRequestControllerAttributes(false);
final Target result = targetInfoRepository.save(targetInfo).getTarget();
afterCommit.afterCommit(
() -> eventPublisher.publishEvent(new TargetUpdatedEvent(result, applicationContext.getId())));
return result;
return targetRepository.save(target);
}
@Override

View File

@@ -51,7 +51,6 @@ 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.JpaTargetInfo;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
@@ -63,8 +62,6 @@ 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;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
@@ -125,9 +122,6 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@Autowired
private TargetManagement targetManagement;
@Autowired
private TargetInfoRepository targetInfoRepository;
@Autowired
private AuditorAware<String> auditorProvider;
@@ -257,9 +251,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
currentUser = null;
}
targetIds.forEach(tIds -> targetRepository.setAssignedDistributionSet(set, System.currentTimeMillis(),
currentUser, tIds));
targetIds.forEach(tIds -> targetInfoRepository.setTargetUpdateStatus(TargetUpdateStatus.PENDING, tIds));
targetIds.forEach(tIds -> targetRepository.setAssignedDistributionSetAndUpdateStatus(TargetUpdateStatus.PENDING,
set, System.currentTimeMillis(), currentUser, tIds));
final Map<String, JpaAction> targetIdsToActions = targets.stream().map(
t -> actionRepository.save(createTargetAction(targetsWithActionMap, t, set, rollout, rolloutGroup)))
.collect(Collectors.toMap(a -> a.getTarget().getControllerId(), Function.identity()));
@@ -311,10 +304,15 @@ public class JpaDeploymentManagement implements DeploymentManagement {
}
private void assignDistributionSetEvent(final Action action) {
((JpaTargetInfo) action.getTarget().getTargetInfo()).setUpdateStatus(TargetUpdateStatus.PENDING);
afterCommit.afterCommit(() -> eventPublisher
.publishEvent(new TargetUpdatedEvent(action.getTarget(), applicationContext.getId())));
// Update is not available in the object as the update was executed
// through JQL
final JpaTarget target = (JpaTarget) action.getTarget();
target.setUpdateStatus(TargetUpdateStatus.PENDING);
entityManager.detach(target);
afterCommit.afterCommit(
() -> eventPublisher.publishEvent(new TargetUpdatedEvent(target, applicationContext.getId())));
afterCommit.afterCommit(() -> eventPublisher
.publishEvent(new TargetAssignDistributionSetEvent(action, applicationContext.getId())));
}
@@ -388,15 +386,10 @@ public class JpaDeploymentManagement implements DeploymentManagement {
* the action id of the assignment
*/
private void cancelAssignDistributionSetEvent(final Target target, final Long actionId) {
loadLazyTargetInfo(target);
afterCommit.afterCommit(() -> eventPublisher
.publishEvent(new CancelTargetAssignmentEvent(target, actionId, applicationContext.getId())));
}
private static void loadLazyTargetInfo(final Target target) {
target.getTargetInfo();
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_COMMITTED)
@@ -419,8 +412,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELED, System.currentTimeMillis(),
"A force quit has been performed."));
DeploymentHelper.successCancellation(action, actionRepository, targetRepository, targetInfoRepository,
entityManager);
DeploymentHelper.successCancellation(action, actionRepository, targetRepository);
return actionRepository.save(action);
}
@@ -428,29 +420,29 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@Override
@Modifying
@Transactional(isolation = Isolation.READ_COMMITTED)
public long startScheduledActionsByRolloutGroupParent(@NotNull final Rollout rollout,
final RolloutGroup rolloutGroupParent) {
public long startScheduledActionsByRolloutGroupParent(@NotNull final Long rolloutId,
final Long rolloutGroupParentId) {
long totalActionsCount = 0L;
long lastStartedActionsCount;
do {
lastStartedActionsCount = startScheduledActionsByRolloutGroupParentInNewTransaction(rollout,
rolloutGroupParent, ACTION_PAGE_LIMIT);
lastStartedActionsCount = startScheduledActionsByRolloutGroupParentInNewTransaction(rolloutId,
rolloutGroupParentId, ACTION_PAGE_LIMIT);
totalActionsCount += lastStartedActionsCount;
} while (lastStartedActionsCount > 0);
return totalActionsCount;
}
private long startScheduledActionsByRolloutGroupParentInNewTransaction(final Rollout rollout,
final RolloutGroup rolloutGroupParent, final int limit) {
private long startScheduledActionsByRolloutGroupParentInNewTransaction(final Long rolloutId,
final Long rolloutGroupParentId, final int limit) {
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("startScheduledActions");
def.setReadOnly(false);
def.setIsolationLevel(Isolation.READ_UNCOMMITTED.value());
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
return new TransactionTemplate(txManager, def).execute(status -> {
final Page<Action> rolloutGroupActions = findActionsByRolloutAndRolloutGroupParent(rollout,
rolloutGroupParent, limit);
final Page<Action> rolloutGroupActions = findActionsByRolloutAndRolloutGroupParent(rolloutId,
rolloutGroupParentId, limit);
rolloutGroupActions.map(action -> (JpaAction) action).forEach(this::startScheduledAction);
@@ -458,33 +450,35 @@ public class JpaDeploymentManagement implements DeploymentManagement {
});
}
private Page<Action> findActionsByRolloutAndRolloutGroupParent(final Rollout rollout,
final RolloutGroup rolloutGroupParent, final int limit) {
final JpaRollout jpaRollout = (JpaRollout) rollout;
final JpaRolloutGroup jpaRolloutGroup = (JpaRolloutGroup) rolloutGroupParent;
private Page<Action> findActionsByRolloutAndRolloutGroupParent(final Long rolloutId,
final Long rolloutGroupParentId, final int limit) {
final PageRequest pageRequest = new PageRequest(0, limit);
if (rolloutGroupParent == null) {
return actionRepository.findByRolloutAndRolloutGroupParentIsNullAndStatus(pageRequest, jpaRollout,
if (rolloutGroupParentId == null) {
return actionRepository.findByRolloutIdAndRolloutGroupParentIsNullAndStatus(pageRequest, rolloutId,
Action.Status.SCHEDULED);
} else {
return actionRepository.findByRolloutAndRolloutGroupParentAndStatus(pageRequest, jpaRollout,
jpaRolloutGroup, Action.Status.SCHEDULED);
return actionRepository.findByRolloutIdAndRolloutGroupParentIdAndStatus(pageRequest, rolloutId,
rolloutGroupParentId, Action.Status.SCHEDULED);
}
}
private Action startScheduledAction(final JpaAction action) {
private void startScheduledAction(final JpaAction action) {
// check if we need to override running update actions
final Set<Long> overrideObsoleteUpdateActions = overrideObsoleteUpdateActions(
Collections.singletonList(action.getTarget().getId()));
if (action.getTarget().getAssignedDistributionSet() != null && action.getDistributionSet().getId()
.equals(action.getTarget().getAssignedDistributionSet().getId())) {
JpaTarget target = (JpaTarget) action.getTarget();
if (target.getAssignedDistributionSet() != null
&& action.getDistributionSet().getId().equals(target.getAssignedDistributionSet().getId())) {
// the target has already the distribution set assigned, we don't
// need to start the scheduled action, just finish it.
action.setStatus(Status.FINISHED);
action.setActive(false);
setSkipActionStatus(action);
return actionRepository.save(action);
actionRepository.save(action);
return;
}
action.setActive(true);
@@ -493,20 +487,18 @@ public class JpaDeploymentManagement implements DeploymentManagement {
setRunningActionStatus(savedAction, null);
final JpaTarget target = (JpaTarget) savedAction.getTarget();
target = (JpaTarget) entityManager.merge(savedAction.getTarget());
target.setAssignedDistributionSet(savedAction.getDistributionSet());
final JpaTargetInfo targetInfo = (JpaTargetInfo) target.getTargetInfo();
targetInfo.setUpdateStatus(TargetUpdateStatus.PENDING);
target.setUpdateStatus(TargetUpdateStatus.PENDING);
targetRepository.save(target);
targetInfoRepository.save(targetInfo);
// in case we canceled an action before for this target, then don't fire
// assignment event
if (!overrideObsoleteUpdateActions.contains(savedAction.getId())) {
assignDistributionSetEvent(savedAction);
afterCommit.afterCommit(() -> eventPublisher
.publishEvent(new TargetAssignDistributionSetEvent(savedAction, applicationContext.getId())));
}
return savedAction;
}
private void setRunningActionStatus(final JpaAction action, final String actionMessage) {
@@ -682,4 +674,18 @@ public class JpaDeploymentManagement implements DeploymentManagement {
public Slice<Action> findActionsAll(final Pageable pageable) {
return convertAcPage(actionRepository.findAll(pageable), pageable);
}
@Override
public Optional<DistributionSet> getAssignedDistributionSet(final String controllerId) {
throwExceptionIfTargetFoesNotExist(controllerId);
return distributoinSetRepository.findAssignedToTarget(controllerId);
}
@Override
public Optional<DistributionSet> getInstalledDistributionSet(final String controllerId) {
throwExceptionIfTargetFoesNotExist(controllerId);
return distributoinSetRepository.findInstalledAtTarget(controllerId);
}
}

View File

@@ -675,7 +675,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
final JpaDistributionSet result = entityManager.merge((JpaDistributionSet) ds);
result.setLastModifiedAt(0L);
return result;
return distributionSetRepository.save(result);
}
/**

View File

@@ -24,7 +24,6 @@ import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.ListJoin;
import javax.persistence.criteria.Root;
@@ -33,8 +32,6 @@ import org.eclipse.hawkbit.repository.ReportManagement;
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.JpaTargetInfo;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.report.model.DataReportSeries;
@@ -85,19 +82,18 @@ public class JpaReportManagement implements ReportManagement {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
final Root<JpaTarget> targetRoot = query.from(JpaTarget.class);
final Join<JpaTarget, JpaTargetInfo> targetInfo = targetRoot.join(JpaTarget_.targetInfo);
final Expression<Long> countColumn = cb.count(targetInfo.get(JpaTargetInfo_.targetId));
final Expression<Long> countColumn = cb.count(targetRoot.get(JpaTarget_.id));
final CriteriaQuery<Object[]> multiselect = query
.multiselect(targetInfo.get(JpaTargetInfo_.updateStatus), countColumn)
.groupBy(targetInfo.get(JpaTargetInfo_.updateStatus))
.orderBy(cb.desc(targetInfo.get(JpaTargetInfo_.updateStatus)));
.multiselect(targetRoot.get(JpaTarget_.updateStatus), countColumn)
.groupBy(targetRoot.get(JpaTarget_.updateStatus))
.orderBy(cb.desc(targetRoot.get(JpaTarget_.updateStatus)));
// | col1 | col2 |
// | U_STATUS | COUNT |
final List<Object[]> resultList = entityManager.createQuery(multiselect).getResultList();
final List<DataReportSeriesItem<TargetUpdateStatus>> reportSeriesItems = resultList.stream()
.map(r -> new DataReportSeriesItem<TargetUpdateStatus>((TargetUpdateStatus) r[0], (Long) r[1]))
.map(r -> new DataReportSeriesItem<>((TargetUpdateStatus) r[0], (Long) r[1]))
.collect(Collectors.toList());
return new DataReportSeries<>("Target Status Overview", reportSeriesItems);
@@ -117,25 +113,25 @@ public class JpaReportManagement implements ReportManagement {
final List<DataReportSeriesItem<SeriesTime>> resultList = new ArrayList<>();
// hours
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.HOUR,
resultList.add(new DataReportSeriesItem<>(SeriesTime.HOUR,
entityManager.createQuery(createCountSelectTargetsLastPoll(cb, beforeHour, now)).getSingleResult()));
// days
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.DAY, entityManager
resultList.add(new DataReportSeriesItem<>(SeriesTime.DAY, entityManager
.createQuery(createCountSelectTargetsLastPoll(cb, beforeDay, beforeHour)).getSingleResult()));
// weeks
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.WEEK, entityManager
resultList.add(new DataReportSeriesItem<>(SeriesTime.WEEK, entityManager
.createQuery(createCountSelectTargetsLastPoll(cb, beforeWeek, beforeDay)).getSingleResult()));
// months
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.MONTH, entityManager
resultList.add(new DataReportSeriesItem<>(SeriesTime.MONTH, entityManager
.createQuery(createCountSelectTargetsLastPoll(cb, beforeMonth, beforeWeek)).getSingleResult()));
// years
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.YEAR, entityManager
resultList.add(new DataReportSeriesItem<>(SeriesTime.YEAR, entityManager
.createQuery(createCountSelectTargetsLastPoll(cb, beforeYear, beforeMonth)).getSingleResult()));
// years
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.MORE_THAN_YEAR,
resultList.add(new DataReportSeriesItem<>(SeriesTime.MORE_THAN_YEAR,
entityManager.createQuery(createCountSelectTargetsLastPoll(cb, null, beforeYear)).getSingleResult()));
// never
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.NEVER,
resultList.add(new DataReportSeriesItem<>(SeriesTime.NEVER,
entityManager.createQuery(createCountSelectTargetsLastPoll(cb, null, null)).getSingleResult()));
return new DataReportSeries<>("TargetLastPoll", resultList);
@@ -172,8 +168,8 @@ public class JpaReportManagement implements ReportManagement {
final CriteriaBuilder cbTopX = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> queryTopX = cbTopX.createQuery(Object[].class);
final Root<JpaDistributionSet> rootTopX = queryTopX.from(JpaDistributionSet.class);
final ListJoin<JpaDistributionSet, JpaTargetInfo> joinTopX = rootTopX
.join(JpaDistributionSet_.installedAtTargets, JoinType.LEFT);
final ListJoin<JpaDistributionSet, JpaTarget> joinTopX = rootTopX.join(JpaDistributionSet_.installedAtTargets,
JoinType.LEFT);
final Expression<Long> countColumn = cbTopX.count(joinTopX);
// top x usage query
final CriteriaQuery<Object[]> groupBy = queryTopX
@@ -270,14 +266,13 @@ public class JpaReportManagement implements ReportManagement {
// count select statement
final CriteriaQuery<Long> countSelect = cb.createQuery(Long.class);
final Root<JpaTarget> countSelectRoot = countSelect.from(JpaTarget.class);
final Join<JpaTarget, JpaTargetInfo> targetInfoJoin = countSelectRoot.join(JpaTarget_.targetInfo);
countSelect.select(cb.count(countSelectRoot));
if (start != null && end != null) {
countSelect.where(cb.between(targetInfoJoin.get(JpaTargetInfo_.lastTargetQuery), start, end));
countSelect.where(cb.between(countSelectRoot.get(JpaTarget_.lastTargetQuery), start, end));
} else if (from == null && to != null) {
countSelect.where(cb.lessThanOrEqualTo(targetInfoJoin.get(JpaTargetInfo_.lastTargetQuery), end));
countSelect.where(cb.lessThanOrEqualTo(countSelectRoot.get(JpaTarget_.lastTargetQuery), end));
} else {
countSelect.where(cb.isNull(targetInfoJoin.get(JpaTargetInfo_.lastTargetQuery)));
countSelect.where(cb.isNull(countSelectRoot.get(JpaTarget_.lastTargetQuery)));
}
return countSelect;
}
@@ -314,10 +309,10 @@ public class JpaReportManagement implements ReportManagement {
outerReportItems.add(outer.toItem());
}
} else {
outerReportItems.add(new DataReportSeriesItem<String>("misc", inner.count));
outerReportItems.add(new DataReportSeriesItem<>("misc", inner.count));
}
innerOuterReport.add(new InnerOuterDataReportSeries<String>(
innerOuterReport.add(new InnerOuterDataReportSeries<>(
new DataReportSeries<>("DS-Name", Collections.singletonList(inner.toItem())),
new DataReportSeries<>("DS-Version", outerReportItems)));
}

View File

@@ -432,7 +432,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
throw new RolloutIllegalStateException("First Group is not the first group.");
}
deploymentManagement.startScheduledActionsByRolloutGroupParent(rollout, null);
deploymentManagement.startScheduledActionsByRolloutGroupParent(rollout.getId(), null);
rolloutGroup.setStatus(RolloutGroupStatus.RUNNING);
rolloutGroupRepository.save(rolloutGroup);

View File

@@ -25,6 +25,7 @@ import org.eclipse.hawkbit.repository.jpa.builder.JpaTagCreate;
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.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
@@ -242,4 +243,9 @@ public class JpaTagManagement implements TagManagement {
return convertDsPage(distributionSetTagRepository.findAll(spec, pageable), pageable);
}
@Override
public Page<TargetTag> findAllTargetTags(final Pageable pageable, final String controllerId) {
return convertTPage(targetTagRepository.findAll(TagSpecification.ofTarget(controllerId), pageable), pageable);
}
}

View File

@@ -13,6 +13,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
@@ -20,8 +21,6 @@ import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.validation.constraints.NotNull;
@@ -38,11 +37,8 @@ import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetCreate;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetUpdate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
@@ -99,9 +95,6 @@ public class JpaTargetManagement implements TargetManagement {
@Autowired
private TargetTagRepository targetTagRepository;
@Autowired
private TargetInfoRepository targetInfoRepository;
@Autowired
private NoCountPagingRepository criteriaNoCountDao;
@@ -122,28 +115,6 @@ public class JpaTargetManagement implements TargetManagement {
return targetRepository.findByControllerId(controllerId);
}
@Override
public Optional<Target> findTargetByControllerIDWithDetails(final String controllerId) {
final Optional<Target> result = targetRepository.findByControllerId(controllerId);
// load lazy relations
if (!result.isPresent()) {
return result;
}
result.get().getTargetInfo().getControllerAttributes().size();
if (result.get().getTargetInfo() != null
&& result.get().getTargetInfo().getInstalledDistributionSet() != null) {
result.get().getTargetInfo().getInstalledDistributionSet().getName();
result.get().getTargetInfo().getInstalledDistributionSet().getModules().size();
}
if (result.get().getAssignedDistributionSet() != null) {
result.get().getAssignedDistributionSet().getName();
result.get().getAssignedDistributionSet().getModules().size();
}
return result;
}
@Override
public List<Target> findTargetByControllerID(final Collection<String> controllerIDs) {
return Collections.unmodifiableList(targetRepository
@@ -157,16 +128,7 @@ public class JpaTargetManagement implements TargetManagement {
@Override
public Slice<Target> findTargetsAll(final Pageable pageable) {
// workarround - no join fetch allowed that is why we need specification
// instead of query for
// count() of Pageable
final Specification<JpaTarget> spec = (root, query, cb) -> {
if (!query.getResultType().isAssignableFrom(Long.class)) {
root.fetch(JpaTarget_.targetInfo);
}
return cb.conjunction();
};
return convertPage(criteriaNoCountDao.findAll(spec, pageable, JpaTarget.class), pageable);
return convertPage(criteriaNoCountDao.findAll(pageable, JpaTarget.class), pageable);
}
@Override
@@ -188,14 +150,6 @@ public class JpaTargetManagement implements TargetManagement {
return convertPage(targetRepository.findAll(spec, pageable), pageable);
}
@Override
public List<Target> findTargetsByControllerIDsWithTags(final List<String> controllerIDs) {
final List<List<String>> partition = Lists.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT);
return partition.stream()
.map(ids -> targetRepository.findAll(TargetSpecifications.byControllerIdWithStatusAndTagsInJoin(ids)))
.flatMap(t -> t.stream()).collect(Collectors.toList());
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@@ -205,11 +159,9 @@ public class JpaTargetManagement implements TargetManagement {
final JpaTarget target = (JpaTarget) targetRepository.findByControllerId(update.getControllerId())
.orElseThrow(() -> new EntityNotFoundException(Target.class, update.getControllerId()));
target.setNew(false);
update.getName().ifPresent(target::setName);
update.getDescription().ifPresent(target::setDescription);
update.getAddress().ifPresent(address -> ((JpaTargetInfo) target.getTargetInfo()).setAddress(address));
update.getAddress().ifPresent(target::setAddress);
update.getSecurityToken().ifPresent(target::setSecurityToken);
return targetRepository.save(target);
@@ -282,7 +234,7 @@ public class JpaTargetManagement implements TargetManagement {
@Override
public Page<Target> findTargetByInstalledDistributionSet(final Long distributionSetID, final Pageable pageReq) {
throwEntityNotFoundIfDsDoesNotExist(distributionSetID);
return targetRepository.findByTargetInfoInstalledDistributionSetId(pageReq, distributionSetID);
return targetRepository.findByInstalledDistributionSetId(pageReq, distributionSetID);
}
@Override
@@ -303,7 +255,7 @@ public class JpaTargetManagement implements TargetManagement {
@Override
public Page<Target> findTargetByUpdateStatus(final Pageable pageable, final TargetUpdateStatus status) {
return targetRepository.findByTargetInfoUpdateStatus(pageable, status);
return targetRepository.findByUpdateStatus(pageable, status);
}
@Override
@@ -312,8 +264,7 @@ public class JpaTargetManagement implements TargetManagement {
final Boolean selectTargetWithNoTag, final String... tagNames) {
final List<Specification<JpaTarget>> specList = buildSpecificationList(
new FilterParams(installedOrAssignedDistributionSetId, status, overdueState, searchText,
selectTargetWithNoTag, tagNames),
true);
selectTargetWithNoTag, tagNames));
return findByCriteriaAPI(pageable, specList);
}
@@ -323,16 +274,14 @@ public class JpaTargetManagement implements TargetManagement {
final Boolean selectTargetWithNoTag, final String... tagNames) {
final List<Specification<JpaTarget>> specList = buildSpecificationList(
new FilterParams(installedOrAssignedDistributionSetId, status, overdueState, searchText,
selectTargetWithNoTag, tagNames),
true);
selectTargetWithNoTag, tagNames));
return countByCriteriaAPI(specList);
}
private List<Specification<JpaTarget>> buildSpecificationList(final FilterParams filterParams,
final boolean fetch) {
private List<Specification<JpaTarget>> buildSpecificationList(final FilterParams filterParams) {
final List<Specification<JpaTarget>> specList = new ArrayList<>();
if (filterParams.getFilterByStatus() != null && !filterParams.getFilterByStatus().isEmpty()) {
specList.add(TargetSpecifications.hasTargetUpdateStatus(filterParams.getFilterByStatus(), fetch));
specList.add(TargetSpecifications.hasTargetUpdateStatus(filterParams.getFilterByStatus()));
}
if (filterParams.getOverdueState() != null) {
specList.add(TargetSpecifications.isOverdue(TimestampCalculator.calculateOverdueTimestamp()));
@@ -470,15 +419,11 @@ public class JpaTargetManagement implements TargetManagement {
final CriteriaQuery<JpaTarget> query = cb.createQuery(JpaTarget.class);
final Root<JpaTarget> targetRoot = query.from(JpaTarget.class);
// necessary joins for the select
final Join<JpaTarget, JpaTargetInfo> targetInfo = (Join<JpaTarget, JpaTargetInfo>) targetRoot
.fetch(JpaTarget_.targetInfo, JoinType.LEFT);
// select case expression to retrieve the case value as a column to be
// able to order based on
// this column, installed first,...
final Expression<Object> selectCase = cb.selectCase()
.when(cb.equal(targetInfo.get(JpaTargetInfo_.installedDistributionSet).get(JpaDistributionSet_.id),
.when(cb.equal(targetRoot.get(JpaTarget_.installedDistributionSet).get(JpaDistributionSet_.id),
orderByDistributionId), 1)
.when(cb.equal(targetRoot.get(JpaTarget_.assignedDistributionSet).get(JpaDistributionSet_.id),
orderByDistributionId), 2)
@@ -487,8 +432,8 @@ public class JpaTargetManagement implements TargetManagement {
query.distinct(true);
// build the specifications and then to predicates necessary by the
// given filters
final Predicate[] specificationsForMultiSelect = specificationsToPredicate(
buildSpecificationList(filterParams, true), targetRoot, query, cb);
final Predicate[] specificationsForMultiSelect = specificationsToPredicate(buildSpecificationList(filterParams),
targetRoot, query, cb);
// if we have some predicates then add it to the where clause of the
// multiselect
@@ -532,7 +477,7 @@ public class JpaTargetManagement implements TargetManagement {
public Long countTargetByInstalledDistributionSet(final Long distId) {
throwEntityNotFoundIfDsDoesNotExist(distId);
return targetRepository.countByTargetInfoInstalledDistributionSetId(distId);
return targetRepository.countByInstalledDistributionSetId(distId);
}
@Override
@@ -611,14 +556,7 @@ public class JpaTargetManagement implements TargetManagement {
throw new EntityAlreadyExistsException();
}
target.setNew(true);
final JpaTarget savedTarget = targetRepository.save(target);
final JpaTargetInfo targetInfo = (JpaTargetInfo) savedTarget.getTargetInfo();
targetInfo.setNew(true);
final Target targetToReturn = targetInfoRepository.save(targetInfo).getTarget();
targetInfo.setNew(false);
return targetToReturn;
return targetRepository.save(target);
}
@Override
@@ -668,4 +606,12 @@ public class JpaTargetManagement implements TargetManagement {
return Collections.unmodifiableList(targetRepository.findAll(ids));
}
@Override
public Map<String, String> getControllerAttributes(final String controllerId) {
final JpaTarget target = (JpaTarget) findTargetByControllerID(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
return target.getControllerAttributes();
}
}

View File

@@ -1,58 +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;
import java.util.List;
import javax.persistence.Entity;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
/**
* Usually a JPA spring data repository to handle {@link TargetInfo} entity.
* However, do to an eclipselink bug with spring boot now a regular interface
* that is implemented by {@link EclipseLinkTargetInfoRepository}.
*
*/
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public interface TargetInfoRepository {
/**
* Sets new TargetUpdateStatus of given target if is not already on that
* value.
*
* @param status
* to set
* @param targets
* to set it for
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Query("update JpaTargetInfo ti set ti.updateStatus = :status where ti.targetId in :targets and ti.updateStatus != :status")
void setTargetUpdateStatus(@Param("status") TargetUpdateStatus status, @Param("targets") List<Long> targets);
/**
* Save entity and evict cache with it.
*
* @param entity
* to persists
*
* @return persisted or updated {@link Entity}
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
<S extends JpaTargetInfo> S save(S entity);
}

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
@@ -17,12 +18,9 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Tag;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
@@ -38,19 +36,43 @@ import org.springframework.transaction.annotation.Transactional;
public interface TargetRepository extends BaseEntityRepository<JpaTarget, Long>, JpaSpecificationExecutor<JpaTarget> {
/**
* Loads {@link Target} including details {@link EntityGraph} by given ID.
* Sets {@link JpaTarget#getAssignedDistributionSet()}.
*
* @param set
* to use
* @param status
* to set
* @param modifiedAt
* current time
* @param modifiedBy
* current auditor
* @param targets
* to update
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Query("UPDATE JpaTarget t SET t.assignedDistributionSet = :set, t.lastModifiedAt = :lastModifiedAt, t.lastModifiedBy = :lastModifiedBy, t.updateStatus = :status WHERE t.id IN :targets")
void setAssignedDistributionSetAndUpdateStatus(@Param("status") TargetUpdateStatus status,
@Param("set") JpaDistributionSet set, @Param("lastModifiedAt") Long modifiedAt,
@Param("lastModifiedBy") String modifiedBy, @Param("targets") Collection<Long> targets);
/**
* Loads {@link Target} by given ID.
*
* @param controllerID
* to search for
* @return found {@link Target} or <code>null</code> if not found.
*/
@EntityGraph(value = "Target.detail", type = EntityGraphType.LOAD)
Optional<Target> findByControllerId(String controllerID);
@Query("SELECT t.controllerAttributes FROM JpaTarget t WHERE t.controllerId=:controllerId")
Map<String, String> getControllerAttributes(@Param("controllerId") String controllerId);
/**
* Checks if target with given id exists.
*
* @param controllerId to check
* @param controllerId
* to check
* @return <code>true</code> if target with given id exists
*/
@Query("SELECT CASE WHEN COUNT(t)>0 THEN 'true' ELSE 'false' END FROM JpaTarget t WHERE t.controllerId=:controllerId")
@@ -102,7 +124,7 @@ public interface TargetRepository extends BaseEntityRepository<JpaTarget, Long>,
*
* @return found targets
*/
Page<Target> findByTargetInfoUpdateStatus(final Pageable pageable, final TargetUpdateStatus status);
Page<Target> findByUpdateStatus(final Pageable pageable, final TargetUpdateStatus status);
/**
* retrieves the {@link Target}s which has the {@link DistributionSet}
@@ -114,7 +136,7 @@ public interface TargetRepository extends BaseEntityRepository<JpaTarget, Long>,
* the ID of the {@link DistributionSet}
* @return the found {@link Target}s
*/
Page<Target> findByTargetInfoInstalledDistributionSetId(final Pageable pageable, final Long setID);
Page<Target> findByInstalledDistributionSetId(final Pageable pageable, final Long setID);
/**
* Finds all targets that have defined {@link DistributionSet} assigned.
@@ -141,13 +163,13 @@ public interface TargetRepository extends BaseEntityRepository<JpaTarget, Long>,
/**
* Counts number of targets with given
* {@link TargetInfo#getInstalledDistributionSet()}.
* {@link Target#getInstalledDistributionSet()}.
*
* @param distId
* to search for
* @return number of found {@link Target}s.
*/
Long countByTargetInfoInstalledDistributionSetId(final Long distId);
Long countByInstalledDistributionSetId(final Long distId);
/**
* Finds all {@link Target}s in the repository.
@@ -164,24 +186,6 @@ public interface TargetRepository extends BaseEntityRepository<JpaTarget, Long>,
@Query("SELECT t FROM JpaTarget t WHERE t.id IN ?1")
List<JpaTarget> findAll(Iterable<Long> ids);
/**
* Sets {@link Target#getAssignedDistributionSet()}.
*
* @param set
* to use
* @param modifiedAt
* current time
* @param modifiedBy
* current auditor
* @param targets
* to update
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Query("UPDATE JpaTarget t SET t.assignedDistributionSet = :set, t.lastModifiedAt = :lastModifiedAt, t.lastModifiedBy = :lastModifiedBy WHERE t.id IN :targets")
void setAssignedDistributionSet(@Param("set") JpaDistributionSet set, @Param("lastModifiedAt") Long modifiedAt,
@Param("lastModifiedBy") String modifiedBy, @Param("targets") Collection<Long> targets);
/**
*
* Finds all targets of a rollout group.

View File

@@ -12,7 +12,6 @@ import org.apache.commons.lang3.StringUtils;
import org.eclipse.hawkbit.repository.builder.AbstractTargetUpdateCreate;
import org.eclipse.hawkbit.repository.builder.TargetCreate;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
/**
@@ -40,11 +39,9 @@ public class JpaTargetCreate extends AbstractTargetUpdateCreate<TargetCreate> im
}
target.setDescription(description);
final JpaTargetInfo targetInfo = (JpaTargetInfo) target.getTargetInfo();
targetInfo.setAddress(address);
targetInfo.setUpdateStatus(getStatus().orElse(TargetUpdateStatus.UNKNOWN));
getLastTargetQuery().ifPresent(targetInfo::setLastTargetQuery);
target.setAddress(address);
target.setUpdateStatus(getStatus().orElse(TargetUpdateStatus.UNKNOWN));
getLastTargetQuery().ifPresent(target::setLastTargetQuery);
return target;
}

View File

@@ -18,12 +18,14 @@ import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Version;
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* Holder of the base attributes common to all entities.
@@ -72,26 +74,26 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity {
@Override
@Access(AccessType.PROPERTY)
@Column(name = "last_modified_at", insertable = false, updatable = true)
@Column(name = "last_modified_at", insertable = true, updatable = true)
public Long getLastModifiedAt() {
return lastModifiedAt;
}
@Override
@Access(AccessType.PROPERTY)
@Column(name = "last_modified_by", insertable = false, updatable = true, length = 40)
@Column(name = "last_modified_by", insertable = true, updatable = true, length = 40)
public String getLastModifiedBy() {
return lastModifiedBy;
}
@CreatedBy
public void setCreatedBy(final String createdBy) {
this.createdBy = createdBy;
}
if (isController()) {
this.createdBy = "CONTROLLER_PLUG_AND_PLAY";
return;
}
@LastModifiedBy
public void setLastModifiedBy(final String lastModifiedBy) {
this.lastModifiedBy = lastModifiedBy;
this.createdBy = createdBy;
}
@CreatedDate
@@ -101,9 +103,30 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity {
@LastModifiedDate
public void setLastModifiedAt(final Long lastModifiedAt) {
if (isController()) {
return;
}
this.lastModifiedAt = lastModifiedAt;
}
@LastModifiedBy
public void setLastModifiedBy(final String lastModifiedBy) {
if (isController()) {
return;
}
this.lastModifiedBy = lastModifiedBy;
}
private boolean isController() {
return SecurityContextHolder.getContext().getAuthentication()
.getDetails() instanceof TenantAwareAuthenticationDetails
&& ((TenantAwareAuthenticationDetails) SecurityContextHolder.getContext().getAuthentication()
.getDetails()).isController();
}
@Override
public int getOptLockRevision() {
return optLockRevision;

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.jpa.model;
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.eclipse.persistence.descriptors.DescriptorEventAdapter;
import org.eclipse.persistence.queries.UpdateObjectQuery;
/**
* Listens to change in property values of an entity and calls the corresponding
@@ -29,10 +30,13 @@ public class EntityPropertyChangeListener extends DescriptorEventAdapter {
@Override
public void postUpdate(final DescriptorEvent event) {
final Object object = event.getObject();
if (isEventAwareEntity(object)) {
if (isEventAwareEntity(object)
&& isFireUpdate((EventAwareEntity) object, (UpdateObjectQuery) event.getQuery())) {
doNotifiy(() -> ((EventAwareEntity) object).fireUpdateEvent(event));
}
}
@Override
@@ -51,4 +55,9 @@ public class EntityPropertyChangeListener extends DescriptorEventAdapter {
AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(runnable);
}
private static boolean isFireUpdate(final EventAwareEntity entity, final UpdateObjectQuery query) {
return entity.getUpdateIgnoreFields().isEmpty() || query.getObjectChangeSet().getChangedAttributeNames()
.stream().anyMatch(field -> !entity.getUpdateIgnoreFields().contains(field));
}
}

View File

@@ -8,6 +8,9 @@
*/
package org.eclipse.hawkbit.repository.jpa.model;
import java.util.Collections;
import java.util.List;
import org.eclipse.persistence.descriptors.DescriptorEvent;
/**
@@ -36,4 +39,12 @@ public interface EventAwareEntity {
* @param descriptorEvent
*/
void fireDeleteEvent(DescriptorEvent descriptorEvent);
/**
* @return list of entity fields that if the only changed fields prevents
* {@link #fireUpdateEvent(DescriptorEvent)} call.
*/
default List<String> getUpdateIgnoreFields() {
return Collections.emptyList();
}
}

View File

@@ -99,8 +99,8 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
@OneToMany(mappedBy = "autoAssignDistributionSet", targetEntity = JpaTargetFilterQuery.class, fetch = FetchType.LAZY)
private List<TargetFilterQuery> autoAssignFilters;
@OneToMany(mappedBy = "installedDistributionSet", targetEntity = JpaTargetInfo.class, fetch = FetchType.LAZY)
private List<JpaTargetInfo> installedAtTargets;
@OneToMany(mappedBy = "installedDistributionSet", targetEntity = JpaTarget.class, fetch = FetchType.LAZY)
private List<JpaTarget> installedAtTargets;
@OneToMany(mappedBy = "distributionSet", targetEntity = JpaAction.class, fetch = FetchType.LAZY)
private List<JpaAction> actions;

View File

@@ -8,16 +8,27 @@
*/
package org.eclipse.hawkbit.repository.jpa.model;
import java.net.URI;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.Index;
@@ -25,14 +36,11 @@ import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedAttributeNode;
import javax.persistence.NamedEntityGraph;
import javax.persistence.MapKeyColumn;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
@@ -45,14 +53,21 @@ import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHol
import org.eclipse.hawkbit.repository.jpa.model.helper.SystemSecurityContextHolder;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.PollStatus;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.data.domain.Persistable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Lists;
/**
* JPA implementation of {@link Target}.
@@ -64,26 +79,27 @@ import org.springframework.data.domain.Persistable;
@Index(name = "sp_idx_target_02", columnList = "tenant,name"),
@Index(name = "sp_idx_target_03", columnList = "tenant,controller_id,assigned_distribution_set"),
@Index(name = "sp_idx_target_04", columnList = "tenant,created_at"),
@Index(name = "sp_idx_target_05", columnList = "tenant,address"),
@Index(name = "sp_idx_target_prim", columnList = "tenant,id") }, uniqueConstraints = @UniqueConstraint(columnNames = {
"controller_id", "tenant" }, name = "uk_tenant_controller_id"))
@NamedEntityGraph(name = "Target.detail", attributeNodes = { @NamedAttributeNode("tags"),
@NamedAttributeNode(value = "assignedDistributionSet"), @NamedAttributeNode(value = "targetInfo") })
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for
// sub entities
@SuppressWarnings("squid:S2160")
public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Long>, Target, EventAwareEntity {
public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAwareEntity {
private static final long serialVersionUID = 1L;
private static final Logger LOG = LoggerFactory.getLogger(JpaTarget.class);
private static final List<String> TARGET_UPDATE_EVENT_IGNORE_FIELDS = Lists.newArrayList("lastTargetQuery",
"lastTargetQuery", "address", "optLockRevision", "lastModifiedAt", "lastModifiedBy");
@Column(name = "controller_id", length = 64)
@Size(min = 1, max = 64)
@NotEmpty
@Pattern(regexp = "[.\\S]*", message = "has whitespaces which are not allowed")
private String controllerId;
@Transient
private boolean entityNew;
@CascadeOnDelete
@ManyToMany(targetEntity = JpaTargetTag.class)
@JoinTable(name = "sp_target_target_tag", joinColumns = {
@@ -95,16 +111,6 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Lon
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, targetEntity = JpaAction.class)
private List<JpaAction> actions;
@ManyToOne(optional = true, fetch = FetchType.LAZY, targetEntity = JpaDistributionSet.class)
@JoinColumn(name = "assigned_distribution_set", nullable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_assign_ds"))
private JpaDistributionSet assignedDistributionSet;
@CascadeOnDelete
@OneToOne(cascade = { CascadeType.PERSIST,
CascadeType.MERGE }, fetch = FetchType.LAZY, targetEntity = JpaTargetInfo.class)
@PrimaryKeyJoinColumn
private JpaTargetInfo targetInfo;
/**
* the security token of the target which allows if enabled to authenticate
* with this security token.
@@ -118,6 +124,46 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Lon
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST })
private List<RolloutTargetGroup> rolloutTargetGroup;
@Column(name = "address", length = 512)
@Size(max = 512)
private String address;
@Column(name = "last_target_query")
private Long lastTargetQuery;
@Column(name = "install_date")
private Long installationDate;
@Column(name = "update_status", nullable = false, length = 16)
@Enumerated(EnumType.STRING)
@NotNull
private TargetUpdateStatus updateStatus = TargetUpdateStatus.UNKNOWN;
@ManyToOne(optional = true, fetch = FetchType.LAZY)
@JoinColumn(name = "installed_distribution_set", nullable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_inst_ds"))
private JpaDistributionSet installedDistributionSet;
@ManyToOne(optional = true, fetch = FetchType.LAZY)
@JoinColumn(name = "assigned_distribution_set", nullable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_assign_ds"))
private JpaDistributionSet assignedDistributionSet;
/**
* Read only on management API. Are committed by controller.
*/
@CascadeOnDelete
@ElementCollection
@Column(name = "attribute_value", length = 128)
@MapKeyColumn(name = "attribute_key", nullable = false, length = 32)
@CollectionTable(name = "sp_target_attributes", joinColumns = {
@JoinColumn(name = "target_id", nullable = false, updatable = false) }, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_attrib_target"))
private final Map<String, String> controllerAttributes = Collections.synchronizedMap(new HashMap<String, String>());
// set default request controller attributes to true, because we want to
// request them the first
// time
@Column(name = "request_controller_attributes", nullable = false)
private boolean requestControllerAttributes = true;
/**
* Constructor.
*
@@ -140,14 +186,12 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Lon
this.controllerId = controllerId;
setName(controllerId);
this.securityToken = securityToken;
targetInfo = new JpaTargetInfo(this);
}
JpaTarget() {
// empty constructor for JPA.
}
@Override
public DistributionSet getAssignedDistributionSet() {
return assignedDistributionSet;
}
@@ -157,7 +201,6 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Lon
return controllerId;
}
@Override
public Set<TargetTag> getTags() {
if (tags == null) {
return Collections.emptySet();
@@ -198,7 +241,6 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Lon
this.controllerId = controllerId;
}
@Override
public List<Action> getActions() {
if (actions == null) {
return Collections.emptyList();
@@ -215,36 +257,6 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Lon
return actions.add((JpaAction) action);
}
@Override
@Transient
public boolean isNew() {
return entityNew;
}
/**
* @param entityNew
* the isNew to set
*/
public void setNew(final boolean entityNew) {
this.entityNew = entityNew;
}
/**
* @return the targetInfo
*/
@Override
public TargetInfo getTargetInfo() {
return targetInfo;
}
/**
* @param targetInfo
* the targetInfo to set
*/
public void setTargetInfo(final TargetInfo targetInfo) {
this.targetInfo = (JpaTargetInfo) targetInfo;
}
/**
* @return the securityToken if the current security context contains the
* necessary permission {@link SpPermission#READ_TARGET_SEC_TOKEN}
@@ -260,17 +272,110 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Lon
return null;
}
/**
* @param securityToken
* the securityToken to set
*/
public void setSecurityToken(final String securityToken) {
this.securityToken = securityToken;
}
/**
* @return the ipAddress
*/
@Override
public URI getAddress() {
if (address == null) {
return null;
}
try {
return URI.create(address);
} catch (final IllegalArgumentException e) {
LOG.warn("Invalid address provided. Cloud not be configured to URI", e);
return null;
}
}
/**
* @return the poll time which holds the last poll time of the target, the
* next poll time and the overdue time. In case the
* {@link #lastTargetQuery} is not set e.g. the target never polled
* before this method returns {@code null}
*/
@Override
public PollStatus getPollStatus() {
if (lastTargetQuery == null) {
return null;
}
return SystemSecurityContextHolder.getInstance().getSystemSecurityContext().runAsSystem(() -> {
final Duration pollTime = DurationHelper.formattedStringToDuration(TenantConfigurationManagementHolder
.getInstance().getTenantConfigurationManagement()
.getConfigurationValue(TenantConfigurationKey.POLLING_TIME_INTERVAL, String.class).getValue());
final Duration overdueTime = DurationHelper.formattedStringToDuration(
TenantConfigurationManagementHolder.getInstance().getTenantConfigurationManagement()
.getConfigurationValue(TenantConfigurationKey.POLLING_OVERDUE_TIME_INTERVAL, String.class)
.getValue());
final LocalDateTime currentDate = LocalDateTime.now();
final LocalDateTime lastPollDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(lastTargetQuery),
ZoneId.systemDefault());
final LocalDateTime nextPollDate = lastPollDate.plus(pollTime);
final LocalDateTime overdueDate = nextPollDate.plus(overdueTime);
return new PollStatus(lastPollDate, nextPollDate, overdueDate, currentDate);
});
}
@Override
public Long getLastTargetQuery() {
return lastTargetQuery;
}
@Override
public Long getInstallationDate() {
return installationDate;
}
@Override
public TargetUpdateStatus getUpdateStatus() {
return updateStatus;
}
public JpaDistributionSet getInstalledDistributionSet() {
return installedDistributionSet;
}
public Map<String, String> getControllerAttributes() {
return controllerAttributes;
}
@Override
public boolean isRequestControllerAttributes() {
return requestControllerAttributes;
}
@Override
public String toString() {
return "Target [controllerId=" + controllerId + ", getId()=" + getId() + "]";
return "JpaTarget [controllerId=" + controllerId + ", revision=" + getOptLockRevision() + ", id=" + getId()
+ "]";
}
public void setAddress(final String address) {
this.address = address;
}
public void setLastTargetQuery(final Long lastTargetQuery) {
this.lastTargetQuery = lastTargetQuery;
}
public void setInstallationDate(final Long installationDate) {
this.installationDate = installationDate;
}
public void setInstalledDistributionSet(final JpaDistributionSet installedDistributionSet) {
this.installedDistributionSet = installedDistributionSet;
}
public void setUpdateStatus(final TargetUpdateStatus updateStatus) {
this.updateStatus = updateStatus;
}
public void setRequestControllerAttributes(final boolean requestControllerAttributes) {
this.requestControllerAttributes = requestControllerAttributes;
}
@Override
@@ -279,6 +384,11 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Lon
.publishEvent(new TargetCreatedEvent(this, EventPublisherHolder.getInstance().getApplicationId()));
}
@Override
public List<String> getUpdateIgnoreFields() {
return TARGET_UPDATE_EVENT_IGNORE_FIELDS;
}
@Override
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher()

View File

@@ -1,310 +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 java.net.URI;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.CascadeType;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.MapKeyColumn;
import javax.persistence.MapsId;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.eclipse.hawkbit.repository.jpa.model.helper.SystemSecurityContextHolder;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.PollStatus;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Persistable;
/**
* A table which contains all the information inserted, updated by the
* controller itself. So this entity does not provide audit information because
* changes on this entity are mostly only done by controller requests. That's
* the reason that we store these information in a separated table so we don't
* modifying the {@link Target} itself when a controller reports it's
* {@link #lastTargetQuery} for example.
*
*/
@Table(name = "sp_target_info", indexes = {
@Index(name = "sp_idx_target_info_02", columnList = "target_id,update_status") })
@Entity
@EntityListeners(EntityPropertyChangeListener.class)
public class JpaTargetInfo implements Persistable<Long>, TargetInfo {
private static final long serialVersionUID = 1L;
private static final Logger LOG = LoggerFactory.getLogger(TargetInfo.class);
@Id
private Long targetId;
@Transient
private boolean entityNew;
@CascadeOnDelete
@OneToOne(cascade = { CascadeType.MERGE }, fetch = FetchType.LAZY, targetEntity = JpaTarget.class)
@JoinColumn(name = "target_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_stat_targ"))
@MapsId
private JpaTarget target;
@Column(name = "address", length = 512)
@Size(max = 512)
private String address;
@Column(name = "last_target_query")
private Long lastTargetQuery;
@Column(name = "install_date")
private Long installationDate;
@Column(name = "update_status", nullable = false, length = 16)
@Enumerated(EnumType.STRING)
@NotNull
private TargetUpdateStatus updateStatus = TargetUpdateStatus.UNKNOWN;
@ManyToOne(optional = true, fetch = FetchType.LAZY)
@JoinColumn(name = "installed_distribution_set", nullable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_inst_ds"))
private JpaDistributionSet installedDistributionSet;
/**
* Read only on management API. Are commited by controller.
*/
@CascadeOnDelete
@ElementCollection
@Column(name = "attribute_value", length = 128)
@MapKeyColumn(name = "attribute_key", nullable = false, length = 32)
@CollectionTable(name = "sp_target_attributes", joinColumns = {
@JoinColumn(name = "target_id", nullable = false, updatable = false) }, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_attrib_target"))
private final Map<String, String> controllerAttributes = Collections.synchronizedMap(new HashMap<String, String>());
// set default request controller attributes to true, because we want to
// request them the first
// time
@Column(name = "request_controller_attributes", nullable = false)
private boolean requestControllerAttributes = true;
/**
* Constructor for {@link JpaTargetInfo}.
*
* @param target
* related to this status.
*/
public JpaTargetInfo(final JpaTarget target) {
this.target = target;
targetId = target.getId();
}
JpaTargetInfo() {
target = null;
targetId = null;
}
@Override
public Long getId() {
return targetId;
}
@Override
@Transient
public boolean isNew() {
return entityNew;
}
/**
* @param entityNew
* the isNew to set
*/
public void setNew(final boolean entityNew) {
this.entityNew = entityNew;
}
/**
* @return the ipAddress
*/
@Override
public URI getAddress() {
if (address == null) {
return null;
}
try {
return URI.create(address);
} catch (final IllegalArgumentException e) {
LOG.warn("Invalid address provided. Cloud not be configured to URI", e);
return null;
}
}
public void setAddress(final String address) {
this.address = address;
}
public Long getTargetId() {
return targetId;
}
public void setTargetId(final Long targetId) {
this.targetId = targetId;
}
@Override
public Target getTarget() {
return target;
}
public void setTarget(final JpaTarget target) {
this.target = target;
}
@Override
public Long getLastTargetQuery() {
return lastTargetQuery;
}
public void setLastTargetQuery(final long lastTargetQuery) {
this.lastTargetQuery = lastTargetQuery;
}
public void setRequestControllerAttributes(final boolean requestControllerAttributes) {
this.requestControllerAttributes = requestControllerAttributes;
}
@Override
public Map<String, String> getControllerAttributes() {
return controllerAttributes;
}
@Override
public boolean isRequestControllerAttributes() {
return requestControllerAttributes;
}
@Override
public Long getInstallationDate() {
return installationDate;
}
public void setInstallationDate(final Long installationDate) {
this.installationDate = installationDate;
}
@Override
public TargetUpdateStatus getUpdateStatus() {
return updateStatus;
}
public void setUpdateStatus(final TargetUpdateStatus updateStatus) {
this.updateStatus = updateStatus;
}
@Override
public DistributionSet getInstalledDistributionSet() {
return installedDistributionSet;
}
public void setInstalledDistributionSet(final JpaDistributionSet installedDistributionSet) {
this.installedDistributionSet = installedDistributionSet;
}
/**
* @return the poll time which holds the last poll time of the target, the
* next poll time and the overdue time. In case the
* {@link #lastTargetQuery} is not set e.g. the target never polled
* before this method returns {@code null}
*/
@Override
public PollStatus getPollStatus() {
if (lastTargetQuery == null) {
return null;
}
return SystemSecurityContextHolder.getInstance().getSystemSecurityContext().runAsSystem(() -> {
final Duration pollTime = DurationHelper.formattedStringToDuration(TenantConfigurationManagementHolder
.getInstance().getTenantConfigurationManagement()
.getConfigurationValue(TenantConfigurationKey.POLLING_TIME_INTERVAL, String.class).getValue());
final Duration overdueTime = DurationHelper.formattedStringToDuration(
TenantConfigurationManagementHolder.getInstance().getTenantConfigurationManagement()
.getConfigurationValue(TenantConfigurationKey.POLLING_OVERDUE_TIME_INTERVAL, String.class)
.getValue());
final LocalDateTime currentDate = LocalDateTime.now();
final LocalDateTime lastPollDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(lastTargetQuery),
ZoneId.systemDefault());
final LocalDateTime nextPollDate = lastPollDate.plus(pollTime);
final LocalDateTime overdueDate = nextPollDate.plus(overdueTime);
return new PollStatus(lastPollDate, nextPollDate, overdueDate, currentDate);
});
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((target == null) ? 0 : target.hashCode());
result = prime * result + ((targetId == null) ? 0 : targetId.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof TargetInfo)) {
return false;
}
final JpaTargetInfo other = (JpaTargetInfo) obj;
if (target == null) {
if (other.target != null) {
return false;
}
} else if (!target.equals(other.target)) {
return false;
}
if (targetId == null) {
if (other.targetId != null) {
return false;
}
} else if (!targetId.equals(other.targetId)) {
return false;
}
return true;
}
}

View File

@@ -56,8 +56,8 @@ public class StartNextGroupRolloutGroupSuccessAction implements RolloutGroupActi
// retrieve all actions according to the parent group of the finished
// rolloutGroup, so retrieve all child-group actions which need to be
// started.
final long countOfStartedActions = deploymentManagement.startScheduledActionsByRolloutGroupParent(rollout,
rolloutGroup);
final long countOfStartedActions = deploymentManagement
.startScheduledActionsByRolloutGroupParent(rollout.getId(), rolloutGroup.getId());
logger.debug("{} Next actions started for rollout {} and parent group {}", countOfStartedActions, rollout,
rolloutGroup);
if (countOfStartedActions > 0) {

View File

@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.repository.jpa.specifications;
import java.util.Collection;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.ListJoin;
import javax.persistence.criteria.Path;
@@ -24,8 +23,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
@@ -200,10 +197,9 @@ public final class DistributionSetSpecification {
*/
public static Specification<JpaDistributionSet> installedTarget(final String installedTargetId) {
return (dsRoot, query, cb) -> {
final ListJoin<JpaDistributionSet, JpaTargetInfo> installedTargetJoin = dsRoot
final ListJoin<JpaDistributionSet, JpaTarget> installedTargetJoin = dsRoot
.join(JpaDistributionSet_.installedAtTargets, JoinType.INNER);
final Join<JpaTargetInfo, JpaTarget> targetJoin = installedTargetJoin.join(JpaTargetInfo_.target);
return cb.equal(targetJoin.get(JpaTarget_.controllerId), installedTargetId);
return cb.equal(installedTargetJoin.get(JpaTarget_.controllerId), installedTargetId);
};
}

View File

@@ -0,0 +1,50 @@
/**
* 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.specifications;
import javax.persistence.criteria.Join;
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.springframework.data.jpa.domain.Specification;
/**
* Specifications class for {@link Tag}s. The class provides Spring Data JPQL
* Specifications.
*
*/
public final class TagSpecification {
private TagSpecification() {
// utility class
}
/**
* {@link Specification} for retrieving {@link DistributionSet}s by its
* DELETED attribute.
*
* @param isDeleted
* TRUE/FALSE are compared to the attribute DELETED. If NULL the
* attribute is ignored
* @return the {@link DistributionSet} {@link Specification}
*/
public static Specification<JpaTargetTag> ofTarget(final String controllerId) {
return (targetRoot, query, criteriaBuilder) -> {
final Join<JpaTargetTag, JpaTarget> tagJoin = targetRoot.join(JpaTargetTag_.assignedToTargets);
query.distinct(true);
return criteriaBuilder.equal(tagJoin.get(JpaTarget_.controllerId), controllerId);
};
}
}

View File

@@ -12,7 +12,6 @@ import java.util.Collection;
import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.ListJoin;
import javax.persistence.criteria.Path;
@@ -27,8 +26,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
@@ -37,7 +34,6 @@ import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup_;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.springframework.data.jpa.domain.Specification;
@@ -101,20 +97,8 @@ public final class TargetSpecifications {
* join it.
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasTargetUpdateStatus(final Collection<TargetUpdateStatus> updateStatus,
final boolean fetch) {
return (targetRoot, query, cb) -> {
if (!query.getResultType().isAssignableFrom(Long.class)) {
if (fetch) {
targetRoot.fetch(JpaTarget_.targetInfo);
} else {
targetRoot.join(JpaTarget_.targetInfo);
}
return targetRoot.get(JpaTarget_.targetInfo).get(JpaTargetInfo_.updateStatus).in(updateStatus);
}
final Join<JpaTarget, JpaTargetInfo> targetInfoJoin = targetRoot.join(JpaTarget_.targetInfo);
return targetInfoJoin.get(JpaTargetInfo_.updateStatus).in(updateStatus);
};
public static Specification<JpaTarget> hasTargetUpdateStatus(final Collection<TargetUpdateStatus> updateStatus) {
return (targetRoot, query, cb) -> targetRoot.get(JpaTarget_.updateStatus).in(updateStatus);
}
/**
@@ -134,10 +118,8 @@ public final class TargetSpecifications {
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> isOverdue(final long overdueTimestamp) {
return (targetRoot, query, cb) -> {
final Join<JpaTarget, JpaTargetInfo> targetInfoJoin = targetRoot.join(JpaTarget_.targetInfo);
return cb.lessThanOrEqualTo(targetInfoJoin.get(JpaTargetInfo_.lastTargetQuery), overdueTimestamp);
};
return (targetRoot, query, cb) -> cb.lessThanOrEqualTo(targetRoot.get(JpaTarget_.lastTargetQuery),
overdueTimestamp);
}
/**
@@ -166,14 +148,11 @@ public final class TargetSpecifications {
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasInstalledOrAssignedDistributionSet(@NotNull final Long distributionId) {
return (targetRoot, query, cb) -> {
final Join<JpaTarget, JpaTargetInfo> targetInfoJoin = targetRoot.join(JpaTarget_.targetInfo);
return cb.or(
cb.equal(targetInfoJoin.get(JpaTargetInfo_.installedDistributionSet).get(JpaDistributionSet_.id),
distributionId),
cb.equal(targetRoot.<JpaDistributionSet> get(JpaTarget_.assignedDistributionSet)
.get(JpaDistributionSet_.id), distributionId));
};
return (targetRoot, query, cb) -> cb.or(
cb.equal(targetRoot.get(JpaTarget_.installedDistributionSet).get(JpaDistributionSet_.id),
distributionId),
cb.equal(targetRoot.<JpaDistributionSet> get(JpaTarget_.assignedDistributionSet)
.get(JpaDistributionSet_.id), distributionId));
}
/**
@@ -309,10 +288,7 @@ public final class TargetSpecifications {
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasInstalledDistributionSet(final Long distributionSetId) {
return (targetRoot, query, cb) -> {
final Join<JpaTarget, JpaTargetInfo> targetInfoJoin = targetRoot.join(JpaTarget_.targetInfo);
return cb.equal(targetInfoJoin.get(JpaTargetInfo_.installedDistributionSet).get(JpaDistributionSet_.id),
distributionSetId);
};
return (targetRoot, query, cb) -> cb.equal(
targetRoot.get(JpaTarget_.installedDistributionSet).get(JpaDistributionSet_.id), distributionSetId);
}
}

View File

@@ -0,0 +1,32 @@
ALTER TABLE sp_target ADD COLUMN install_date bigint;
ALTER TABLE sp_target ADD COLUMN address varchar(512);
ALTER TABLE sp_target ADD COLUMN last_target_query bigint;
ALTER TABLE sp_target ADD COLUMN request_controller_attributes bit not null;
ALTER TABLE sp_target ADD COLUMN update_status varchar(16) not null;
ALTER TABLE sp_target ADD COLUMN installed_distribution_set bigint;
UPDATE sp_target t SET install_date=(SELECT i.install_date FROM sp_target_info i WHERE t.id = i.target_id);
UPDATE sp_target t SET address=(SELECT i.address FROM sp_target_info i WHERE t.id = i.target_id);
UPDATE sp_target t SET last_target_query=(SELECT i.last_target_query FROM sp_target_info i WHERE t.id = i.target_id);
UPDATE sp_target t SET request_controller_attributes=(SELECT i.request_controller_attributes FROM sp_target_info i WHERE t.id = i.target_id);
UPDATE sp_target t SET update_status=(SELECT i.update_status FROM sp_target_info i WHERE t.id = i.target_id);
UPDATE sp_target t SET installed_distribution_set=(SELECT i.installed_distribution_set FROM sp_target_info i WHERE t.id = i.target_id);
ALTER TABLE sp_target_attributes DROP CONSTRAINT fk_targ_attrib_target;
ALTER TABLE sp_target_attributes
ADD CONSTRAINT fk_targ_attrib_target
FOREIGN KEY (target_id)
REFERENCES sp_target (id)
ON DELETE cascade;
ALTER TABLE sp_target_info DROP CONSTRAINT fk_target_inst_ds;
ALTER TABLE sp_target
ADD CONSTRAINT fk_target_inst_ds
FOREIGN KEY (installed_distribution_set)
REFERENCES sp_distribution_set (id);
ALTER TABLE sp_target_info DROP CONSTRAINT fk_targ_stat_targ;
ALTER TABLE sp_target_info DROP INDEX sp_idx_target_info_02;
CREATE INDEX sp_idx_target_05 ON sp_target (tenant,address);
DROP TABLE sp_target_info;

View File

@@ -0,0 +1,32 @@
ALTER TABLE sp_target ADD COLUMN install_date bigint;
ALTER TABLE sp_target ADD COLUMN address varchar(512);
ALTER TABLE sp_target ADD COLUMN last_target_query bigint;
ALTER TABLE sp_target ADD COLUMN request_controller_attributes bit not null;
ALTER TABLE sp_target ADD COLUMN update_status varchar(16) not null;
ALTER TABLE sp_target ADD COLUMN installed_distribution_set bigint;
UPDATE sp_target AS t INNER JOIN sp_target_info AS i
ON t.id = i.target_id
SET t.install_date = i.install_date, t.address = i.address,t.last_target_query = i.last_target_query,
t.request_controller_attributes = i.request_controller_attributes,t.update_status = i.update_status,
t.installed_distribution_set = i.installed_distribution_set;
ALTER TABLE sp_target_info DROP INDEX sp_idx_target_info_02;
CREATE INDEX sp_idx_target_05 ON sp_target (tenant,address);
ALTER TABLE sp_target_attributes DROP FOREIGN KEY fk_targ_attrib_target;
ALTER TABLE sp_target_attributes
ADD CONSTRAINT fk_targ_attrib_target
FOREIGN KEY (target_id)
REFERENCES sp_target (id)
ON DELETE cascade;
ALTER TABLE sp_target_info DROP FOREIGN KEY fk_target_inst_ds;
ALTER TABLE sp_target
ADD CONSTRAINT fk_target_inst_ds
FOREIGN KEY (installed_distribution_set)
REFERENCES sp_distribution_set (id);
ALTER TABLE sp_target_info DROP FOREIGN KEY fk_targ_stat_targ;
DROP TABLE sp_target_info;