Started with #100 by means of splitting the management services into API

and Jpa implementation.

Signed-off-by: Kai Zimmermann <kai.zimmermann@bosch-si.com>
This commit is contained in:
Kai Zimmermann
2016-05-13 08:44:34 +02:00
parent 02e2608b40
commit 6654d71969
135 changed files with 4711 additions and 4055 deletions

View File

@@ -12,8 +12,8 @@ import java.util.HashMap;
import java.util.Map;
import org.eclipse.hawkbit.aspects.ExceptionMappingAspectHandler;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.jpa.SystemManagement;
import org.eclipse.hawkbit.repository.model.helper.AfterTransactionCommitExecutorHolder;
import org.eclipse.hawkbit.repository.model.helper.CacheManagerHolder;
import org.eclipse.hawkbit.repository.model.helper.SecurityTokenGeneratorHolder;

View File

@@ -19,7 +19,7 @@ import org.eclipse.hawkbit.eventbus.event.TargetCreatedEvent;
import org.eclipse.hawkbit.eventbus.event.TargetDeletedEvent;
import org.eclipse.hawkbit.eventbus.event.TargetInfoUpdateEvent;
import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.TargetRepository;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
@@ -66,7 +66,7 @@ public class EntityChangeEventListener {
* in case exception happens in the
* {@link ProceedingJoinPoint#proceed()}
*/
@Around("execution(* org.eclipse.hawkbit.repository.TargetInfoRepository.save(..))")
@Around("execution(* org.eclipse.hawkbit.repository.jpa.TargetInfoRepository.save(..))")
// Exception squid:S00112 - Is aspectJ proxy
@SuppressWarnings({ "squid:S00112" })
public Object targetCreated(final ProceedingJoinPoint joinpoint) throws Throwable {
@@ -93,7 +93,7 @@ public class EntityChangeEventListener {
* in case exception happens in the
* {@link ProceedingJoinPoint#proceed()}
*/
@Around("execution(* org.eclipse.hawkbit.repository.TargetRepository.deleteByIdIn(..))")
@Around("execution(* org.eclipse.hawkbit.repository.jpa.TargetRepository.deleteByIdIn(..))")
// Exception squid:S00112 - Is aspectJ proxy
@SuppressWarnings({ "squid:S00112" })
public Object targetDeletedById(final ProceedingJoinPoint joinpoint) throws Throwable {
@@ -115,8 +115,8 @@ public class EntityChangeEventListener {
* in case exception happens in the
* {@link ProceedingJoinPoint#proceed()}
*/
@Around("execution(* org.eclipse.hawkbit.repository.TargetRepository.delete(..))")
// Exception squid:S00112 - Is aspectJ proxy
@Around("execution(* org.eclipse.hawkbit.repository.jpa.TargetRepository.delete(..))")
// Exception squid:S00112 - Is aspectJ proxy
@SuppressWarnings({ "squid:S00112", "unchecked" })
public Object targetDeleted(final ProceedingJoinPoint joinpoint) throws Throwable {
final String currentTenant = tenantAware.getCurrentTenant();
@@ -146,7 +146,7 @@ public class EntityChangeEventListener {
afterCommit.afterCommit(() -> eventBus.post(new TargetDeletedEvent(tenant, targetId)));
}
private boolean isTargetInfoNew(final Object targetInfo) {
private static boolean isTargetInfoNew(final Object targetInfo) {
return ((TargetInfo) targetInfo).isNew();
}

View File

@@ -13,11 +13,7 @@ import java.util.List;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
import org.eclipse.hawkbit.artifact.repository.ArtifactStoreException;
import org.eclipse.hawkbit.artifact.repository.HashNotMatchException;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException;
import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException;
@@ -31,47 +27,110 @@ import org.eclipse.hawkbit.repository.model.ExternalArtifact;
import org.eclipse.hawkbit.repository.model.ExternalArtifactProvider;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.specifications.SoftwareModuleSpecification;
import org.hibernate.validator.constraints.NotEmpty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.hateoas.Identifiable;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
/**
* Service for {@link Artifact} management operations.
*
*/
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated
@Service
public class ArtifactManagement {
public interface ArtifactManagement {
private static final Logger LOG = LoggerFactory.getLogger(ArtifactManagement.class);
/**
* Creates {@link ExternalArtifact} based on given provider.
*
* @param externalRepository
* the artifact is located in
* @param urlSuffix
* of the artifact
* {@link ExternalArtifactProvider#getDefaultSuffix()} is used if
* empty.
* @param moduleId
* to assign the artifact to
*
* @return created {@link ExternalArtifact}
*
* @throws EntityNotFoundException
* if {@link SoftwareModule} with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
ExternalArtifact createExternalArtifact(@NotNull ExternalArtifactProvider externalRepository, String urlSuffix,
@NotNull Long moduleId);
@Autowired
private LocalArtifactRepository localArtifactRepository;
/**
* Persists {@link ExternalArtifactProvider} based on given properties.
*
* @param name
* of the provided
* @param description
* which is optional
* @param basePath
* of all {@link ExternalArtifact}s of the provider
* @param defaultUrlSuffix
* that is used if {@link ExternalArtifact#getUrlSuffix()} is
* empty.
* @return created {@link ExternalArtifactProvider}
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
ExternalArtifactProvider createExternalArtifactProvider(@NotEmpty String name, String description,
@NotNull String basePath, String defaultUrlSuffix);
@Autowired
private ExternalArtifactRepository externalArtifactRepository;
/**
* Persists artifact binary as provided by given InputStream. assign the
* artifact in addition to given {@link SoftwareModule}.
*
* @param inputStream
* to read from for artifact binary
* @param moduleId
* to assign the new artifact to
* @param filename
* of the artifact
* @param overrideExisting
* to <code>true</code> if the artifact binary can be overridden
* if it already exists
*
* @return uploaded {@link LocalArtifact}
*
* @throw ArtifactUploadFailedException if upload fails
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
default LocalArtifact createLocalArtifact(final InputStream inputStream, final Long moduleId, final String filename,
final boolean overrideExisting) {
return createLocalArtifact(inputStream, moduleId, filename, null, null, overrideExisting, null);
}
@Autowired
private SoftwareModuleRepository softwareModuleRepository;
@Autowired
private ExternalArtifactProviderRepository externalArtifactProviderRepository;
@Autowired
private ArtifactRepository artifactRepository;
/**
* Persists artifact binary as provided by given InputStream. assign the
* artifact in addition to given {@link SoftwareModule}.
*
* @param inputStream
* to read from for artifact binary
* @param moduleId
* to assign the new artifact to
* @param filename
* of the artifact
* @param overrideExisting
* to <code>true</code> if the artifact binary can be overridden
* if it already exists
* @param contentType
* the contentType of the file
*
* @return uploaded {@link LocalArtifact}
*
* @throw ArtifactUploadFailedException if upload fails
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
default LocalArtifact createLocalArtifact(final InputStream inputStream, final Long moduleId, final String filename,
final boolean overrideExisting, final String contentType) {
return createLocalArtifact(inputStream, moduleId, filename, null, null, overrideExisting, contentType);
}
/**
* Persists artifact binary as provided by given InputStream. assign the
@@ -105,59 +164,112 @@ public class ArtifactManagement {
* @throws InvalidSHA1HashException
* if check against provided SHA1 checksum failed
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public LocalArtifact createLocalArtifact(@NotNull final InputStream stream, @NotNull final Long moduleId,
@NotEmpty final String filename, final String providedMd5Sum, final String providedSha1Sum,
final boolean overrideExisting, final String contentType) {
DbArtifact result = null;
LocalArtifact createLocalArtifact(@NotNull InputStream stream, @NotNull Long moduleId, @NotEmpty String filename,
String providedMd5Sum, String providedSha1Sum, boolean overrideExisting, String contentType);
final SoftwareModule softwareModule = getModuleAndThrowExceptionIfThatFails(moduleId);
/**
* Deletes {@link Artifact} based on given id.
*
* @param id
* of the {@link Artifact} that has to be deleted.
* @throws ArtifactDeleteFailedException
* if deletion failed (MongoDB is not available)
*
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
void deleteExternalArtifact(@NotNull Long id);
final LocalArtifact existing = checkForExistingArtifact(filename, overrideExisting, softwareModule);
/**
* Deletes a local artifact.
*
* @param existing
* the related local artifact
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
void deleteLocalArtifact(@NotNull LocalArtifact existing);
try {
result = artifactRepository.store(stream, filename, contentType,
new DbArtifactHash(providedSha1Sum, providedMd5Sum));
} catch (final ArtifactStoreException e) {
throw new ArtifactUploadFailedException(e);
} catch (final HashNotMatchException e) {
if (e.getHashFunction().equals(HashNotMatchException.SHA1)) {
throw new InvalidSHA1HashException(e.getMessage(), e);
} else {
throw new InvalidMD5HashException(e.getMessage(), e);
}
}
if (result == null) {
return null;
}
/**
* Deletes {@link Artifact} based on given id.
*
* @param id
* of the {@link Artifact} that has to be deleted.
* @throws ArtifactDeleteFailedException
* if deletion failed (MongoDB is not available)
*
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
void deleteLocalArtifact(@NotNull Long id);
return storeArtifactMetadata(softwareModule, filename, result, existing);
}
/**
* Searches for {@link Artifact} with given {@link Identifiable}.
*
* @param id
* to search for
* @return found {@link Artifact} or <code>null</code> is it could not be
* found.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Artifact findArtifact(@NotNull Long id);
private static LocalArtifact checkForExistingArtifact(final String filename, final boolean overrideExisting,
final SoftwareModule softwareModule) {
if (softwareModule.getLocalArtifactByFilename(filename).isPresent()) {
if (overrideExisting) {
LOG.debug("overriding existing artifact with new filename {}", filename);
return softwareModule.getLocalArtifactByFilename(filename).get();
} else {
throw new EntityAlreadyExistsException("File with that name already exists in the Software Module");
}
}
return null;
}
/**
* Find by artifact by software module id and filename.
*
* @param filename
* file name
* @param softwareModuleId
* software module id.
* @return LocalArtifact if artifact present
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER)
List<LocalArtifact> findByFilenameAndSoftwareModule(@NotNull String filename, @NotNull Long softwareModuleId);
private SoftwareModule getModuleAndThrowExceptionIfThatFails(final Long moduleId) {
final SoftwareModule softwareModule = findSoftwareModuleWithDetails(moduleId);
/**
* Find all local artifact by sha1 and return the first artifact.
*
* @param sha1
* the sha1
* @return the first local artifact
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER)
LocalArtifact findFirstLocalArtifactsBySHA1(@NotNull String sha1);
if (softwareModule == null) {
LOG.debug("no software module with ID {} exists", moduleId);
throw new EntityNotFoundException("Software Module: " + moduleId);
}
return softwareModule;
}
/**
* Searches for {@link Artifact} with given file name.
*
* @param filename
* to search for
* @return found List of {@link LocalArtifact}s.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER)
List<LocalArtifact> findLocalArtifactByFilename(@NotNull String filename);
/**
* Get local artifact for a base software module.
*
* @param pageReq
* Pageable
* @param swId
* software module id
* @return Page<LocalArtifact>
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Page<LocalArtifact> findLocalArtifactBySoftwareModule(@NotNull Pageable pageReq, @NotNull Long swId);
/**
* Finds {@link SoftwareModule} by given id.
*
* @param id
* to search for
* @return the found {@link SoftwareModule}s or <code>null</code> if not
* found.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER)
SoftwareModule findSoftwareModuleById(@NotNull Long id);
/**
* Retrieves software module including details (
@@ -170,7 +282,7 @@ public class ArtifactManagement {
* @return the found {@link SoftwareModule}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
protected SoftwareModule findSoftwareModuleWithDetails(@NotNull final Long id) {
default SoftwareModule findSoftwareModuleWithDetails(@NotNull final Long id) {
final SoftwareModule result = findSoftwareModuleById(id);
if (result != null) {
result.getArtifacts().size();
@@ -179,211 +291,6 @@ public class ArtifactManagement {
return result;
}
/**
* Find all local artifact by sha1 and return the first artifact.
*
* @param sha1
* the sha1
* @return the first local artifact
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER)
public LocalArtifact findFirstLocalArtifactsBySHA1(final String sha1) {
return localArtifactRepository.findFirstByGridFsFileName(sha1);
}
/**
* Finds {@link SoftwareModule} by given id.
*
* @param id
* to search for
* @return the found {@link SoftwareModule}s or <code>null</code> if not
* found.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER)
protected SoftwareModule findSoftwareModuleById(@NotNull final Long id) {
final Specification<SoftwareModule> spec = SoftwareModuleSpecification.byId(id);
return softwareModuleRepository.findOne(spec);
}
private LocalArtifact storeArtifactMetadata(final SoftwareModule softwareModule, final String providedFilename,
final DbArtifact result, final LocalArtifact existing) {
LocalArtifact artifact = existing;
if (existing == null) {
artifact = new LocalArtifact(result.getHashes().getSha1(), providedFilename, softwareModule);
}
artifact.setMd5Hash(result.getHashes().getMd5());
artifact.setSha1Hash(result.getHashes().getSha1());
artifact.setSize(result.getSize());
LOG.debug("storing new artifact into repository {}", artifact);
return localArtifactRepository.save(artifact);
}
/**
* Persists {@link ExternalArtifactProvider} based on given properties.
*
* @param name
* of the provided
* @param description
* which is optional
* @param basePath
* of all {@link ExternalArtifact}s of the provider
* @param defaultUrlSuffix
* that is used if {@link ExternalArtifact#getUrlSuffix()} is
* empty.
* @return created {@link ExternalArtifactProvider}
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public ExternalArtifactProvider createExternalArtifactProvider(@NotEmpty final String name,
final String description, @NotNull final String basePath, final String defaultUrlSuffix) {
return externalArtifactProviderRepository
.save(new ExternalArtifactProvider(name, description, basePath, defaultUrlSuffix));
}
/**
* Creates {@link ExternalArtifact} based on given provider.
*
* @param externalRepository
* the artifact is located in
* @param urlSuffix
* of the artifact
* {@link ExternalArtifactProvider#getDefaultSuffix()} is used if
* empty.
* @param moduleId
* to assign the artifact to
*
* @return created {@link ExternalArtifact}
*
* @throws EntityNotFoundException
* if {@link SoftwareModule} with given ID does not exist
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public ExternalArtifact createExternalArtifact(@NotNull final ExternalArtifactProvider externalRepository,
final String urlSuffix, @NotNull final Long moduleId) {
final SoftwareModule module = getModuleAndThrowExceptionIfThatFails(moduleId);
return externalArtifactRepository.save(new ExternalArtifact(externalRepository, urlSuffix, module));
}
/**
* Deletes {@link Artifact} based on given id.
*
* @param id
* of the {@link Artifact} that has to be deleted.
* @throws ArtifactDeleteFailedException
* if deletion failed (MongoDB is not available)
*
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
public void deleteLocalArtifact(@NotNull final Long id) {
final LocalArtifact existing = localArtifactRepository.findOne(id);
if (null == existing) {
return;
}
deleteGridFsArtifact(existing);
existing.getSoftwareModule().removeArtifact(existing);
softwareModuleRepository.save(existing.getSoftwareModule());
localArtifactRepository.delete(id);
}
/**
* Delete a grid fs file.
*
* @param existing
* the related local artifact
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
public void deleteGridFsArtifact(@NotNull final LocalArtifact existing) {
if (existing == null) {
return;
}
boolean artifactIsOnlyUsedByOneSoftwareModule = true;
for (final LocalArtifact lArtifact : localArtifactRepository
.findByGridFsFileName(existing.getGridFsFileName())) {
if (!lArtifact.getSoftwareModule().isDeleted()
&& Long.compare(lArtifact.getSoftwareModule().getId(), existing.getSoftwareModule().getId()) != 0) {
artifactIsOnlyUsedByOneSoftwareModule = false;
break;
}
}
if (artifactIsOnlyUsedByOneSoftwareModule) {
try {
LOG.debug("deleting artifact from repository {}", existing.getGridFsFileName());
artifactRepository.deleteBySha1(existing.getGridFsFileName());
} catch (final ArtifactStoreException e) {
throw new ArtifactDeleteFailedException(e);
}
}
}
/**
* Deletes {@link Artifact} based on given id.
*
* @param id
* of the {@link Artifact} that has to be deleted.
* @throws ArtifactDeleteFailedException
* if deletion failed (MongoDB is not available)
*
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
public void deleteExternalArtifact(@NotNull final Long id) {
final ExternalArtifact existing = externalArtifactRepository.findOne(id);
if (null == existing) {
return;
}
existing.getSoftwareModule().removeArtifact(existing);
softwareModuleRepository.save(existing.getSoftwareModule());
externalArtifactRepository.delete(id);
}
/**
* Searches for {@link Artifact} with given file name.
*
* @param filename
* to search for
* @return found List of {@link LocalArtifact}s.
*/
@NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER)
public List<LocalArtifact> findLocalArtifactByFilename(@NotNull final String filename) {
return localArtifactRepository.findByFilename(filename);
}
/**
* Searches for {@link Artifact} with given {@link Identifiable}.
*
* @param id
* to search for
* @return found {@link Artifact} or <code>null</code> is it could not be
* found.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Artifact findArtifact(@NotNull final Long id) {
return localArtifactRepository.findOne(id);
}
/**
* Loads {@link org.eclipse.hawkbit.artifact.server.json.model.Artifact}
* from store for given {@link LocalArtifact}.
@@ -398,98 +305,6 @@ public class ArtifactManagement {
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DOWNLOAD_ARTIFACT + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.HAS_CONTROLLER_DOWNLOAD)
public DbArtifact loadLocalArtifactBinary(@NotNull final LocalArtifact artifact) {
final DbArtifact result = artifactRepository.getArtifactBySha1(artifact.getGridFsFileName());
if (result == null) {
throw new GridFSDBFileNotFoundException(artifact.getGridFsFileName());
}
DbArtifact loadLocalArtifactBinary(@NotNull LocalArtifact artifact);
return result;
}
/**
* Persists artifact binary as provided by given InputStream. assign the
* artifact in addition to given {@link SoftwareModule}.
*
* @param inputStream
* to read from for artifact binary
* @param moduleId
* to assign the new artifact to
* @param filename
* of the artifact
* @param overrideExisting
* to <code>true</code> if the artifact binary can be overridden
* if it already exists
* @param contentType
* the contentType of the file
*
* @return uploaded {@link LocalArtifact}
*
* @throw ArtifactUploadFailedException if upload fails
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public LocalArtifact createLocalArtifact(final InputStream inputStream, final Long moduleId, final String filename,
final boolean overrideExisting, final String contentType) {
return createLocalArtifact(inputStream, moduleId, filename, null, null, overrideExisting, contentType);
}
/**
* Persists artifact binary as provided by given InputStream. assign the
* artifact in addition to given {@link SoftwareModule}.
*
* @param inputStream
* to read from for artifact binary
* @param moduleId
* to assign the new artifact to
* @param filename
* of the artifact
* @param overrideExisting
* to <code>true</code> if the artifact binary can be overdiden
* if it already exists
*
* @return uploaded {@link LocalArtifact}
*
* @throw ArtifactUploadFailedException if upload failes
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public LocalArtifact createLocalArtifact(final InputStream inputStream, final Long moduleId, final String filename,
final boolean overrideExisting) {
return createLocalArtifact(inputStream, moduleId, filename, null, null, overrideExisting, null);
}
/**
* Get local artifact for a base software module.
*
* @param pageReq
* Pageable
* @param swId
* software module id
* @return Page<LocalArtifact>
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
public Page<LocalArtifact> findLocalArtifactBySoftwareModule(@NotNull final Pageable pageReq,
@NotNull final Long swId) {
return localArtifactRepository.findBySoftwareModuleId(pageReq, swId);
}
/**
* Find by artifact by software module id and filename.
*
* @param filename
* file name
* @param softwareModuleId
* software module id.
* @return LocalArtifact if artifact present
*/
@NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER)
public List<LocalArtifact> findByFilenameAndSoftwareModule(@NotNull final String filename,
@NotNull final Long softwareModuleId) {
return localArtifactRepository.findByFilenameAndSoftwareModuleId(filename, softwareModuleId);
}
}
}

View File

@@ -12,10 +12,6 @@ import java.net.URI;
import java.util.List;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
@@ -26,78 +22,102 @@ import org.eclipse.hawkbit.repository.exception.ToManyStatusEntriesException;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.ActionStatus_;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
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.repository.model.Target_;
import org.eclipse.hawkbit.repository.model.TenantConfiguration;
import org.eclipse.hawkbit.repository.specifications.ActionSpecifications;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
import org.hibernate.validator.constraints.NotEmpty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
/**
* Service layer for all operations of the controller API (with access
* permissions only for the controller).
*
*
* Service layer for all operations of the DDI API (with access permissions only
* for the controller).
*
*/
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated
@Service
public class ControllerManagement {
private static final Logger LOG = LoggerFactory.getLogger(ControllerManagement.class);
private static final Logger LOG_DOS = LoggerFactory.getLogger("server-security.dos");
public interface ControllerManagement {
public static final String SERVER_MESSAGE_PREFIX = "Update Server: ";
String SERVER_MESSAGE_PREFIX = "Update Server: ";
@Autowired
private EntityManager entityManager;
/**
* Simple addition of a new {@link ActionStatus} entry to the {@link Action}
* . No state changes.
*
* @param statusMessage
* to add to the action
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
void addInformationalActionStatus(@NotNull ActionStatus statusMessage);
@Autowired
private ActionRepository actionRepository;
/**
* Adds an {@link ActionStatus} for a cancel {@link Action} including
* potential state changes for the target and the {@link Action} itself.
*
* @param actionStatus
* to be added
* @return the persisted {@link Action}
*
* @throws EntityAlreadyExistsException
* if a given entity already exists
*
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
Action addCancelActionStatus(@NotNull ActionStatus actionStatus);
@Autowired
private TargetRepository targetRepository;
/**
* Adds an {@link ActionStatus} entry for an update {@link Action} including
* potential state changes for the target and the {@link Action} itself.
*
* @param actionStatus
* to be added
* @return the updated {@link Action}
*
* @throws EntityAlreadyExistsException
* if a given entity already exists
* @throws ToManyStatusEntriesException
* if more than the allowed number of status entries are
* inserted
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
Action addUpdateActionStatus(@NotNull ActionStatus actionStatus);
@Autowired
private TargetManagement targetManagement;
/**
* Retrieves all {@link Action}s which are active and assigned to a
* {@link Target}.
*
* @param target
* the target to retrieve the actions from
* @return a list of actions assigned to given target which are active
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
List<Action> findActionByTargetAndActive(@NotNull Target target);
@Autowired
private DeploymentManagement deploymentManagement;
/**
* Get the {@link Action} entity for given actionId with all lazy
* attributes.
*
* @param actionId
* to be id of the action
* @return the corresponding {@link Action}
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
Action findActionWithDetails(@NotNull Long actionId);
@Autowired
private TargetInfoRepository targetInfoRepository;
@Autowired
private SoftwareModuleRepository softwareModuleRepository;
@Autowired
private ActionStatusRepository actionStatusRepository;
@Autowired
private HawkbitSecurityProperties securityProperties;
@Autowired
private TenantConfigurationRepository tenantConfigurationRepository;
@Autowired
private TenantConfigurationManagement tenantConfigurationManagement;
/**
* register new target in the repository (plug-and-play).
*
* @param controllerId
* reference
* @param address
* the client IP address of the target, might be {@code null}
* @return target reference
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
Target findOrRegisterTargetIfItDoesNotexist(@NotEmpty String controllerId, URI address);
/**
* Retrieves all {@link SoftwareModule}s which are assigned to the given
@@ -110,46 +130,13 @@ public class ControllerManagement {
* {@code distributionSet}
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public String findPollingTime() {
final TenantConfigurationKey configurationKey = TenantConfigurationKey.POLLING_TIME_INTERVAL;
final Class<String> propertyType = String.class;
tenantConfigurationManagement.validateTenantConfigurationDataType(configurationKey, propertyType);
final TenantConfiguration tenantConfiguration = tenantConfigurationRepository
.findByKey(configurationKey.getKeyName());
return tenantConfigurationManagement
.buildTenantConfigurationValueByKey(configurationKey, propertyType, tenantConfiguration).getValue();
}
/**
* Refreshes the time of the last time the controller has been connected to
* the server.
*
* @param targetid
* of the target to to update
* @param address
* the client address of the target, might be {@code null}
* @return the updated target
*
* @throws EntityNotFoundException
* if target with given ID could not be found
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public Target updateLastTargetQuery(@NotEmpty final String targetid, final URI address) {
final Target target = targetRepository.findByControllerId(targetid);
if (target == null) {
throw new EntityNotFoundException(targetid);
}
return updateLastTargetQuery(target.getTargetInfo(), address).getTarget();
}
List<SoftwareModule> findSoftwareModulesByDistributionSet(@NotNull DistributionSet distributionSet);
/**
* Retrieves last {@link UpdateAction} for a download of an artifact of
* given module and target.
*
* @param targetId
* @param controllerId
* to look for
* @param module
* that should be assigned to the target
@@ -159,17 +146,28 @@ public class ControllerManagement {
* if action for given combination could not be found
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public Action getActionForDownloadByTargetAndSoftwareModule(@NotNull final String targetId,
@NotNull final SoftwareModule module) {
final List<Action> action = actionRepository.findActionByTargetAndSoftwareModule(targetId, module);
Action getActionForDownloadByTargetAndSoftwareModule(@NotEmpty String controllerId, @NotNull SoftwareModule module);
if (action.isEmpty() || action.get(0).isCancelingOrCanceled()) {
throw new EntityNotFoundException(
"No assigment found for module " + module.getId() + " to target " + targetId);
}
/**
* @return current {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
String getPollingTime();
return action.get(0);
}
/**
* An direct access to the security token of an
* {@link Target#getSecurityToken()} without authorization. This is
* necessary to be able to access the security-token without any
* security-context information because the security-token is used for
* authentication.
*
* @param controllerId
* the ID of the controller to retrieve the security token for
* @return the security context of the target, in case no target exists for
* the given controllerId {@code null} is returned
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
String getSecurityTokenByControllerId(@NotEmpty String controllerId);
/**
* Checks if a given target has currently or has even been assigned to the
@@ -178,7 +176,7 @@ public class ControllerManagement {
* assigned or had ever been assigned to the target and so it's visible to a
* specific target e.g. for downloading.
*
* @param targetId
* @param controllerId
* the ID of the target to check
* @param localArtifact
* the artifact to verify if the given target had even been
@@ -187,14 +185,56 @@ public class ControllerManagement {
* relation to the given artifact through the action history,
* otherwise {@code false}
*/
public boolean hasTargetArtifactAssigned(@NotNull final String targetId,
@NotNull final LocalArtifact localArtifact) {
final Target target = targetRepository.findByControllerId(targetId);
if (target == null) {
return false;
}
return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(target, localArtifact)) > 0;
}
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
boolean hasTargetArtifactAssigned(@NotNull String controllerId, @NotNull LocalArtifact localArtifact);
/**
* Registers retrieved status for given {@link Target} and {@link Action} if
* it does not exist yet.
*
* @param action
* to the handle status for
* @param message
* for the status
* @return the update action in case the status has been changed to
* {@link Status#RETRIEVED}
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
Action registerRetrieved(@NotNull Action action, String message);
/**
* Updates attributes of the controller.
*
* @param controllerId
* to update
* @param attributes
* to insert
*
* @return updated {@link Target}
*
* @throws EntityNotFoundException
* if target that has to be updated could not be found
* @throws ToManyAttributeEntriesException
* if maximum
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
Target updateControllerAttributes(@NotEmpty String controllerId, @NotNull Map<String, String> attributes);
/**
* Refreshes the time of the last time the controller has been connected to
* the server.
*
* @param controllerId
* of the target to to update
* @param address
* the client address of the target, might be {@code null}
* @return the updated target
*
* @throws EntityNotFoundException
* if target with given ID could not be found
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
Target updateLastTargetQuery(@NotEmpty String controllerId, URI address);
/**
* Refreshes the time of the last time the controller has been connected to
@@ -207,83 +247,12 @@ public class ControllerManagement {
* @return the updated target
*
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public TargetInfo updateLastTargetQuery(@NotNull final TargetInfo target, final URI address) {
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
default TargetInfo updateLastTargetQuery(@NotNull final TargetInfo target, @NotNull final URI address) {
return updateTargetStatus(target, null, System.currentTimeMillis(), address);
}
/**
* Retrieves all {@link Action}s which are active and assigned to a
* {@link Target}.
*
* @param target
* the target to retrieve the actions from
* @return a list of actions assigned to given target which are active
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public List<Action> findActionByTargetAndActive(final Target target) {
return actionRepository.findByTargetAndActiveOrderByIdAsc(target, true);
}
/**
* Retrieves all {@link SoftwareModule}s which are assigned to the given
* {@link DistributionSet}.
*
* @param distributionSet
* the distribution set which should be assigned to the returned
* {@link SoftwareModule}s
* @return a list of {@link SoftwareModule}s assigned to given
* {@code distributionSet}
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public List<SoftwareModule> findSoftwareModulesByDistributionSet(final DistributionSet distributionSet) {
return softwareModuleRepository.findByAssignedTo(distributionSet);
}
/**
* Get the {@link Action} entity for given actionId with all lazy
* attributes.
*
* @param actionId
* to be id of the action
* @return the corresponding {@link Action}
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public Action findActionWithDetails(@NotNull final Long actionId) {
return actionRepository.findById(actionId);
}
/**
* register new target in the repository (plug-and-play).
*
* @param targetid
* reference
* @param address
* the client IP address of the target, might be {@code null}
* @return target reference
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public Target findOrRegisterTargetIfItDoesNotexist(@NotEmpty final String targetid, final URI address) {
final Specification<Target> spec = (targetRoot, query, cb) -> cb.equal(targetRoot.get(Target_.controllerId),
targetid);
Target target = targetRepository.findOne(spec);
if (target == null) {
target = new Target(targetid);
target.setDescription("Plug and Play target: " + targetid);
target.setName(targetid);
return targetManagement.createTarget(target, TargetUpdateStatus.REGISTERED, System.currentTimeMillis(),
address);
}
return updateLastTargetQuery(target.getTargetInfo(), address).getTarget();
}
/**
* Update selective the target status of a given {@code target}.
*
@@ -300,313 +269,8 @@ public class ControllerManagement {
* the client address of the target, might be {@code null}
* @return the updated TargetInfo
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public TargetInfo updateTargetStatus(@NotNull final TargetInfo targetInfo, final TargetUpdateStatus status,
final Long lastTargetQuery, final URI address) {
final TargetInfo mtargetInfo = entityManager.merge(targetInfo);
if (status != null) {
mtargetInfo.setUpdateStatus(status);
}
if (lastTargetQuery != null) {
mtargetInfo.setLastTargetQuery(lastTargetQuery);
}
if (address != null) {
mtargetInfo.setAddress(address.toString());
}
return targetInfoRepository.save(mtargetInfo);
}
TargetInfo updateTargetStatus(@NotNull TargetInfo targetInfo, TargetUpdateStatus status, Long lastTargetQuery,
URI address);
/**
* Adds an {@link ActionStatus} for a {@link UpdateAction} and cancels the
* {@link UpdateAction} if necessary.
*
* @param actionStatus
* to be updated
* @param action
* the status is for
* @return the persisted {@link Action}
*
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public Action addCancelActionStatus(@NotNull final ActionStatus actionStatus, final Action action) {
checkForToManyStatusEntries(action);
action.setStatus(actionStatus.getStatus());
switch (actionStatus.getStatus()) {
case WARNING:
case ERROR:
case RUNNING:
break;
case CANCELED:
case FINISHED:
// in case of successful cancellation we also report the success at
// the canceled action itself.
actionStatus.addMessage(
ControllerManagement.SERVER_MESSAGE_PREFIX + "Cancellation completion is finished sucessfully.");
deploymentManagement.successCancellation(action);
break;
case RETRIEVED:
actionStatus.addMessage(ControllerManagement.SERVER_MESSAGE_PREFIX + "Cancellation request retrieved.");
break;
default:
}
actionRepository.save(action);
actionStatusRepository.save(actionStatus);
return action;
}
/**
* Updates an {@link ActionStatus} for a {@link UpdateAction}.
*
* @param actionStatus
* to be updated
* @param action
* the update is for
* @return the persisted {@link Action}
*
* @throws EntityAlreadyExistsException
* if a given entity already exists
* @throws ToManyStatusEntriesException
* if more than the allowed number of status entries are
* inserted
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public Action addUpdateActionStatus(@NotNull final ActionStatus actionStatus, final Action action) {
if (!action.isActive()) {
LOG.debug("Update of actionStatus {} for action {} not possible since action not active anymore.",
actionStatus.getId(), action.getId());
return action;
}
return handleAddUpdateActionStatus(actionStatus, action);
}
/**
* Sets {@link TargetUpdateStatus} based on given {@link ActionStatus}.
*
* @param actionStatus
* @param action
* @return
*/
public Action handleAddUpdateActionStatus(final ActionStatus actionStatus, final Action action) {
LOG.debug("addUpdateActionStatus for action {}", action.getId());
final Action mergedAction = entityManager.merge(action);
Target mergedTarget = mergedAction.getTarget();
// check for a potential DOS attack
checkForToManyStatusEntries(action);
switch (actionStatus.getStatus()) {
case ERROR:
mergedTarget = deploymentManagement.updateTargetInfo(mergedTarget, TargetUpdateStatus.ERROR, false);
handleErrorOnAction(mergedAction, mergedTarget);
break;
case FINISHED:
handleFinishedAndStoreInTargetStatus(mergedTarget, mergedAction);
break;
case CANCELED:
case WARNING:
case RUNNING:
deploymentManagement.updateTargetInfo(mergedTarget, TargetUpdateStatus.PENDING, false);
break;
default:
break;
}
actionStatusRepository.save(actionStatus);
LOG.debug("addUpdateActionStatus {} for target {} is finished.", action.getId(), mergedTarget.getId());
return actionRepository.save(mergedAction);
}
private void handleErrorOnAction(final Action mergedAction, final Target mergedTarget) {
mergedAction.setActive(false);
mergedAction.setStatus(Status.ERROR);
mergedTarget.setAssignedDistributionSet(null);
targetManagement.updateTarget(mergedTarget);
}
private void checkForToManyStatusEntries(final Action action) {
if (securityProperties.getDos().getMaxStatusEntriesPerAction() > 0) {
final Long statusCount = actionStatusRepository.countByAction(action);
if (statusCount >= securityProperties.getDos().getMaxStatusEntriesPerAction()) {
LOG_DOS.error(
"Potential denial of service (DOS) attack identfied. More status entries in the system than permitted ({})!",
securityProperties.getDos().getMaxStatusEntriesPerAction());
throw new ToManyStatusEntriesException(
String.valueOf(securityProperties.getDos().getMaxStatusEntriesPerAction()));
}
}
}
private void handleFinishedAndStoreInTargetStatus(final Target target, final Action action) {
action.setActive(false);
action.setStatus(Status.FINISHED);
final TargetInfo targetInfo = target.getTargetInfo();
final DistributionSet ds = entityManager.merge(action.getDistributionSet());
targetInfo.setInstalledDistributionSet(ds);
if (target.getAssignedDistributionSet() != null && targetInfo.getInstalledDistributionSet() != null && target
.getAssignedDistributionSet().getId().equals(targetInfo.getInstalledDistributionSet().getId())) {
targetInfo.setUpdateStatus(TargetUpdateStatus.IN_SYNC);
targetInfo.setInstallationDate(System.currentTimeMillis());
} else {
targetInfo.setUpdateStatus(TargetUpdateStatus.PENDING);
targetInfo.setInstallationDate(System.currentTimeMillis());
}
targetInfoRepository.save(targetInfo);
entityManager.detach(ds);
}
/**
* Updates attributes of the controller.
*
* @param targetid
* to update
* @param data
* to insert
*
* @return updated {@link Target}
*
* @throws EntityNotFoundException
* if target that has to be updated could not be found
* @throws ToManyAttributeEntriesException
* if maximum
*/
@Modifying
@NotNull
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public Target updateControllerAttributes(@NotEmpty final String targetid, @NotNull final Map<String, String> data) {
final Target target = targetRepository.findByControllerId(targetid);
if (target == null) {
throw new EntityNotFoundException(targetid);
}
target.getTargetInfo().getControllerAttributes().putAll(data);
if (target.getTargetInfo().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()));
}
target.getTargetInfo().setLastTargetQuery(System.currentTimeMillis());
target.getTargetInfo().setRequestControllerAttributes(false);
return targetRepository.save(target);
}
/**
* Registers retrieved status for given {@link Target} and {@link Action} if
* it does not exist yet.
*
* @param action
* to the handle status for
* @param target
* to the handle status for
* @param message
* for the status
* @return the update action in case the status has been changed to
* {@link Status#RETRIEVED}
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public Action registerRetrieved(final Action action, final String message) {
return handleRegisterRetrieved(action, message);
}
/**
* Registers retrieved status for given {@link Target} and {@link Action} if
* it does not exist yet.
*
* @param action
* to the handle status for
* @param message
* for the status
* @return the updated action in case the status has been changed to
* {@link Status#RETRIEVED}
*/
public Action handleRegisterRetrieved(final Action action, final String message) {
// do a manual query with CriteriaBuilder to avoid unnecessary field
// queries and an extra
// count query made by spring-data when using pageable requests, we
// don't need an extra count
// query, we just want to check if the last action status is a retrieved
// or not.
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> queryActionStatus = cb.createQuery(Object[].class);
final Root<ActionStatus> actionStatusRoot = queryActionStatus.from(ActionStatus.class);
final CriteriaQuery<Object[]> query = queryActionStatus
.multiselect(actionStatusRoot.get(ActionStatus_.id), actionStatusRoot.get(ActionStatus_.status))
.where(cb.equal(actionStatusRoot.get(ActionStatus_.action), action))
.orderBy(cb.desc(actionStatusRoot.get(ActionStatus_.id)));
final List<Object[]> resultList = entityManager.createQuery(query).setFirstResult(0).setMaxResults(1)
.getResultList();
// if the latest status is not in retrieve state then we add a retrieved
// state again, we want
// to document a deployment retrieved status and a cancel retrieved
// status, but multiple
// retrieves after the other we don't want to store to protect to
// overflood action status in
// case controller retrieves a action multiple times.
if (resultList.isEmpty() || resultList.get(0)[1] != Status.RETRIEVED) {
// document that the status has been retrieved
actionStatusRepository
.save(new ActionStatus(action, Status.RETRIEVED, System.currentTimeMillis(), message));
// don't change the action status itself in case the action is in
// canceling state otherwise
// we modify the action status and the controller won't get the
// cancel job anymore.
if (!action.isCancelingOrCanceled()) {
final Action actionMerge = entityManager.merge(action);
actionMerge.setStatus(Status.RETRIEVED);
return actionRepository.save(actionMerge);
}
}
return action;
}
/**
* @param statusMessage
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void addActionStatusMessage(final ActionStatus statusMessage) {
actionStatusRepository.save(statusMessage);
}
/**
* An direct access to the security token of an
* {@link Target#getSecurityToken()} without authorization. This is
* necessary to be able to access the security-token without any
* security-context information because the security-token is used for
* authentication.
*
* @param controllerId
* the ID of the controller to retrieve the security token for
* @return the security context of the target, in case no target exists for
* the given controllerId {@code null} is returned
*/
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public String getSecurityTokenByControllerId(final String controllerId) {
final Target target = targetRepository.findByControllerId(controllerId);
return target != null ? target.getSecurityToken() : null;
}
}
}

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository;
import java.util.Collections;
import java.util.List;
import org.eclipse.hawkbit.repository.jpa.TargetManagement;
import org.eclipse.hawkbit.repository.model.AssignmentResult;
import org.eclipse.hawkbit.repository.model.Target;

View File

@@ -12,77 +12,24 @@ import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.persistence.EntityManager;
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;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.report.model.DataReportSeries;
import org.eclipse.hawkbit.report.model.DataReportSeriesItem;
import org.eclipse.hawkbit.report.model.InnerOuterDataReportSeries;
import org.eclipse.hawkbit.report.model.ListReportSeries;
import org.eclipse.hawkbit.report.model.SeriesTime;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSet_;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetInfo_;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.Target_;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
/**
* Service layer for generating hawkBit reports.
* Service layer for generating hawkBit statistics and reports.
*
*/
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated
@Service
public class ReportManagement {
@Value("${spring.jpa.database}")
private String databaseType;
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM");
private static final String H2_TARGET_CREATED_SQL_TEMPLATE = "SELECT TO_CHAR( DATEADD('second', target0_.created_at / 1000, DATE '1970-01-01'), '%s') AS col_0_0_, count(target0_.controller_id) AS col_1_0_ from sp_target target0_ WHERE TO_CHAR(DATEADD('second', target0_.created_at / 1000, DATE '1970-01-01'),'%s') BETWEEN TO_CHAR('%s', '%s') and TO_CHAR('%s', '%s') AND UPPER(target0_.tenant)=UPPER('%s') GROUP BY TO_CHAR(DATEADD('second', target0_.created_at / 1000, DATE '1970-01-01'), '%s')";
private static final String H2_CONTROLLER_FRRDBACK_SQL_TEMPLATE = "SELECT TO_CHAR(DATEADD('second', action_.created_at / 1000, DATE '1970-01-01'), '%s') AS col_0_0_, count(action_.id) AS col_1_0_ FROM sp_action action_ WHERE TO_CHAR(DATEADD('second', action_.created_at / 1000, DATE '1970-01-01'), '%s') BETWEEN TO_CHAR('%s', '%s') AND TO_CHAR('%s', '%s') AND UPPER(action_.tenant)=UPPER('%s') GROUP BY TO_CHAR(DATEADD('second', action_.created_at / 1000, DATE '1970-01-01'), '%s')";
private static final String MYSQL_TARGET_CREATED_SQL_TEMPLATE = "SELECT DATE_FORMAT(FROM_UNIXTIME(target0_.created_at / 1000), '%s') AS col_0_0_, COUNT(target0_.controller_id) AS col_1_0_ FROM sp_target target0_ WHERE DATE_FORMAT(FROM_UNIXTIME(target0_.created_at / 1000),'%s') BETWEEN DATE_FORMAT('%s', '%s') AND DATE_FORMAT('%s', '%s') AND UPPER(target0_.tenant)=UPPER('%s') GROUP BY DATE_FORMAT(FROM_UNIXTIME(target0_.created_at / 1000), '%s')";
private static final String MYSQL_CONTROLLER_FRRDBACK_SQL_TEMPLATE = "SELECT DATE_FORMAT(FROM_UNIXTIME(action_.created_at / 1000), '%s') AS col_0_0_, COUNT(action_.id) as col_1_0_ FROM sp_action action_ WHERE DATE_FORMAT(FROM_UNIXTIME(action_.created_at / 1000),'%s') BETWEEN DATE_FORMAT('%s', '%s') AND DATE_FORMAT('%s', '%s') AND UPPER(action_.tenant)=UPPER('%s') GROUP BY DATE_FORMAT(FROM_UNIXTIME(action_.created_at / 1000), '%s')";
private static final String MYSQL_DB_TYPE = "MYSQL";
private static final String H2_DB_TYPE = "H2";
@Autowired
private EntityManager entityManager;
@Autowired
private TenantAware tenantAware;
public interface ReportManagement {
/**
* Generates a report of all targets of their current update status count.
@@ -92,29 +39,8 @@ public class ReportManagement {
* @return a data report series which contains the target count for each
* target update status
*/
@Cacheable("targetStatus")
public DataReportSeries<TargetUpdateStatus> targetStatus() {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
final Root<Target> targetRoot = query.from(Target.class);
final Join<Target, TargetInfo> targetInfo = targetRoot.join(Target_.targetInfo);
final Expression<Long> countColumn = cb.count(targetInfo.get(TargetInfo_.targetId));
final CriteriaQuery<Object[]> multiselect = query
.multiselect(targetInfo.get(TargetInfo_.updateStatus), countColumn)
.groupBy(targetInfo.get(TargetInfo_.updateStatus))
.orderBy(cb.desc(targetInfo.get(TargetInfo_.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]))
.collect(Collectors.toList());
return new DataReportSeries<>("Target Status Overview", reportSeriesItems);
}
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
DataReportSeries<TargetUpdateStatus> targetStatus();
/**
* Generates a report of the top x distribution set assigned usage as a list
@@ -132,29 +58,8 @@ public class ReportManagement {
* set entries are summarized as "misc"
* @return a list of inner and outer series of distribution set usage
*/
@Cacheable("distributionUsageAssigned")
public List<InnerOuterDataReportSeries<String>> distributionUsageAssigned(final int topXEntries) {
// top X entries distribution usage
final CriteriaBuilder cbTopX = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> queryTopX = cbTopX.createQuery(Object[].class);
final Root<DistributionSet> rootTopX = queryTopX.from(DistributionSet.class);
final ListJoin<DistributionSet, Target> joinTopX = rootTopX.join(DistributionSet_.assignedToTargets,
JoinType.LEFT);
final Expression<Long> countColumn = cbTopX.count(joinTopX);
// top x usage query
final CriteriaQuery<Object[]> groupBy = queryTopX
.multiselect(rootTopX.get(DistributionSet_.name), rootTopX.get(DistributionSet_.version), countColumn)
.where(cbTopX.equal(rootTopX.get(DistributionSet_.deleted), false))
.groupBy(rootTopX.get(DistributionSet_.name), rootTopX.get(DistributionSet_.version))
.orderBy(cbTopX.desc(countColumn), cbTopX.asc(rootTopX.get(DistributionSet_.name)));
// | col1 | col2 | col3 |
// | NAME | VER | COUNT |
final List<Object[]> resultListTop = entityManager.createQuery(groupBy).getResultList();
// end of top X entries distribution usage
return mapDistirbutionUsageResultToDataReport(topXEntries, resultListTop);
}
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
List<InnerOuterDataReportSeries<String>> distributionUsageAssigned(int topXEntries);
/**
* Generates a report of the top x distribution set installed usage as a
@@ -173,28 +78,8 @@ public class ReportManagement {
* set entries are summarized as "misc"
* @return a list of inner and outer series of distribution set usage
*/
@Cacheable("distributionUsageInstalled")
public List<InnerOuterDataReportSeries<String>> distributionUsageInstalled(final int topXEntries) {
// top X entries distribution usage
final CriteriaBuilder cbTopX = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> queryTopX = cbTopX.createQuery(Object[].class);
final Root<DistributionSet> rootTopX = queryTopX.from(DistributionSet.class);
final ListJoin<DistributionSet, TargetInfo> joinTopX = rootTopX.join(DistributionSet_.installedAtTargets,
JoinType.LEFT);
final Expression<Long> countColumn = cbTopX.count(joinTopX);
// top x usage query
final CriteriaQuery<Object[]> groupBy = queryTopX
.multiselect(rootTopX.get(DistributionSet_.name), rootTopX.get(DistributionSet_.version), countColumn)
.where(cbTopX.equal(rootTopX.get(DistributionSet_.deleted), false))
.groupBy(rootTopX.get(DistributionSet_.name), rootTopX.get(DistributionSet_.version))
.orderBy(cbTopX.desc(countColumn), cbTopX.asc(rootTopX.get(DistributionSet_.name)));
// | col1 | col2 | col3 |
// | NAME | VER | COUNT |
final List<Object[]> resultListTop = entityManager.createQuery(groupBy).getResultList();
// end of top X entries distribution usage
return mapDistirbutionUsageResultToDataReport(topXEntries, resultListTop);
}
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
List<InnerOuterDataReportSeries<String>> distributionUsageInstalled(int topXEntries);
/**
* Generates report for target created over period.
@@ -208,55 +93,9 @@ public class ReportManagement {
* @return <T> DataReportSeries<T> ListReportSeries list of target created
* count
*/
@Cacheable("targetsCreatedOverPeriod")
public <T extends Serializable> DataReportSeries<T> targetsCreatedOverPeriod(final DateType<T> dateType,
final LocalDateTime from, final LocalDateTime to) {
final Query createNativeQuery = entityManager
.createNativeQuery(getTargetsCreatedQueryTemplate(dateType, from, to));
final List<Object[]> resultList = createNativeQuery.getResultList();
final List<DataReportSeriesItem<T>> reportItems = resultList.stream()
.map(r -> new DataReportSeriesItem<>(dateType.format((String) r[0]), ((Number) r[1]).longValue()))
.collect(Collectors.toList());
return new DataReportSeries<>("CreatedTargets", reportItems);
}
private String getTargetsCreatedQueryTemplate(final DateType<?> dateType, final LocalDateTime from,
final LocalDateTime to) {
switch (databaseType) {
case H2_DB_TYPE:
return String.format(H2_TARGET_CREATED_SQL_TEMPLATE, dateTimeFormatToSqlFormat(dateType),
dateTimeFormatToSqlFormat(dateType), from.format(DATE_FORMAT), dateTimeFormatToSqlFormat(dateType),
to.format(DATE_FORMAT), dateTimeFormatToSqlFormat(dateType), tenantAware.getCurrentTenant(),
dateTimeFormatToSqlFormat(dateType));
case MYSQL_DB_TYPE:
return String.format(MYSQL_TARGET_CREATED_SQL_TEMPLATE, dateTimeFormatToSqlFormat(dateType),
dateTimeFormatToSqlFormat(dateType), from.toString(), dateTimeFormatToSqlFormat(dateType),
to.toString(), dateTimeFormatToSqlFormat(dateType), tenantAware.getCurrentTenant(),
dateTimeFormatToSqlFormat(dateType));
default:
return null;
}
}
private String getFeedbackReceivedQueryTemplate(final DateType<?> dateType, final LocalDateTime from,
final LocalDateTime to) {
switch (databaseType) {
case H2_DB_TYPE:
return String.format(H2_CONTROLLER_FRRDBACK_SQL_TEMPLATE, dateTimeFormatToSqlFormat(dateType),
dateTimeFormatToSqlFormat(dateType), from.format(DATE_FORMAT), dateTimeFormatToSqlFormat(dateType),
to.format(DATE_FORMAT), dateTimeFormatToSqlFormat(dateType), tenantAware.getCurrentTenant(),
dateTimeFormatToSqlFormat(dateType));
case MYSQL_DB_TYPE:
return String.format(MYSQL_CONTROLLER_FRRDBACK_SQL_TEMPLATE, dateTimeFormatToSqlFormat(dateType),
dateTimeFormatToSqlFormat(dateType), from.toString(), dateTimeFormatToSqlFormat(dateType),
to.toString(), dateTimeFormatToSqlFormat(dateType), tenantAware.getCurrentTenant(),
dateTimeFormatToSqlFormat(dateType));
default:
return null;
}
}
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
<T extends Serializable> DataReportSeries<T> targetsCreatedOverPeriod(@NotNull DateType<T> dateType,
@NotNull LocalDateTime from, @NotNull LocalDateTime to);
/**
* Generates report for feedback over period.
@@ -270,19 +109,9 @@ public class ReportManagement {
* @return <T> DataReportSeries<T> ListReportSeries list of action status
* count
*/
@Cacheable("feedbackReceivedOverTime")
public <T extends Serializable> DataReportSeries<T> feedbackReceivedOverTime(final DateType<T> dateType,
final LocalDateTime from, final LocalDateTime to) {
final Query createNativeQuery = entityManager
.createNativeQuery(getFeedbackReceivedQueryTemplate(dateType, from, to));
final List<Object[]> resultList = createNativeQuery.getResultList();
final List<DataReportSeriesItem<T>> reportItems = resultList.stream()
.map(r -> new DataReportSeriesItem<>(dateType.format((String) r[0]), ((Number) r[1]).longValue()))
.collect(Collectors.toList());
return new DataReportSeries<>("FeedbackRecieved", reportItems);
}
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
<T extends Serializable> DataReportSeries<T> feedbackReceivedOverTime(@NotNull DateType<T> dateType,
@NotNull LocalDateTime from, @NotNull LocalDateTime to);
/**
* Generates a report as a {@link ListReportSeries} targets polled based on
@@ -297,212 +126,8 @@ public class ReportManagement {
* than a year, never.
*
*/
@Cacheable("targetsLastPoll")
public DataReportSeries<SeriesTime> targetsLastPoll() {
final LocalDateTime now = LocalDateTime.now();
final LocalDateTime beforeHour = now.minusHours(1);
final LocalDateTime beforeDay = now.minusDays(1);
final LocalDateTime beforeWeek = now.minusWeeks(1);
final LocalDateTime beforeMonth = now.minusMonths(1);
final LocalDateTime beforeYear = now.minusYears(1);
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final List<DataReportSeriesItem<SeriesTime>> resultList = new ArrayList<>();
// hours
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.HOUR,
entityManager.createQuery(createCountSelectTargetsLastPoll(cb, beforeHour, now)).getSingleResult()));
// days
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.DAY, entityManager
.createQuery(createCountSelectTargetsLastPoll(cb, beforeDay, beforeHour)).getSingleResult()));
// weeks
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.WEEK, entityManager
.createQuery(createCountSelectTargetsLastPoll(cb, beforeWeek, beforeDay)).getSingleResult()));
// months
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.MONTH, entityManager
.createQuery(createCountSelectTargetsLastPoll(cb, beforeMonth, beforeWeek)).getSingleResult()));
// years
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.YEAR, entityManager
.createQuery(createCountSelectTargetsLastPoll(cb, beforeYear, beforeMonth)).getSingleResult()));
// years
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.MORE_THAN_YEAR,
entityManager.createQuery(createCountSelectTargetsLastPoll(cb, null, beforeYear)).getSingleResult()));
// never
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.NEVER,
entityManager.createQuery(createCountSelectTargetsLastPoll(cb, null, null)).getSingleResult()));
return new DataReportSeries<>("TargetLastPoll", resultList);
}
private CriteriaQuery<Long> createCountSelectTargetsLastPoll(final CriteriaBuilder cb, final LocalDateTime from,
final LocalDateTime to) {
Long start = null;
Long end = null;
if (from != null) {
start = from.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
}
if (to != null) {
end = to.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
}
// count select statement
final CriteriaQuery<Long> countSelect = cb.createQuery(Long.class);
final Root<Target> countSelectRoot = countSelect.from(Target.class);
final Join<Target, TargetInfo> targetInfoJoin = countSelectRoot.join(Target_.targetInfo);
countSelect.select(cb.count(countSelectRoot));
if (start != null && end != null) {
countSelect.where(cb.between(targetInfoJoin.get(TargetInfo_.lastTargetQuery), start, end));
} else if (from == null && to != null) {
countSelect.where(cb.lessThanOrEqualTo(targetInfoJoin.get(TargetInfo_.lastTargetQuery), end));
} else {
countSelect.where(cb.isNull(targetInfoJoin.get(TargetInfo_.lastTargetQuery)));
}
return countSelect;
}
private List<InnerOuterDataReportSeries<String>> mapDistirbutionUsageResultToDataReport(final int topXEntries,
final List<Object[]> resultListTop) {
final List<InnerOuterDataReportSeries<String>> innerOuterReport = new ArrayList<>();
final Map<DSName, InnerOuter> map = new LinkedHashMap<>();
int topXCounter = 0;
for (final Object[] objects : resultListTop) {
final boolean containsInnerOuter = map.containsKey(new DSName((String) objects[0]));
final String name = containsInnerOuter || topXCounter < topXEntries ? (String) objects[0] : null;
final DSName dsName = new DSName(name);
final String version = containsInnerOuter || topXCounter < topXEntries ? (String) objects[1] : null;
final Long count = (Long) objects[2];
InnerOuter innerouter = map.get(dsName);
if (innerouter == null) {
topXCounter++;
innerouter = new InnerOuter(dsName);
map.put(new DSName(name), innerouter);
}
innerouter.addOuter(new DSName(version), count);
}
for (final InnerOuter inner : map.values()) {
final List<DataReportSeriesItem<String>> outerReportItems = new ArrayList<>();
if (inner.name.getName() != null) {
for (final InnerOuter outer : inner.outer) {
outerReportItems.add(outer.toItem());
}
} else {
outerReportItems.add(new DataReportSeriesItem<String>("misc", inner.count));
}
innerOuterReport.add(new InnerOuterDataReportSeries<String>(
new DataReportSeries<>("DS-Name", Collections.singletonList(inner.toItem())),
new DataReportSeries<>("DS-Version", outerReportItems)));
}
return innerOuterReport;
}
private static final class InnerOuter {
final DSName name;
long count;
final List<InnerOuter> outer;
private InnerOuter(final DSName idName) {
name = idName;
outer = new ArrayList<>();
}
private InnerOuter(final DSName idName, final long count) {
name = idName;
this.count = count;
outer = new ArrayList<>();
}
private void addOuter(final DSName idName, final long count) {
outer.add(new InnerOuter(idName, count));
this.count += count;
}
private DataReportSeriesItem<String> toItem() {
return new DataReportSeriesItem<>(name.getName() != null ? name.getName() : "misc", count);
}
}
/**
* Object contains the name and the id of an entity.
*
*/
private static final class DSName {
private final String name;
/**
* @param id
* the ID of an entity
* @param name
* the name of an entity
*/
private DSName(final String name) {
this.name = name;
}
/**
* @return the name
*/
private String getName() {
return name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (name == null ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) { // NOSONAR - as this is
// generated
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DSName other = (DSName) obj;
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
return true;
}
@Override
public String toString() {
return "DSName [name=" + name + "]";
}
}
private String dateTimeFormatToSqlFormat(final DateType<?> datatype) {
switch (databaseType) {
case H2_DB_TYPE:
return datatype.h2Format();
case MYSQL_DB_TYPE:
return datatype.mySqlFormat();
default:
return null;
}
}
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
DataReportSeries<SeriesTime> targetsLastPoll();
/**
* Return DateTypes.
@@ -580,4 +205,5 @@ public class ReportManagement {
}
}
}
}

View File

@@ -8,65 +8,20 @@
*/
package org.eclipse.hawkbit.repository;
import java.util.List;
import java.util.Map;
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;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action_;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.RolloutGroup_;
import org.eclipse.hawkbit.repository.model.RolloutTargetGroup;
import org.eclipse.hawkbit.repository.model.RolloutTargetGroup_;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetWithActionStatus;
import org.eclipse.hawkbit.repository.model.Target_;
import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
import org.springframework.beans.factory.annotation.Autowired;
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.data.jpa.domain.Specification;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
/**
* RolloutGroupManagement to control rollout groups. This service secures all
* the functionality based on the {@link PreAuthorize} annotation on methods.
*/
@Validated
@Service
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public class RolloutGroupManagement {
@Autowired
private RolloutGroupRepository rolloutGroupRepository;
@Autowired
private ActionRepository actionRepository;
@Autowired
private TargetRepository targetRepository;
@Autowired
private EntityManager entityManager;
public interface RolloutGroupManagement {
/**
* Retrieves a single {@link RolloutGroup} by its ID.
@@ -77,9 +32,7 @@ public class RolloutGroupManagement {
* does not exists
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
public RolloutGroup findRolloutGroupById(final Long rolloutGroupId) {
return rolloutGroupRepository.findOne(rolloutGroupId);
}
RolloutGroup findRolloutGroupById(@NotNull Long rolloutGroupId);
/**
* Retrieves a page of {@link RolloutGroup}s filtered by a given
@@ -92,9 +45,7 @@ public class RolloutGroupManagement {
* @return a page of found {@link RolloutGroup}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
public Page<RolloutGroup> findRolloutGroupsByRolloutId(final Long rolloutId, final Pageable page) {
return rolloutGroupRepository.findByRolloutId(rolloutId, page);
}
Page<RolloutGroup> findRolloutGroupsByRolloutId(@NotNull Long rolloutId, @NotNull Pageable page);
/**
* Retrieves a page of {@link RolloutGroup}s filtered by a given
@@ -110,12 +61,8 @@ public class RolloutGroupManagement {
* @return a page of found {@link RolloutGroup}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
public Page<RolloutGroup> findRolloutGroupsByPredicate(final Rollout rollout,
final Specification<RolloutGroup> specification, final Pageable page) {
return rolloutGroupRepository.findAll((root, query, criteriaBuilder) -> criteriaBuilder.and(
criteriaBuilder.equal(root.get(RolloutGroup_.rollout), rollout),
specification.toPredicate(root, query, criteriaBuilder)), page);
}
Page<RolloutGroup> findRolloutGroupsAll(@NotNull Rollout rollout,
@NotNull Specification<RolloutGroup> specification, @NotNull Pageable page);
/**
* Retrieves a page of {@link RolloutGroup}s filtered by a given
@@ -128,21 +75,7 @@ public class RolloutGroupManagement {
* @return a page of found {@link RolloutGroup}s
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
public Page<RolloutGroup> findAllRolloutGroupsWithDetailedStatus(final Long rolloutId, final Pageable page) {
final Page<RolloutGroup> rolloutGroups = rolloutGroupRepository.findByRolloutId(rolloutId, page);
final List<Long> rolloutGroupIds = rolloutGroups.getContent().stream().map(rollout -> rollout.getId())
.collect(Collectors.toList());
final Map<Long, List<TotalTargetCountActionStatus>> allStatesForRollout = getStatusCountItemForRolloutGroup(
rolloutGroupIds);
for (final RolloutGroup rolloutGroup : rolloutGroups) {
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
allStatesForRollout.get(rolloutGroup.getId()), rolloutGroup.getTotalTargets());
rolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus);
}
return rolloutGroups;
}
Page<RolloutGroup> findAllRolloutGroupsWithDetailedStatus(@NotNull Long rolloutId, @NotNull Pageable page);
/**
* Get count of targets in different status in rollout group.
@@ -152,25 +85,9 @@ public class RolloutGroupManagement {
* @return rolloutGroup with details of targets count for different statuses
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
public RolloutGroup findRolloutGroupWithDetailedStatus(final Long rolloutGroupId) {
final RolloutGroup rolloutGroup = findRolloutGroupById(rolloutGroupId);
final List<TotalTargetCountActionStatus> rolloutStatusCountItems = actionRepository
.getStatusCountByRolloutGroupId(rolloutGroupId);
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems,
rolloutGroup.getTotalTargets());
rolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus);
return rolloutGroup;
}
private Map<Long, List<TotalTargetCountActionStatus>> getStatusCountItemForRolloutGroup(
final List<Long> rolloutGroupIds) {
final List<TotalTargetCountActionStatus> resultList = actionRepository
.getStatusCountByRolloutGroupId(rolloutGroupIds);
return resultList.stream().collect(Collectors.groupingBy(TotalTargetCountActionStatus::getId));
}
RolloutGroup findRolloutGroupWithDetailedStatus(@NotNull Long rolloutGroupId);
// TODO discuss: target read perm missing?
/**
* Get targets of specified rollout group.
*
@@ -184,15 +101,10 @@ public class RolloutGroupManagement {
* @return Page<Target> list of targets of a rollout group
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
public Page<Target> findRolloutGroupTargets(final RolloutGroup rolloutGroup,
final Specification<Target> specification, final Pageable page) {
return targetRepository.findAll((root, query, criteriaBuilder) -> {
final ListJoin<Target, RolloutTargetGroup> rolloutTargetJoin = root.join(Target_.rolloutTargetGroup);
return criteriaBuilder.and(specification.toPredicate(root, query, criteriaBuilder),
criteriaBuilder.equal(rolloutTargetJoin.get(RolloutTargetGroup_.rolloutGroup), rolloutGroup));
}, page);
}
Page<Target> findRolloutGroupTargets(@NotNull RolloutGroup rolloutGroup,
@NotNull Specification<Target> specification, @NotNull Pageable page);
// TODO discuss: target read perm missing?
/**
* Get targets of specified rollout group.
*
@@ -204,20 +116,9 @@ public class RolloutGroupManagement {
* @return Page<Target> list of targets of a rollout group
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
public Page<Target> findRolloutGroupTargets(@NotNull final RolloutGroup rolloutGroup, final Pageable page) {
if (isRolloutStatusReady(rolloutGroup)) {
// in case of status ready the action has not been created yet and
// the relation information between target and rollout-group is
// stored in the #TargetRolloutGroup.
return targetRepository.findByRolloutTargetGroupRolloutGroupId(rolloutGroup.getId(), page);
}
return targetRepository.findByActionsRolloutGroup(rolloutGroup, page);
}
private static boolean isRolloutStatusReady(final RolloutGroup rolloutGroup) {
return rolloutGroup != null && RolloutStatus.READY.equals(rolloutGroup.getRollout().getStatus());
}
Page<Target> findRolloutGroupTargets(@NotNull RolloutGroup rolloutGroup, @NotNull Pageable page);
// TODO discuss: target read perm missing?
/**
*
* Find all targets with action status by rollout group id. The action
@@ -233,31 +134,7 @@ public class RolloutGroupManagement {
* @return {@link TargetWithActionStatus} target with action status
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
public Page<TargetWithActionStatus> findAllTargetsWithActionStatus(final PageRequest pageRequest,
@NotNull final RolloutGroup rolloutGroup) {
Page<TargetWithActionStatus> findAllTargetsWithActionStatus(@NotNull PageRequest pageRequest,
@NotNull RolloutGroup rolloutGroup);
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
final CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
final Root<RolloutTargetGroup> targetRoot = query.distinct(true).from(RolloutTargetGroup.class);
final Join<RolloutTargetGroup, Target> targetJoin = targetRoot.join(RolloutTargetGroup_.target);
final ListJoin<RolloutTargetGroup, Action> actionJoin = targetRoot.join(RolloutTargetGroup_.actions,
JoinType.LEFT);
final Root<RolloutTargetGroup> countQueryFrom = countQuery.distinct(true).from(RolloutTargetGroup.class);
countQueryFrom.join(RolloutTargetGroup_.target);
countQueryFrom.join(RolloutTargetGroup_.actions, JoinType.LEFT);
countQuery.select(cb.count(countQueryFrom))
.where(cb.equal(countQueryFrom.get(RolloutTargetGroup_.rolloutGroup), rolloutGroup));
final Long totalCount = entityManager.createQuery(countQuery).getSingleResult();
final CriteriaQuery<Object[]> multiselect = query.multiselect(targetJoin, actionJoin.get(Action_.status))
.where(cb.equal(targetRoot.get(RolloutTargetGroup_.rolloutGroup), rolloutGroup));
final List<TargetWithActionStatus> targetWithActionStatus = entityManager.createQuery(multiselect)
.setFirstResult(pageRequest.getOffset()).setMaxResults(pageRequest.getPageSize()).getResultList()
.stream().map(o -> new TargetWithActionStatus((Target) o[0], (Action.Status) o[1]))
.collect(Collectors.toList());
return new PageImpl<>(targetWithActionStatus, pageRequest, totalCount);
}
}
}

View File

@@ -8,131 +8,29 @@
*/
package org.eclipse.hawkbit.repository;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
import javax.persistence.EntityManager;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.cache.CacheWriteNotify;
import org.eclipse.hawkbit.eventbus.event.RolloutGroupCreatedEvent;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupConditions;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorCondition;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
import org.eclipse.hawkbit.repository.model.RolloutTargetGroup;
import org.eclipse.hawkbit.repository.model.Rollout_;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
import org.eclipse.hawkbit.rollout.condition.RolloutGroupActionEvaluator;
import org.eclipse.hawkbit.rollout.condition.RolloutGroupConditionEvaluator;
import org.hibernate.validator.constraints.NotEmpty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.Page;
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.data.jpa.repository.Modifying;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;
/**
* RolloutManagement to control rollouts e.g. like creating, starting, resuming
* and pausing rollouts. This service secures all the functionality based on the
* {@link PreAuthorize} annotation on methods.
*/
@Validated
@Service
@EnableScheduling
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public class RolloutManagement {
private static final Logger LOGGER = LoggerFactory.getLogger(RolloutManagement.class);
@Autowired
private EntityManager entityManager;
@Autowired
private RolloutRepository rolloutRepository;
@Autowired
private TargetManagement targetManagement;
@Autowired
private TargetRepository targetRepository;
@Autowired
private RolloutGroupRepository rolloutGroupRepository;
@Autowired
private DeploymentManagement deploymentManagement;
@Autowired
private RolloutTargetGroupRepository rolloutTargetGroupRepository;
@Autowired
private ActionRepository actionRepository;
@Autowired
private ApplicationContext context;
@Autowired
private NoCountPagingRepository criteriaNoCountDao;
@Autowired
private PlatformTransactionManager txManager;
@Autowired
private CacheWriteNotify cacheWriteNotify;
@Autowired
@Qualifier("asyncExecutor")
private Executor executor;
/*
* set which stores the rollouts which are asynchronously creating. This is
* necessary to verify rollouts which maybe stuck during creationg e.g.
* because of database interruption, failures or even application crash.
* !This is not cluster aware!
*/
private static final Set<String> creatingRollouts = ConcurrentHashMap.newKeySet();
/*
* set which stores the rollouts which are asynchronously starting. This is
* necessary to verify rollouts which maybe stuck during starting e.g.
* because of database interruption, failures or even application crash.
* !This is not cluster aware!
*/
private static final Set<String> startingRollouts = ConcurrentHashMap.newKeySet();
public interface RolloutManagement {
/**
* Retrieves all rollouts.
@@ -142,9 +40,7 @@ public class RolloutManagement {
* @return a page of found rollouts
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
public Page<Rollout> findAll(final Pageable page) {
return rolloutRepository.findAll(page);
}
Page<Rollout> findAll(@NotNull Pageable page);
/**
* Retrieves all rollouts found by the given specification.
@@ -156,12 +52,8 @@ public class RolloutManagement {
* @return a page of found rollouts
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
public Page<Rollout> findAllWithDetailedStatusByPredicate(final Specification<Rollout> specification,
final Pageable page) {
final Page<Rollout> findAll = rolloutRepository.findAll(specification, page);
setRolloutStatusDetails(findAll);
return findAll;
}
Page<Rollout> findAllWithDetailedStatusByPredicate(@NotNull Specification<Rollout> specification,
@NotNull Pageable page);
/**
* Retrieves a specific rollout by its ID.
@@ -172,9 +64,7 @@ public class RolloutManagement {
* not exists
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
public Rollout findRolloutById(final Long rolloutId) {
return rolloutRepository.findOne(rolloutId);
}
Rollout findRolloutById(@NotNull Long rolloutId);
/**
* Persists a new rollout entity. The filter within the
@@ -201,14 +91,8 @@ public class RolloutManagement {
* @throws IllegalArgumentException
* in case the given groupSize is zero or lower.
*/
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
public Rollout createRollout(final Rollout rollout, final int amountGroup,
final RolloutGroupConditions conditions) {
final Rollout savedRollout = createRollout(rollout, amountGroup);
return createRolloutGroups(amountGroup, conditions, savedRollout);
}
Rollout createRollout(@NotNull Rollout rollout, int amountGroup, @NotNull RolloutGroupConditions conditions);
/**
* Persists a new rollout entity. The filter within the
@@ -244,116 +128,8 @@ public class RolloutManagement {
* @return the created rollout entity in state
* {@link RolloutStatus#CREATING}
*/
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
public Rollout createRolloutAsync(final Rollout rollout, final int amountGroup,
final RolloutGroupConditions conditions) {
final Rollout savedRollout = createRollout(rollout, amountGroup);
creatingRollouts.add(savedRollout.getName());
// need to flush the entity manager here to get the ID of the rollout,
// because entity manager is set to FlushMode#Auto, entitymanager will
// flush the Target entity, due the indirect relationship to the Rollout
// entity without set an ID JPA is throwing a Invalid
// 'org.springframework.dao.InvalidDataAccessApiUsageException: During
// synchronization aect was found through a relationship that was not
// marked cascade PERSIST'
entityManager.flush();
executor.execute(() -> {
try {
createRolloutGroupsInNewTransaction(amountGroup, conditions, savedRollout);
} finally {
creatingRollouts.remove(savedRollout.getName());
}
});
return savedRollout;
}
private Rollout createRollout(final Rollout rollout, final int amountGroup) {
verifyRolloutGroupParameter(amountGroup);
final Long totalTargets = targetManagement.countTargetByTargetFilterQuery(rollout.getTargetFilterQuery());
rollout.setTotalTargets(totalTargets.longValue());
return rolloutRepository.save(rollout);
}
private static void verifyRolloutGroupParameter(final int amountGroup) {
if (amountGroup <= 0) {
throw new IllegalArgumentException("the amountGroup must be greater than zero");
} else if (amountGroup > 500) {
throw new IllegalArgumentException("the amountGroup must not be greater than 500");
}
}
private Rollout createRolloutGroupsInNewTransaction(final int amountOfGroups,
final RolloutGroupConditions conditions, final Rollout savedRollout) {
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("creatingRollout");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
return new TransactionTemplate(txManager, def)
.execute(status -> createRolloutGroups(amountOfGroups, conditions, savedRollout));
}
/**
* Method for creating rollout groups and calculating group sizes. Group
* sizes are calculated by dividing the total count of targets through the
* amount of given groups. In same cases this will lead to less rollout
* groups than given by client.
*
* @param amountOfGroups
* the amount of groups
* @param conditions
* the rollout group conditions
* @param savedRollout
* the rollout
* @return the rollout with created groups
*/
private Rollout createRolloutGroups(final int amountOfGroups, final RolloutGroupConditions conditions,
final Rollout savedRollout) {
int pageIndex = 0;
int groupIndex = 0;
final Long totalCount = savedRollout.getTotalTargets();
final int groupSize = (int) Math.ceil((double) totalCount / (double) amountOfGroups);
// validate if the amount of groups that will be created are the amount
// of groups that the client what's to have created.
int amountGroupValidated = amountOfGroups;
final int amountGroupCreation = (int) (Math.ceil((double) totalCount / (double) groupSize));
if (amountGroupCreation == (amountOfGroups - 1)) {
amountGroupValidated--;
}
RolloutGroup lastSavedGroup = null;
while (pageIndex < totalCount) {
groupIndex++;
final String nameAndDesc = "group-" + groupIndex;
final RolloutGroup group = new RolloutGroup();
group.setName(nameAndDesc);
group.setDescription(nameAndDesc);
group.setRollout(savedRollout);
group.setParent(lastSavedGroup);
group.setSuccessCondition(conditions.getSuccessCondition());
group.setSuccessConditionExp(conditions.getSuccessConditionExp());
group.setErrorCondition(conditions.getErrorCondition());
group.setErrorConditionExp(conditions.getErrorConditionExp());
group.setErrorAction(conditions.getErrorAction());
group.setErrorActionExp(conditions.getErrorActionExp());
final RolloutGroup savedGroup = rolloutGroupRepository.save(group);
final Slice<Target> targetGroup = targetManagement.findTargetsAll(savedRollout.getTargetFilterQuery(),
new OffsetBasedPageRequest(pageIndex, groupSize, new Sort(Direction.ASC, "id")));
savedGroup.setTotalTargets(targetGroup.getContent().size());
lastSavedGroup = savedGroup;
targetGroup
.forEach(target -> rolloutTargetGroupRepository.save(new RolloutTargetGroup(savedGroup, target)));
cacheWriteNotify.rolloutGroupCreated(groupIndex, savedRollout.getId(), savedGroup.getId(),
amountGroupValidated, groupIndex);
pageIndex += groupSize;
}
savedRollout.setStatus(RolloutStatus.READY);
return rolloutRepository.save(savedRollout);
}
Rollout createRolloutAsync(@NotNull Rollout rollout, int amountGroup, @NotNull RolloutGroupConditions conditions);
/**
* Starts a rollout which has been created. The rollout must be in
@@ -373,15 +149,9 @@ public class RolloutManagement {
* if given rollout is not in {@link RolloutStatus#READY}. Only
* ready rollouts can be started.
*/
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
public Rollout startRollout(final Rollout rollout) {
final Rollout mergedRollout = entityManager.merge(rollout);
checkIfRolloutCanStarted(rollout, mergedRollout);
return doStartRollout(mergedRollout);
}
Rollout startRollout(@NotNull Rollout rollout);
/**
* Starts a rollout asynchronously which has been created. The rollout must
@@ -402,61 +172,9 @@ public class RolloutManagement {
* if given rollout is not in {@link RolloutStatus#READY}. Only
* ready rollouts can be started.
*/
@Transactional
@Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
public Rollout startRolloutAsync(final Rollout rollout) {
final Rollout mergedRollout = entityManager.merge(rollout);
checkIfRolloutCanStarted(rollout, mergedRollout);
mergedRollout.setStatus(RolloutStatus.STARTING);
final Rollout updatedRollout = rolloutRepository.save(mergedRollout);
startingRollouts.add(updatedRollout.getName());
executor.execute(() -> {
try {
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("startingRollout");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
new TransactionTemplate(txManager, def).execute(status -> {
doStartRollout(updatedRollout);
return null;
});
} finally {
startingRollouts.remove(updatedRollout.getName());
}
});
return updatedRollout;
}
private Rollout doStartRollout(final Rollout rollout) {
final DistributionSet distributionSet = rollout.getDistributionSet();
final ActionType actionType = rollout.getActionType();
final long forceTime = rollout.getForcedTime();
final List<RolloutGroup> rolloutGroups = rolloutGroupRepository.findByRolloutOrderByIdAsc(rollout);
for (int iGroup = 0; iGroup < rolloutGroups.size(); iGroup++) {
final RolloutGroup rolloutGroup = rolloutGroups.get(iGroup);
final List<Target> targetGroup = targetRepository.findByRolloutTargetGroupRolloutGroup(rolloutGroup);
// firstgroup can already be started
if (iGroup == 0) {
final List<TargetWithActionType> targetsWithActionType = targetGroup.stream()
.map(t -> new TargetWithActionType(t.getControllerId(), actionType, forceTime))
.collect(Collectors.toList());
deploymentManagement.assignDistributionSet(distributionSet.getId(), targetsWithActionType, rollout,
rolloutGroup);
rolloutGroup.setStatus(RolloutGroupStatus.RUNNING);
} else {
// create only not active actions with status scheduled so they
// can be activated later
deploymentManagement.createScheduledAction(targetGroup, distributionSet, actionType, forceTime, rollout,
rolloutGroup);
rolloutGroup.setStatus(RolloutGroupStatus.SCHEDULED);
}
rolloutGroupRepository.save(rolloutGroup);
}
rollout.setStatus(RolloutStatus.RUNNING);
return rolloutRepository.save(rollout);
}
Rollout startRolloutAsync(@NotNull Rollout rollout);
/**
* Pauses a rollout which is currently running. The Rollout switches
@@ -477,24 +195,9 @@ public class RolloutManagement {
* if given rollout is not in {@link RolloutStatus#RUNNING}.
* Only running rollouts can be paused.
*/
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
public void pauseRollout(final Rollout rollout) {
final Rollout mergedRollout = entityManager.merge(rollout);
if (mergedRollout.getStatus() != RolloutStatus.RUNNING) {
throw new RolloutIllegalStateException("Rollout can only be paused in state running but current state is "
+ rollout.getStatus().name().toLowerCase());
}
// setting the complete rollout only in paused state. This is sufficient
// due the currently running groups will be completed and new groups are
// not started until rollout goes back to running state again. The
// periodically check for running rollouts will skip rollouts in pause
// state.
mergedRollout.setStatus(RolloutStatus.PAUSED);
rolloutRepository.save(mergedRollout);
}
void pauseRollout(@NotNull Rollout rollout);
/**
* Resumes a paused rollout. The rollout switches back to
@@ -507,19 +210,9 @@ public class RolloutManagement {
* if given rollout is not in {@link RolloutStatus#PAUSED}. Only
* paused rollouts can be resumed.
*/
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
public void resumeRollout(final Rollout rollout) {
final Rollout mergedRollout = entityManager.merge(rollout);
if (!(RolloutStatus.PAUSED.equals(mergedRollout.getStatus()))) {
throw new RolloutIllegalStateException("Rollout can only be resumed in state paused but current state is "
+ rollout.getStatus().name().toLowerCase());
}
mergedRollout.setStatus(RolloutStatus.RUNNING);
rolloutRepository.save(mergedRollout);
}
void resumeRollout(@NotNull Rollout rollout);
/**
* Checking running rollouts. Rollouts which are checked updating the
@@ -549,211 +242,30 @@ public class RolloutManagement {
* this check. This check is only applied if the last check is
* less than (lastcheck-delay).
*/
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED)
@Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
public void checkRunningRollouts(final long delayBetweenChecks) {
verifyStuckedRollouts();
final long lastCheck = System.currentTimeMillis();
final int updated = rolloutRepository.updateLastCheck(lastCheck, delayBetweenChecks, RolloutStatus.RUNNING);
if (updated == 0) {
// nothing to check, maybe another instance already checked in
// between
LOGGER.debug("No rolloutcheck necessary for current scheduled check {}, next check at {}", lastCheck,
lastCheck + delayBetweenChecks);
return;
}
final List<Rollout> rolloutsToCheck = rolloutRepository.findByLastCheckAndStatus(lastCheck,
RolloutStatus.RUNNING);
LOGGER.info("Found {} running rollouts to check", rolloutsToCheck.size());
for (final Rollout rollout : rolloutsToCheck) {
LOGGER.debug("Checking rollout {}", rollout);
final List<RolloutGroup> rolloutGroups = rolloutGroupRepository.findByRolloutAndStatus(rollout,
RolloutGroupStatus.RUNNING);
if (rolloutGroups.isEmpty()) {
// no running rollouts, probably there was an error
// somewhere at the latest group. And the latest group has
// been switched from running into error state. So we need
// to find the latest group which
executeLatestRolloutGroup(rollout);
} else {
LOGGER.debug("Rollout {} has {} running groups", rollout.getId(), rolloutGroups.size());
executeRolloutGroups(rollout, rolloutGroups);
}
if (isRolloutComplete(rollout)) {
LOGGER.info("Rollout {} is finished, setting finished status", rollout);
rollout.setStatus(RolloutStatus.FINISHED);
rolloutRepository.save(rollout);
}
}
}
void checkRunningRollouts(long delayBetweenChecks);
/**
* Verifies and handles stucked rollouts in asynchronous creation or
* starting state. If rollouts are created or started asynchronously it
* might be that they keep in state {@link RolloutStatus#CREATING} or
* {@link RolloutStatus#STARTING} due database or application interruption.
* In case this happens, set the rollout to error state.
*/
private void verifyStuckedRollouts() {
final List<Rollout> rolloutsInCreatingState = rolloutRepository.findByStatus(RolloutStatus.CREATING);
rolloutsInCreatingState.stream().filter(rollout -> !creatingRollouts.contains(rollout.getName()))
.forEach(rollout -> {
LOGGER.warn(
"Determined error during rollout creation of rollout {}, stucking in creating state, setting to status",
rollout, RolloutStatus.ERROR_CREATING);
rollout.setStatus(RolloutStatus.ERROR_CREATING);
rolloutRepository.save(rollout);
});
final List<Rollout> rolloutsInStartingState = rolloutRepository.findByStatus(RolloutStatus.STARTING);
rolloutsInStartingState.stream().filter(rollout -> !startingRollouts.contains(rollout.getName()))
.forEach(rollout -> {
LOGGER.warn(
"Determined error during rollout starting of rollout {}, stucking in starting state, setting to status",
rollout, RolloutStatus.ERROR_STARTING);
rollout.setStatus(RolloutStatus.ERROR_STARTING);
rolloutRepository.save(rollout);
});
}
private void executeRolloutGroups(final Rollout rollout, final List<RolloutGroup> rolloutGroups) {
for (final RolloutGroup rolloutGroup : rolloutGroups) {
// error state check, do we need to stop the whole
// rollout because of error?
final RolloutGroupErrorCondition errorCondition = rolloutGroup.getErrorCondition();
final boolean isError = checkErrorState(rollout, rolloutGroup, errorCondition);
if (isError) {
LOGGER.info("Rollout {} {} has error, calling error action", rollout.getName(), rollout.getId());
callErrorAction(rollout, rolloutGroup);
} else {
// not in error so check finished state, do we need to
// start the next group?
final RolloutGroupSuccessCondition finishedCondition = rolloutGroup.getSuccessCondition();
checkFinishCondition(rollout, rolloutGroup, finishedCondition);
if (isRolloutGroupComplete(rollout, rolloutGroup)) {
rolloutGroup.setStatus(RolloutGroupStatus.FINISHED);
rolloutGroupRepository.save(rolloutGroup);
}
}
}
}
private void executeLatestRolloutGroup(final Rollout rollout) {
final List<RolloutGroup> latestRolloutGroup = rolloutGroupRepository
.findByRolloutAndStatusNotOrderByIdDesc(rollout, RolloutGroupStatus.SCHEDULED);
if (latestRolloutGroup.isEmpty()) {
return;
}
executeRolloutGroupSuccessAction(rollout, latestRolloutGroup.get(0));
}
private void callErrorAction(final Rollout rollout, final RolloutGroup rolloutGroup) {
try {
context.getBean(rolloutGroup.getErrorAction().getBeanName(), RolloutGroupActionEvaluator.class)
.eval(rollout, rolloutGroup, rolloutGroup.getErrorActionExp());
} catch (final BeansException e) {
LOGGER.error("Something bad happend when accessing the error action bean {}",
rolloutGroup.getErrorAction().getBeanName(), e);
}
}
private boolean isRolloutComplete(final Rollout rollout) {
final Long groupsActiveLeft = rolloutGroupRepository.countByRolloutAndStatusOrStatus(rollout,
RolloutGroupStatus.RUNNING, RolloutGroupStatus.SCHEDULED);
return groupsActiveLeft == 0;
}
private boolean isRolloutGroupComplete(final Rollout rollout, final RolloutGroup rolloutGroup) {
final Long actionsLeftForRollout = actionRepository
.countByRolloutAndRolloutGroupAndStatusNotAndStatusNotAndStatusNot(rollout, rolloutGroup,
Action.Status.ERROR, Action.Status.FINISHED, Action.Status.CANCELED);
return actionsLeftForRollout == 0;
}
private boolean checkErrorState(final Rollout rollout, final RolloutGroup rolloutGroup,
final RolloutGroupErrorCondition errorCondition) {
if (errorCondition == null) {
// there is no error condition, so return false, don't have error.
return false;
}
try {
return context.getBean(errorCondition.getBeanName(), RolloutGroupConditionEvaluator.class).eval(rollout,
rolloutGroup, rolloutGroup.getErrorConditionExp());
} catch (final BeansException e) {
LOGGER.error("Something bad happend when accessing the error condition bean {}",
errorCondition.getBeanName(), e);
return false;
}
}
private boolean checkFinishCondition(final Rollout rollout, final RolloutGroup rolloutGroup,
final RolloutGroupSuccessCondition finishCondition) {
LOGGER.trace("Checking finish condition {} on rolloutgroup {}", finishCondition, rolloutGroup);
try {
final boolean isFinished = context
.getBean(finishCondition.getBeanName(), RolloutGroupConditionEvaluator.class)
.eval(rollout, rolloutGroup, rolloutGroup.getSuccessConditionExp());
if (isFinished) {
LOGGER.info("Rolloutgroup {} is finished, starting next group", rolloutGroup);
executeRolloutGroupSuccessAction(rollout, rolloutGroup);
} else {
LOGGER.debug("Rolloutgroup {} is still running", rolloutGroup);
}
return isFinished;
} catch (final BeansException e) {
LOGGER.error("Something bad happend when accessing the finish condition bean {}",
finishCondition.getBeanName(), e);
return false;
}
}
private void executeRolloutGroupSuccessAction(final Rollout rollout, final RolloutGroup rolloutGroup) {
context.getBean(rolloutGroup.getSuccessAction().getBeanName(), RolloutGroupActionEvaluator.class).eval(rollout,
rolloutGroup, rolloutGroup.getSuccessActionExp());
}
/**
* Counts all {@link Target}s in the repository.
* Counts all {@link Rollout}s in the repository.
*
* @return number of targets
* @return number of roll outs
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
public Long countRolloutsAll() {
return rolloutRepository.count();
}
Long countRolloutsAll();
/**
* Count rollouts by specified filter text.
* Count rollouts by given text in name or description.
*
* @param searchText
* name or description
* @return total count rollouts for specified filter text.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
public Long countRolloutsAllByFilters(final String searchText) {
return rolloutRepository.count(likeNameOrDescription(searchText));
}
private static Specification<Rollout> likeNameOrDescription(final String searchText) {
return (rolloutRoot, query, criteriaBuilder) -> {
final String searchTextToLower = searchText.toLowerCase();
return criteriaBuilder.or(
criteriaBuilder.like(criteriaBuilder.lower(rolloutRoot.get(Rollout_.name)), searchTextToLower),
criteriaBuilder.like(criteriaBuilder.lower(rolloutRoot.get(Rollout_.description)),
searchTextToLower));
};
}
Long countRolloutsAllByFilters(@NotEmpty String searchText);
/**
* * Retrieves a specific rollout by its ID.
* Finds rollouts by given text in name or description.
*
* @param pageable
* the page request to sort and limit the result
@@ -763,12 +275,7 @@ public class RolloutManagement {
* not exists
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
public Slice<Rollout> findRolloutByFilters(final Pageable pageable, @NotEmpty final String searchText) {
final Specification<Rollout> specs = likeNameOrDescription(searchText);
final Slice<Rollout> findAll = criteriaNoCountDao.findAll(specs, pageable, Rollout.class);
setRolloutStatusDetails(findAll);
return findAll;
}
Slice<Rollout> findRolloutByFilters(@NotNull Pageable pageable, @NotEmpty String searchText);
/**
* Retrieves a specific rollout by its name.
@@ -779,9 +286,7 @@ public class RolloutManagement {
* does not exists
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
public Rollout findRolloutByName(final String rolloutName) {
return rolloutRepository.findByName(rolloutName);
}
Rollout findRolloutByName(@NotNull String rolloutName);
/**
* Update rollout details.
@@ -791,14 +296,8 @@ public class RolloutManagement {
*
* @return Rollout updated rollout
*/
@NotNull
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
public Rollout updateRollout(@NotNull final Rollout rollout) {
Assert.notNull(rollout.getId());
return rolloutRepository.save(rollout);
}
Rollout updateRollout(@NotNull Rollout rollout);
/**
* Get count of targets in different status in rollout.
@@ -810,12 +309,7 @@ public class RolloutManagement {
*
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
public Page<Rollout> findAllRolloutsWithDetailedStatus(final Pageable page) {
final Page<Rollout> rollouts = findAll(page);
setRolloutStatusDetails(rollouts);
return rollouts;
}
Page<Rollout> findAllRolloutsWithDetailedStatus(@NotNull Pageable page);
/**
* Get count of targets in different status in rollout.
@@ -826,40 +320,7 @@ public class RolloutManagement {
*
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
public Rollout findRolloutWithDetailedStatus(final Long rolloutId) {
final Rollout rollout = findRolloutById(rolloutId);
final List<TotalTargetCountActionStatus> rolloutStatusCountItems = actionRepository
.getStatusCountByRolloutId(rolloutId);
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems,
rollout.getTotalTargets());
rollout.setTotalTargetCountStatus(totalTargetCountStatus);
return rollout;
}
private Map<Long, List<TotalTargetCountActionStatus>> getStatusCountItemForRollout(final List<Long> rolloutIds) {
final List<TotalTargetCountActionStatus> resultList = actionRepository.getStatusCountByRolloutId(rolloutIds);
return resultList.stream().collect(Collectors.groupingBy(TotalTargetCountActionStatus::getId));
}
private void setRolloutStatusDetails(final Slice<Rollout> rollouts) {
final List<Long> rolloutIds = rollouts.getContent().stream().map(rollout -> rollout.getId())
.collect(Collectors.toList());
final Map<Long, List<TotalTargetCountActionStatus>> allStatesForRollout = getStatusCountItemForRollout(
rolloutIds);
for (final Rollout rollout : rollouts) {
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
allStatesForRollout.get(rollout.getId()), rollout.getTotalTargets());
rollout.setTotalTargetCountStatus(totalTargetCountStatus);
}
}
private static void checkIfRolloutCanStarted(final Rollout rollout, final Rollout mergedRollout) {
if (!(RolloutStatus.READY.equals(mergedRollout.getStatus()))) {
throw new RolloutIllegalStateException("Rollout can only be started in state ready but current state is "
+ rollout.getStatus().name().toLowerCase());
}
}
Rollout findRolloutWithDetailedStatus(@NotNull Long rolloutId);
/***
* Get finished percentage details for a specified group which is in running
@@ -871,16 +332,7 @@ public class RolloutManagement {
* the ID of the {@link RolloutGroup}
* @return percentage finished
*/
public float getFinishedPercentForRunningGroup(final Long rolloutId, final RolloutGroup rolloutGroup) {
final Long totalGroup = rolloutGroup.getTotalTargets();
final Long finished = actionRepository.countByRolloutIdAndRolloutGroupIdAndStatus(rolloutId,
rolloutGroup.getId(), Action.Status.FINISHED);
if (totalGroup == 0) {
// in case e.g. targets has been deleted we don't have any actions
// left for this group, so the group is finished
return 100;
}
// calculate threshold
return ((float) finished / (float) totalGroup) * 100;
}
}
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
float getFinishedPercentForRunningGroup(@NotNull Long rolloutId, @NotNull RolloutGroup rolloutGroup);
}

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository;
import java.util.List;
import org.eclipse.hawkbit.repository.jpa.SystemManagement;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;

View File

@@ -1,11 +1,3 @@
/**
* 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;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
@@ -13,39 +5,11 @@ import org.eclipse.hawkbit.repository.model.TenantConfiguration;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationValidatorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationContext;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
/**
* Central tenant configuration management operations of the SP server.
*/
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated
@Service
public class TenantConfigurationManagement implements EnvironmentAware {
@Autowired
private TenantConfigurationRepository tenantConfigurationRepository;
@Autowired
private ApplicationContext applicationContext;
private final ConfigurableConversionService conversionService = new DefaultConversionService();
private Environment environment;
public interface TenantConfigurationManagement {
/**
* Retrieves a configuration value from the e.g. tenant overwritten
@@ -70,37 +34,10 @@ public class TenantConfigurationManagement implements EnvironmentAware {
* if the property cannot be converted to the given
* {@code propertyType}
*/
@Cacheable(value = "tenantConfiguration", key = "#configurationKey.getKeyName()")
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
public <T> TenantConfigurationValue<T> getConfigurationValue(final TenantConfigurationKey configurationKey,
final Class<T> propertyType) {
validateTenantConfigurationDataType(configurationKey, propertyType);
final TenantConfiguration tenantConfiguration = tenantConfigurationRepository
.findByKey(configurationKey.getKeyName());
return buildTenantConfigurationValueByKey(configurationKey, propertyType, tenantConfiguration);
}
/**
* Validates the data type of the tenant configuration. If it is possible to
* cast to the given data type.
*
* @param configurationKey
* the key
* @param propertyType
* the class
*/
protected <T> void validateTenantConfigurationDataType(final TenantConfigurationKey configurationKey,
final Class<T> propertyType) {
if (!configurationKey.getDataType().isAssignableFrom(propertyType)) {
throw new TenantConfigurationValidatorException(
String.format("Cannot parse the database value of type %s into the type %s.",
configurationKey.getDataType(), propertyType));
}
}
<T> TenantConfigurationValue<T> getConfigurationValue(TenantConfigurationKey configurationKey,
Class<T> propertyType);
/**
* Build the tenant configuration by the given key
@@ -114,24 +51,10 @@ public class TenantConfigurationManagement implements EnvironmentAware {
* @return <null> if no default value is set and no database value available
* or returns the tenant configuration value
*/
protected <T> TenantConfigurationValue<T> buildTenantConfigurationValueByKey(
final TenantConfigurationKey configurationKey, final Class<T> propertyType,
final TenantConfiguration tenantConfiguration) {
if (tenantConfiguration != null) {
return TenantConfigurationValue.<T> builder().isGlobal(false).createdBy(tenantConfiguration.getCreatedBy())
.createdAt(tenantConfiguration.getCreatedAt())
.lastModifiedAt(tenantConfiguration.getLastModifiedAt())
.lastModifiedBy(tenantConfiguration.getLastModifiedBy())
.value(conversionService.convert(tenantConfiguration.getValue(), propertyType)).build();
} else if (configurationKey.getDefaultKeyName() != null) {
return TenantConfigurationValue.<T> builder().isGlobal(true).createdBy(null).createdAt(null)
.lastModifiedAt(null).lastModifiedBy(null)
.value(getGlobalConfigurationValue(configurationKey, propertyType)).build();
}
return null;
}
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
<T> TenantConfigurationValue<T> buildTenantConfigurationValueByKey(TenantConfigurationKey configurationKey,
Class<T> propertyType, TenantConfiguration tenantConfiguration);
/**
* Retrieves a configuration value from the e.g. tenant overwritten
@@ -153,9 +76,7 @@ public class TenantConfigurationManagement implements EnvironmentAware {
*/
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
public TenantConfigurationValue<?> getConfigurationValue(final TenantConfigurationKey configurationKey) {
return getConfigurationValue(configurationKey, configurationKey.getDataType());
}
TenantConfigurationValue<?> getConfigurationValue(TenantConfigurationKey configurationKey);
/**
* returns the global configuration property either defined in the property
@@ -179,23 +100,7 @@ public class TenantConfigurationManagement implements EnvironmentAware {
*/
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
public <T> T getGlobalConfigurationValue(final TenantConfigurationKey configurationKey,
final Class<T> propertyType) {
if (!configurationKey.getDataType().isAssignableFrom(propertyType)) {
throw new TenantConfigurationValidatorException(
String.format("Cannot parse the database value of type %s into the type %s.",
configurationKey.getDataType(), propertyType));
}
final T valueInProperties = environment.getProperty(configurationKey.getDefaultKeyName(), propertyType);
if (valueInProperties == null) {
return conversionService.convert(configurationKey.getDefaultValue(), propertyType);
}
return valueInProperties;
}
<T> T getGlobalConfigurationValue(TenantConfigurationKey configurationKey, Class<T> propertyType);
/**
* Adds or updates a specific configuration for a specific tenant.
@@ -213,41 +118,8 @@ public class TenantConfigurationManagement implements EnvironmentAware {
* @throws ConversionFailedException
* if the property cannot be converted to the given
*/
@CacheEvict(value = "tenantConfiguration", key = "#configurationKey.getKeyName()")
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
public <T> TenantConfigurationValue<T> addOrUpdateConfiguration(final TenantConfigurationKey configurationKey,
final T value) {
if (!configurationKey.getDataType().isAssignableFrom(value.getClass())) {
throw new TenantConfigurationValidatorException(String.format(
"Cannot parse the value %s of type %s into the type %s defined by the configuration key.", value,
value.getClass(), configurationKey.getDataType()));
}
configurationKey.validate(applicationContext, value);
TenantConfiguration tenantConfiguration = tenantConfigurationRepository
.findByKey(configurationKey.getKeyName());
if (tenantConfiguration == null) {
tenantConfiguration = new TenantConfiguration(configurationKey.getKeyName(), value.toString());
} else {
tenantConfiguration.setValue(value.toString());
}
final TenantConfiguration updatedTenantConfiguration = tenantConfigurationRepository.save(tenantConfiguration);
final Class<T> clazzT = (Class<T>) value.getClass();
return TenantConfigurationValue.<T> builder().isGlobal(false)
.createdBy(updatedTenantConfiguration.getCreatedBy())
.createdAt(updatedTenantConfiguration.getCreatedAt())
.lastModifiedAt(updatedTenantConfiguration.getLastModifiedAt())
.lastModifiedBy(updatedTenantConfiguration.getLastModifiedBy())
.value(conversionService.convert(updatedTenantConfiguration.getValue(), clazzT)).build();
}
<T> TenantConfigurationValue<T> addOrUpdateConfiguration(TenantConfigurationKey configurationKey, T value);
/**
* Deletes a specific configuration for the current tenant. Does nothing in
@@ -256,16 +128,7 @@ public class TenantConfigurationManagement implements EnvironmentAware {
* @param configurationKey
* the configuration key to be deleted
*/
@CacheEvict(value = "tenantConfiguration", key = "#configurationKey.getKeyName()")
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
public void deleteConfiguration(final TenantConfigurationKey configurationKey) {
tenantConfigurationRepository.deleteByKey(configurationKey.getKeyName());
}
void deleteConfiguration(TenantConfigurationKey configurationKey);
@Override
public void setEnvironment(final Environment environment) {
this.environment = environment;
}
}
}

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import java.util.Collection;
import java.util.List;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import java.io.Serializable;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DsMetadataCompositeKey;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import java.util.Collection;
import java.util.List;
@@ -27,8 +27,6 @@ import org.springframework.transaction.annotation.Transactional;
/**
* {@link DistributionSet} repository.
*
*
*
*/
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public interface DistributionSetRepository

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import java.util.List;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import java.util.Collection;
import java.util.List;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import org.eclipse.hawkbit.repository.model.ExternalArtifactProvider;
import org.springframework.transaction.annotation.Isolation;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import org.eclipse.hawkbit.repository.model.ExternalArtifact;
import org.springframework.data.domain.Page;

View File

@@ -0,0 +1,270 @@
/**
* 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.io.InputStream;
import java.util.List;
import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
import org.eclipse.hawkbit.artifact.repository.ArtifactStoreException;
import org.eclipse.hawkbit.artifact.repository.HashNotMatchException;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException;
import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.GridFSDBFileNotFoundException;
import org.eclipse.hawkbit.repository.exception.InvalidMD5HashException;
import org.eclipse.hawkbit.repository.exception.InvalidSHA1HashException;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.ExternalArtifact;
import org.eclipse.hawkbit.repository.model.ExternalArtifactProvider;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.specifications.SoftwareModuleSpecification;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
/**
* JPA based {@link ArtifactManagement} implementation.
*
*/
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated
@Service
public class JpaArtifactManagement implements ArtifactManagement {
private static final Logger LOG = LoggerFactory.getLogger(JpaArtifactManagement.class);
@Autowired
private LocalArtifactRepository localArtifactRepository;
@Autowired
private ExternalArtifactRepository externalArtifactRepository;
@Autowired
private SoftwareModuleRepository softwareModuleRepository;
@Autowired
private ExternalArtifactProviderRepository externalArtifactProviderRepository;
@Autowired
private ArtifactRepository artifactRepository;
private static LocalArtifact checkForExistingArtifact(final String filename, final boolean overrideExisting,
final SoftwareModule softwareModule) {
if (softwareModule.getLocalArtifactByFilename(filename).isPresent()) {
if (overrideExisting) {
LOG.debug("overriding existing artifact with new filename {}", filename);
return softwareModule.getLocalArtifactByFilename(filename).get();
} else {
throw new EntityAlreadyExistsException("File with that name already exists in the Software Module");
}
}
return null;
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public ExternalArtifact createExternalArtifact(final ExternalArtifactProvider externalRepository,
final String urlSuffix, final Long moduleId) {
final SoftwareModule module = getModuleAndThrowExceptionIfThatFails(moduleId);
return externalArtifactRepository.save(new ExternalArtifact(externalRepository, urlSuffix, module));
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public ExternalArtifactProvider createExternalArtifactProvider(final String name, final String description,
final String basePath, final String defaultUrlSuffix) {
return externalArtifactProviderRepository
.save(new ExternalArtifactProvider(name, description, basePath, defaultUrlSuffix));
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public LocalArtifact createLocalArtifact(final InputStream stream, final Long moduleId, final String filename,
final String providedMd5Sum, final String providedSha1Sum, final boolean overrideExisting,
final String contentType) {
DbArtifact result = null;
final SoftwareModule softwareModule = getModuleAndThrowExceptionIfThatFails(moduleId);
final LocalArtifact existing = checkForExistingArtifact(filename, overrideExisting, softwareModule);
try {
result = artifactRepository.store(stream, filename, contentType,
new DbArtifactHash(providedSha1Sum, providedMd5Sum));
} catch (final ArtifactStoreException e) {
throw new ArtifactUploadFailedException(e);
} catch (final HashNotMatchException e) {
if (e.getHashFunction().equals(HashNotMatchException.SHA1)) {
throw new InvalidSHA1HashException(e.getMessage(), e);
} else {
throw new InvalidMD5HashException(e.getMessage(), e);
}
}
if (result == null) {
return null;
}
return storeArtifactMetadata(softwareModule, filename, result, existing);
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void deleteExternalArtifact(final Long id) {
final ExternalArtifact existing = externalArtifactRepository.findOne(id);
if (null == existing) {
return;
}
existing.getSoftwareModule().removeArtifact(existing);
softwareModuleRepository.save(existing.getSoftwareModule());
externalArtifactRepository.delete(id);
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void deleteLocalArtifact(final LocalArtifact existing) {
if (existing == null) {
return;
}
boolean artifactIsOnlyUsedByOneSoftwareModule = true;
for (final LocalArtifact lArtifact : localArtifactRepository
.findByGridFsFileName(existing.getGridFsFileName())) {
if (!lArtifact.getSoftwareModule().isDeleted()
&& Long.compare(lArtifact.getSoftwareModule().getId(), existing.getSoftwareModule().getId()) != 0) {
artifactIsOnlyUsedByOneSoftwareModule = false;
break;
}
}
if (artifactIsOnlyUsedByOneSoftwareModule) {
try {
LOG.debug("deleting artifact from repository {}", existing.getGridFsFileName());
artifactRepository.deleteBySha1(existing.getGridFsFileName());
} catch (final ArtifactStoreException e) {
throw new ArtifactDeleteFailedException(e);
}
}
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void deleteLocalArtifact(final Long id) {
final LocalArtifact existing = localArtifactRepository.findOne(id);
if (null == existing) {
return;
}
deleteLocalArtifact(existing);
existing.getSoftwareModule().removeArtifact(existing);
softwareModuleRepository.save(existing.getSoftwareModule());
localArtifactRepository.delete(id);
}
@Override
public Artifact findArtifact(final Long id) {
return localArtifactRepository.findOne(id);
}
@Override
public List<LocalArtifact> findByFilenameAndSoftwareModule(final String filename, final Long softwareModuleId) {
return localArtifactRepository.findByFilenameAndSoftwareModuleId(filename, softwareModuleId);
}
@Override
public LocalArtifact findFirstLocalArtifactsBySHA1(final String sha1) {
return localArtifactRepository.findFirstByGridFsFileName(sha1);
}
@Override
public List<LocalArtifact> findLocalArtifactByFilename(final String filename) {
return localArtifactRepository.findByFilename(filename);
}
@Override
public Page<LocalArtifact> findLocalArtifactBySoftwareModule(final Pageable pageReq, final Long swId) {
return localArtifactRepository.findBySoftwareModuleId(pageReq, swId);
}
@Override
public SoftwareModule findSoftwareModuleById(final Long id) {
final Specification<SoftwareModule> spec = SoftwareModuleSpecification.byId(id);
return softwareModuleRepository.findOne(spec);
}
@Override
public SoftwareModule findSoftwareModuleWithDetails(final Long id) {
final SoftwareModule result = findSoftwareModuleById(id);
if (result != null) {
result.getArtifacts().size();
}
return result;
}
private SoftwareModule getModuleAndThrowExceptionIfThatFails(final Long moduleId) {
final SoftwareModule softwareModule = findSoftwareModuleWithDetails(moduleId);
if (softwareModule == null) {
LOG.debug("no software module with ID {} exists", moduleId);
throw new EntityNotFoundException("Software Module: " + moduleId);
}
return softwareModule;
}
@Override
public DbArtifact loadLocalArtifactBinary(final LocalArtifact artifact) {
final DbArtifact result = artifactRepository.getArtifactBySha1(artifact.getGridFsFileName());
if (result == null) {
throw new GridFSDBFileNotFoundException(artifact.getGridFsFileName());
}
return result;
}
private LocalArtifact storeArtifactMetadata(final SoftwareModule softwareModule, final String providedFilename,
final DbArtifact result, final LocalArtifact existing) {
LocalArtifact artifact = existing;
if (existing == null) {
artifact = new LocalArtifact(result.getHashes().getSha1(), providedFilename, softwareModule);
}
artifact.setMd5Hash(result.getHashes().getMd5());
artifact.setSha1Hash(result.getHashes().getSha1());
artifact.setSize(result.getSize());
LOG.debug("storing new artifact into repository {}", artifact);
return localArtifactRepository.save(artifact);
}
}

View File

@@ -0,0 +1,417 @@
/**
* 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.net.URI;
import java.util.List;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.ToManyAttributeEntriesException;
import org.eclipse.hawkbit.repository.exception.ToManyStatusEntriesException;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.ActionStatus_;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
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.repository.model.Target_;
import org.eclipse.hawkbit.repository.model.TenantConfiguration;
import org.eclipse.hawkbit.repository.specifications.ActionSpecifications;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
/**
* JPA based {@link ControllerManagement} implementation.
*
*/
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated
@Service
public class JpaControllerManagement implements ControllerManagement {
private static final Logger LOG = LoggerFactory.getLogger(ControllerManagement.class);
private static final Logger LOG_DOS = LoggerFactory.getLogger("server-security.dos");
@Autowired
private EntityManager entityManager;
@Autowired
private ActionRepository actionRepository;
@Autowired
private TargetRepository targetRepository;
@Autowired
private TargetManagement targetManagement;
@Autowired
private TargetInfoRepository targetInfoRepository;
@Autowired
private SoftwareModuleRepository softwareModuleRepository;
@Autowired
private ActionStatusRepository actionStatusRepository;
@Autowired
private HawkbitSecurityProperties securityProperties;
@Autowired
private TenantConfigurationRepository tenantConfigurationRepository;
@Autowired
private TenantConfigurationManagement tenantConfigurationManagement;
@Override
public String getPollingTime() {
final TenantConfigurationKey configurationKey = TenantConfigurationKey.POLLING_TIME_INTERVAL;
final Class<String> propertyType = String.class;
JpaTenantConfigurationManagement.validateTenantConfigurationDataType(configurationKey, propertyType);
final TenantConfiguration tenantConfiguration = tenantConfigurationRepository
.findByKey(configurationKey.getKeyName());
return tenantConfigurationManagement
.buildTenantConfigurationValueByKey(configurationKey, propertyType, tenantConfiguration).getValue();
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public Target updateLastTargetQuery(final String controllerId, final URI address) {
final Target target = targetRepository.findByControllerId(controllerId);
if (target == null) {
throw new EntityNotFoundException(controllerId);
}
return updateLastTargetQuery(target.getTargetInfo(), address).getTarget();
}
@Override
public Action getActionForDownloadByTargetAndSoftwareModule(final String controllerId,
final SoftwareModule module) {
final List<Action> action = actionRepository.findActionByTargetAndSoftwareModule(controllerId, module);
if (action.isEmpty() || action.get(0).isCancelingOrCanceled()) {
throw new EntityNotFoundException(
"No assigment found for module " + module.getId() + " to target " + controllerId);
}
return action.get(0);
}
@Override
public boolean hasTargetArtifactAssigned(final String controllerId, final LocalArtifact localArtifact) {
final Target target = targetRepository.findByControllerId(controllerId);
if (target == null) {
return false;
}
return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(target, localArtifact)) > 0;
}
@Override
public List<Action> findActionByTargetAndActive(final Target target) {
return actionRepository.findByTargetAndActiveOrderByIdAsc(target, true);
}
@Override
public List<SoftwareModule> findSoftwareModulesByDistributionSet(final DistributionSet distributionSet) {
return softwareModuleRepository.findByAssignedTo(distributionSet);
}
@Override
public Action findActionWithDetails(final Long actionId) {
return actionRepository.findById(actionId);
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public Target findOrRegisterTargetIfItDoesNotexist(final String controllerId, final URI address) {
final Specification<Target> spec = (targetRoot, query, cb) -> cb.equal(targetRoot.get(Target_.controllerId),
controllerId);
Target target = targetRepository.findOne(spec);
if (target == null) {
target = new Target(controllerId);
target.setDescription("Plug and Play target: " + controllerId);
target.setName(controllerId);
return targetManagement.createTarget(target, TargetUpdateStatus.REGISTERED, System.currentTimeMillis(),
address);
}
return updateLastTargetQuery(target.getTargetInfo(), address).getTarget();
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public TargetInfo updateTargetStatus(final TargetInfo targetInfo, final TargetUpdateStatus status,
final Long lastTargetQuery, final URI address) {
final TargetInfo mtargetInfo = entityManager.merge(targetInfo);
if (status != null) {
mtargetInfo.setUpdateStatus(status);
}
if (lastTargetQuery != null) {
mtargetInfo.setLastTargetQuery(lastTargetQuery);
}
if (address != null) {
mtargetInfo.setAddress(address.toString());
}
return targetInfoRepository.save(mtargetInfo);
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public Action addCancelActionStatus(final ActionStatus actionStatus) {
final Action action = actionStatus.getAction();
checkForToManyStatusEntries(action);
action.setStatus(actionStatus.getStatus());
switch (actionStatus.getStatus()) {
case WARNING:
case ERROR:
case RUNNING:
break;
case CANCELED:
case FINISHED:
// in case of successful cancellation we also report the success at
// the canceled action itself.
actionStatus.addMessage(
ControllerManagement.SERVER_MESSAGE_PREFIX + "Cancellation completion is finished sucessfully.");
DeploymentHelper.successCancellation(action, actionRepository, targetManagement, targetInfoRepository,
entityManager);
break;
case RETRIEVED:
actionStatus.addMessage(ControllerManagement.SERVER_MESSAGE_PREFIX + "Cancellation request retrieved.");
break;
default:
}
actionRepository.save(action);
actionStatusRepository.save(actionStatus);
return action;
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public Action addUpdateActionStatus(@NotNull final ActionStatus actionStatus) {
final Action action = actionStatus.getAction();
if (!action.isActive()) {
LOG.debug("Update of actionStatus {} for action {} not possible since action not active anymore.",
actionStatus.getId(), action.getId());
return action;
}
return handleAddUpdateActionStatus(actionStatus, action);
}
/**
* Sets {@link TargetUpdateStatus} based on given {@link ActionStatus}.
*
* @param actionStatus
* @param action
* @return
*/
private Action handleAddUpdateActionStatus(final ActionStatus actionStatus, final Action action) {
LOG.debug("addUpdateActionStatus for action {}", action.getId());
final Action mergedAction = entityManager.merge(action);
Target mergedTarget = mergedAction.getTarget();
// check for a potential DOS attack
checkForToManyStatusEntries(action);
switch (actionStatus.getStatus()) {
case ERROR:
mergedTarget = DeploymentHelper.updateTargetInfo(mergedTarget, TargetUpdateStatus.ERROR, false,
targetInfoRepository, entityManager);
handleErrorOnAction(mergedAction, mergedTarget);
break;
case FINISHED:
handleFinishedAndStoreInTargetStatus(mergedTarget, mergedAction);
break;
case CANCELED:
case WARNING:
case RUNNING:
DeploymentHelper.updateTargetInfo(mergedTarget, TargetUpdateStatus.PENDING, false, targetInfoRepository,
entityManager);
break;
default:
break;
}
actionStatusRepository.save(actionStatus);
LOG.debug("addUpdateActionStatus {} for target {} is finished.", action.getId(), mergedTarget.getId());
return actionRepository.save(mergedAction);
}
private void handleErrorOnAction(final Action mergedAction, final Target mergedTarget) {
mergedAction.setActive(false);
mergedAction.setStatus(Status.ERROR);
mergedTarget.setAssignedDistributionSet(null);
targetManagement.updateTarget(mergedTarget);
}
private void checkForToManyStatusEntries(final Action action) {
if (securityProperties.getDos().getMaxStatusEntriesPerAction() > 0) {
final Long statusCount = actionStatusRepository.countByAction(action);
if (statusCount >= securityProperties.getDos().getMaxStatusEntriesPerAction()) {
LOG_DOS.error(
"Potential denial of service (DOS) attack identfied. More status entries in the system than permitted ({})!",
securityProperties.getDos().getMaxStatusEntriesPerAction());
throw new ToManyStatusEntriesException(
String.valueOf(securityProperties.getDos().getMaxStatusEntriesPerAction()));
}
}
}
private void handleFinishedAndStoreInTargetStatus(final Target target, final Action action) {
action.setActive(false);
action.setStatus(Status.FINISHED);
final TargetInfo targetInfo = target.getTargetInfo();
final DistributionSet ds = entityManager.merge(action.getDistributionSet());
targetInfo.setInstalledDistributionSet(ds);
if (target.getAssignedDistributionSet() != null && targetInfo.getInstalledDistributionSet() != null && target
.getAssignedDistributionSet().getId().equals(targetInfo.getInstalledDistributionSet().getId())) {
targetInfo.setUpdateStatus(TargetUpdateStatus.IN_SYNC);
targetInfo.setInstallationDate(System.currentTimeMillis());
} else {
targetInfo.setUpdateStatus(TargetUpdateStatus.PENDING);
targetInfo.setInstallationDate(System.currentTimeMillis());
}
targetInfoRepository.save(targetInfo);
entityManager.detach(ds);
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public Target updateControllerAttributes(final String controllerId, final Map<String, String> data) {
final Target target = targetRepository.findByControllerId(controllerId);
if (target == null) {
throw new EntityNotFoundException(controllerId);
}
target.getTargetInfo().getControllerAttributes().putAll(data);
if (target.getTargetInfo().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()));
}
target.getTargetInfo().setLastTargetQuery(System.currentTimeMillis());
target.getTargetInfo().setRequestControllerAttributes(false);
return targetRepository.save(target);
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public Action registerRetrieved(final Action action, final String message) {
return handleRegisterRetrieved(action, message);
}
/**
* Registers retrieved status for given {@link Target} and {@link Action} if
* it does not exist yet.
*
* @param action
* to the handle status for
* @param message
* for the status
* @return the updated action in case the status has been changed to
* {@link Status#RETRIEVED}
*/
private Action handleRegisterRetrieved(final Action action, final String message) {
// do a manual query with CriteriaBuilder to avoid unnecessary field
// queries and an extra
// count query made by spring-data when using pageable requests, we
// don't need an extra count
// query, we just want to check if the last action status is a retrieved
// or not.
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> queryActionStatus = cb.createQuery(Object[].class);
final Root<ActionStatus> actionStatusRoot = queryActionStatus.from(ActionStatus.class);
final CriteriaQuery<Object[]> query = queryActionStatus
.multiselect(actionStatusRoot.get(ActionStatus_.id), actionStatusRoot.get(ActionStatus_.status))
.where(cb.equal(actionStatusRoot.get(ActionStatus_.action), action))
.orderBy(cb.desc(actionStatusRoot.get(ActionStatus_.id)));
final List<Object[]> resultList = entityManager.createQuery(query).setFirstResult(0).setMaxResults(1)
.getResultList();
// if the latest status is not in retrieve state then we add a retrieved
// state again, we want
// to document a deployment retrieved status and a cancel retrieved
// status, but multiple
// retrieves after the other we don't want to store to protect to
// overflood action status in
// case controller retrieves a action multiple times.
if (resultList.isEmpty() || resultList.get(0)[1] != Status.RETRIEVED) {
// document that the status has been retrieved
actionStatusRepository
.save(new ActionStatus(action, Status.RETRIEVED, System.currentTimeMillis(), message));
// don't change the action status itself in case the action is in
// canceling state otherwise
// we modify the action status and the controller won't get the
// cancel job anymore.
if (!action.isCancelingOrCanceled()) {
final Action actionMerge = entityManager.merge(action);
actionMerge.setStatus(Status.RETRIEVED);
return actionRepository.save(actionMerge);
}
}
return action;
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void addInformationalActionStatus(final ActionStatus statusMessage) {
actionStatusRepository.save(statusMessage);
}
@Override
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public String getSecurityTokenByControllerId(final String controllerId) {
final Target target = targetRepository.findByControllerId(controllerId);
return target != null ? target.getSecurityToken() : null;
}
}

View File

@@ -0,0 +1,652 @@
/**
* 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.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
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;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.Constants;
import org.eclipse.hawkbit.eventbus.event.CancelTargetAssignmentEvent;
import org.eclipse.hawkbit.eventbus.event.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.eventbus.event.TargetInfoUpdateEvent;
import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.DistributionSetAssignmentResult;
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.ForceQuitActionNotAllowedException;
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
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.Action_;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.DistributionSet_;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.Rollout_;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
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.specifications.TargetSpecifications;
import org.hibernate.validator.constraints.NotEmpty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import com.google.common.collect.Lists;
import com.google.common.eventbus.EventBus;
/**
* JPA implementation for DeploymentManagement.
*
*/
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated
@Service
public class JpaDeploymentManagement implements DeploymentManagement {
private static final Logger LOG = LoggerFactory.getLogger(JpaDeploymentManagement.class);
@Autowired
private EntityManager entityManager;
@Autowired
private ActionRepository actionRepository;
@Autowired
private DistributionSetRepository distributoinSetRepository;
@Autowired
private SoftwareModuleRepository softwareModuleRepository;
@Autowired
private TargetRepository targetRepository;
@Autowired
private ActionStatusRepository actionStatusRepository;
@Autowired
private TargetManagement targetManagement;
@Autowired
private TargetInfoRepository targetInfoRepository;
@Autowired
private AuditorAware<String> auditorProvider;
@Autowired
private EventBus eventBus;
@Autowired
private AfterTransactionCommitExecutor afterCommit;
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
@Modifying
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
public DistributionSetAssignmentResult assignDistributionSet(final DistributionSet pset,
final List<Target> targets) {
return assignDistributionSetByTargetId(pset,
targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList()),
ActionType.FORCED, Action.NO_FORCE_TIME);
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_COMMITTED)
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
public DistributionSetAssignmentResult assignDistributionSet(final Long dsID, final String... targetIDs) {
return assignDistributionSet(dsID, ActionType.FORCED, Action.NO_FORCE_TIME, targetIDs);
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
// Exception squid:S2095: see
// https://jira.sonarsource.com/browse/SONARJAVA-1478
@SuppressWarnings({ "squid:S2095" })
public DistributionSetAssignmentResult assignDistributionSet(final Long dsID, final ActionType actionType,
final long forcedTimestamp, final String... targetIDs) {
return assignDistributionSet(dsID, Arrays.stream(targetIDs)
.map(t -> new TargetWithActionType(t, actionType, forcedTimestamp)).collect(Collectors.toList()));
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_COMMITTED)
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
public DistributionSetAssignmentResult assignDistributionSet(final Long dsID,
final List<TargetWithActionType> targets) {
final DistributionSet set = distributoinSetRepository.findOne(dsID);
if (set == null) {
throw new EntityNotFoundException(
String.format("no %s with id %d found", DistributionSet.class.getSimpleName(), dsID));
}
return assignDistributionSetToTargets(set, targets, null, null);
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_COMMITTED)
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
public DistributionSetAssignmentResult assignDistributionSet(final Long dsID,
final List<TargetWithActionType> targets, final Rollout rollout, final RolloutGroup rolloutGroup) {
final DistributionSet set = distributoinSetRepository.findOne(dsID);
if (set == null) {
throw new EntityNotFoundException(
String.format("no %s with id %d found", DistributionSet.class.getSimpleName(), dsID));
}
return assignDistributionSetToTargets(set, targets, rollout, rolloutGroup);
}
/**
* method assigns the {@link DistributionSet} to all {@link Target}s by
* their IDs with a specific {@link ActionType} and {@code forcetime}.
*
* @param dsID
* the ID of the distribution set to assign
* @param targets
* a list of all targets and their action type
* @param rollout
* the rollout for this assignment
* @param rolloutGroup
* the rollout group for this assignment
* @return the assignment result
*
* @throw IncompleteDistributionSetException if mandatory
* {@link SoftwareModuleType} are not assigned as define by the
* {@link DistributionSetType}.
*/
private DistributionSetAssignmentResult assignDistributionSetToTargets(@NotNull final DistributionSet set,
final List<TargetWithActionType> targetsWithActionType, final Rollout rollout,
final RolloutGroup rolloutGroup) {
if (!set.isComplete()) {
throw new IncompleteDistributionSetException(
"Distribution set of type " + set.getType().getKey() + " is incomplete: " + set.getId());
}
final List<String> controllerIDs = targetsWithActionType.stream().map(TargetWithActionType::getTargetId)
.collect(Collectors.toList());
LOG.debug("assignDistribution({}) to {} targets", set, controllerIDs.size());
final Map<String, TargetWithActionType> targetsWithActionMap = targetsWithActionType.stream()
.collect(Collectors.toMap(TargetWithActionType::getTargetId, Function.identity()));
// split tIDs length into max entries in-statement because many database
// have constraint of max entries in in-statements e.g. Oracle with
// maximum 1000 elements, so we need to split the entries here and
// execute multiple statements we take the target only into account if
// the requested operation is no duplicate of a previous one
final List<Target> targets = Lists.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT).stream()
.map(ids -> targetRepository
.findAll(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(ids, set.getId())))
.flatMap(t -> t.stream()).collect(Collectors.toList());
if (targets.isEmpty()) {
// detaching as it is not necessary to persist the set itself
entityManager.detach(set);
// return with nothing as all targets had the DS already assigned
return new DistributionSetAssignmentResult(Collections.emptyList(), 0, targetsWithActionType.size(),
Collections.emptyList(), targetManagement);
}
final List<List<Long>> targetIds = Lists.partition(
targets.stream().map(Target::getId).collect(Collectors.toList()), Constants.MAX_ENTRIES_IN_STATEMENT);
// override all active actions and set them into canceling state, we
// need to remember which one we have been switched to canceling state
// because for targets which we have changed to canceling we don't want
// to publish the new action update event.
final Set<Long> targetIdsCancellList = new HashSet<>();
targetIds.forEach(ids -> targetIdsCancellList.addAll(overrideObsoleteUpdateActions(ids)));
// cancel all scheduled actions which are in-active, these actions were
// not active before and the manual assignment which has been done
// cancels the
targetIds.forEach(tIds -> actionRepository.switchStatus(Status.CANCELED, tIds, false, Status.SCHEDULED));
// set assigned distribution set and TargetUpdateStatus
final String currentUser;
if (auditorProvider != null) {
currentUser = auditorProvider.getCurrentAuditor();
} else {
currentUser = null;
}
targetIds.forEach(tIds -> targetRepository.setAssignedDistributionSet(set, System.currentTimeMillis(),
currentUser, tIds));
targetIds.forEach(tIds -> targetInfoRepository.setTargetUpdateStatus(TargetUpdateStatus.PENDING, tIds));
final Map<String, Action> targetIdsToActions = actionRepository
.save(targets.stream().map(t -> createTargetAction(targetsWithActionMap, t, set, rollout, rolloutGroup))
.collect(Collectors.toList()))
.stream().collect(Collectors.toMap(a -> a.getTarget().getControllerId(), Function.identity()));
// create initial action status when action is created so we remember
// the initial running status because we will change the status
// of the action itself and with this action status we have a nicer
// action history.
targetIdsToActions.values().forEach(action -> {
final ActionStatus actionStatus = new ActionStatus();
actionStatus.setAction(action);
actionStatus.setOccurredAt(action.getCreatedAt());
actionStatus.setStatus(Status.RUNNING);
actionStatusRepository.save(actionStatus);
});
// flush to get action IDs
entityManager.flush();
// collect updated target and actions IDs in order to return them
final DistributionSetAssignmentResult result = new DistributionSetAssignmentResult(
targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList()), targets.size(),
controllerIDs.size() - targets.size(),
targetIdsToActions.values().stream().map(Action::getId).collect(Collectors.toList()), targetManagement);
LOG.debug("assignDistribution({}) finished {}", set, result);
final List<SoftwareModule> softwareModules = softwareModuleRepository.findByAssignedTo(set);
// detaching as it is not necessary to persist the set itself
entityManager.detach(set);
sendDistributionSetAssignmentEvent(targets, targetIdsCancellList, targetIdsToActions, softwareModules);
return result;
}
private void sendDistributionSetAssignmentEvent(final List<Target> targets, final Set<Long> targetIdsCancellList,
final Map<String, Action> targetIdsToActions, final List<SoftwareModule> softwareModules) {
targets.stream().filter(t -> !!!targetIdsCancellList.contains(t.getId()))
.forEach(t -> assignDistributionSetEvent(t, targetIdsToActions.get(t.getControllerId()).getId(),
softwareModules));
}
private static Action createTargetAction(final Map<String, TargetWithActionType> targetsWithActionMap,
final Target target, final DistributionSet set, final Rollout rollout, final RolloutGroup rolloutGroup) {
final Action actionForTarget = new Action();
final TargetWithActionType targetWithActionType = targetsWithActionMap.get(target.getControllerId());
actionForTarget.setActionType(targetWithActionType.getActionType());
actionForTarget.setForcedTime(targetWithActionType.getForceTime());
actionForTarget.setActive(true);
actionForTarget.setStatus(Status.RUNNING);
actionForTarget.setTarget(target);
actionForTarget.setDistributionSet(set);
actionForTarget.setRollout(rollout);
actionForTarget.setRolloutGroup(rolloutGroup);
return actionForTarget;
}
/**
* Sends the {@link TargetAssignDistributionSetEvent} for a specific target
* to the {@link EventBus}.
*
* @param target
* the Target which has been assigned to a distribution set
* @param actionId
* the action id of the assignment
* @param softwareModules
* the software modules which have been assigned
*/
private void assignDistributionSetEvent(final Target target, final Long actionId,
final List<SoftwareModule> softwareModules) {
target.getTargetInfo().setUpdateStatus(TargetUpdateStatus.PENDING);
afterCommit.afterCommit(() -> {
eventBus.post(new TargetInfoUpdateEvent(target.getTargetInfo()));
eventBus.post(new TargetAssignDistributionSetEvent(target.getOptLockRevision(), target.getTenant(),
target.getControllerId(), actionId, softwareModules, target.getTargetInfo().getAddress(),
target.getSecurityToken()));
});
}
/**
* Removes {@link UpdateAction}s that are no longer necessary and sends
* cancellations to the controller.
*
* @param myTarget
* to override {@link UpdateAction}s
*/
private Set<Long> overrideObsoleteUpdateActions(final List<Long> targetsIds) {
final Set<Long> cancelledTargetIds = new HashSet<>();
// Figure out if there are potential target/action combinations that
// need to be considered
// for cancelation
final List<Action> activeActions = actionRepository
.findByActiveAndTargetIdInAndActionStatusNotEqualToAndDistributionSetRequiredMigrationStep(targetsIds,
Action.Status.CANCELING);
activeActions.forEach(action -> {
action.setStatus(Status.CANCELING);
// document that the status has been retrieved
actionStatusRepository.save(new ActionStatus(action, Status.CANCELING, System.currentTimeMillis(),
"manual cancelation requested"));
cancelAssignDistributionSetEvent(action.getTarget(), action.getId());
cancelledTargetIds.add(action.getTarget().getId());
});
actionRepository.save(activeActions);
return cancelledTargetIds;
}
private DistributionSetAssignmentResult assignDistributionSetByTargetId(@NotNull final DistributionSet set,
@NotEmpty final List<String> tIDs, final ActionType actionType, final long forcedTime) {
return assignDistributionSetToTargets(set, tIDs.stream()
.map(t -> new TargetWithActionType(t, actionType, forcedTime)).collect(Collectors.toList()), null,
null);
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_COMMITTED)
public Action cancelAction(final Action action, final Target target) {
LOG.debug("cancelAction({}, {})", action, target);
if (action.isCancelingOrCanceled()) {
throw new CancelActionNotAllowedException("Actions in canceling or canceled state cannot be canceled");
}
final Action myAction = entityManager.merge(action);
if (myAction.isActive()) {
LOG.debug("action ({}) was still active. Change to {}.", action, Status.CANCELING);
myAction.setStatus(Status.CANCELING);
// document that the status has been retrieved
actionStatusRepository.save(new ActionStatus(myAction, Status.CANCELING, System.currentTimeMillis(),
"manual cancelation requested"));
final Action saveAction = actionRepository.save(myAction);
cancelAssignDistributionSetEvent(target, myAction.getId());
return saveAction;
} else {
throw new CancelActionNotAllowedException(
"Action [id: " + action.getId() + "] is not active and cannot be canceled");
}
}
/**
* Sends the {@link CancelTargetAssignmentEvent} for a specific target to
* the {@link EventBus}.
*
* @param target
* the Target which has been assigned to a distribution set
* @param actionId
* the action id of the assignment
*/
private void cancelAssignDistributionSetEvent(final Target target, final Long actionId) {
afterCommit.afterCommit(() -> eventBus.post(new CancelTargetAssignmentEvent(target.getOptLockRevision(),
target.getTenant(), target.getControllerId(), actionId, target.getTargetInfo().getAddress())));
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_COMMITTED)
public Action forceQuitAction(final Action action) {
final Action mergedAction = entityManager.merge(action);
if (!mergedAction.isCancelingOrCanceled()) {
throw new ForceQuitActionNotAllowedException(
"Action [id: " + action.getId() + "] is not canceled yet and cannot be force quit");
}
if (!mergedAction.isActive()) {
throw new ForceQuitActionNotAllowedException(
"Action [id: " + action.getId() + "] is not active and cannot be force quit");
}
LOG.warn("action ({}) was still activ and has been force quite.", action);
// document that the status has been retrieved
actionStatusRepository.save(new ActionStatus(mergedAction, Status.CANCELED, System.currentTimeMillis(),
"A force quit has been performed."));
DeploymentHelper.successCancellation(mergedAction, actionRepository, targetManagement, targetInfoRepository,
entityManager);
return actionRepository.save(mergedAction);
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_COMMITTED)
public void createScheduledAction(final List<Target> targets, final DistributionSet distributionSet,
final ActionType actionType, final long forcedTime, final Rollout rollout,
final RolloutGroup rolloutGroup) {
// cancel all current scheduled actions for this target. E.g. an action
// is already scheduled and a next action is created then cancel the
// current scheduled action to cancel. E.g. a new scheduled action is
// created.
final List<Long> targetIds = targets.stream().map(t -> t.getId()).collect(Collectors.toList());
actionRepository.switchStatus(Action.Status.CANCELED, targetIds, false, Action.Status.SCHEDULED);
targets.forEach(target -> {
final Action action = new Action();
action.setTarget(target);
action.setActive(false);
action.setDistributionSet(distributionSet);
action.setActionType(actionType);
action.setForcedTime(forcedTime);
action.setStatus(Status.SCHEDULED);
action.setRollout(rollout);
action.setRolloutGroup(rolloutGroup);
actionRepository.save(action);
});
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_COMMITTED)
public Action startScheduledAction(final Action action) {
final Action mergedAction = entityManager.merge(action);
final Target mergedTarget = entityManager.merge(action.getTarget());
// check if we need to override running update actions
final Set<Long> overrideObsoleteUpdateActions = overrideObsoleteUpdateActions(
Collections.singletonList(action.getTarget().getId()));
final boolean hasDistributionSetAlreadyAssigned = targetRepository
.count(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(
Collections.singletonList(mergedTarget.getControllerId()),
action.getDistributionSet().getId())) == 0;
if (hasDistributionSetAlreadyAssigned) {
// the target has already the distribution set assigned, we don't
// need to start the scheduled action, just finished it.
mergedAction.setStatus(Status.FINISHED);
mergedAction.setActive(false);
return actionRepository.save(mergedAction);
}
mergedAction.setActive(true);
mergedAction.setStatus(Status.RUNNING);
final Action savedAction = actionRepository.save(mergedAction);
final ActionStatus actionStatus = new ActionStatus();
actionStatus.setAction(action);
actionStatus.setOccurredAt(action.getCreatedAt());
actionStatus.setStatus(Status.RUNNING);
actionStatusRepository.save(actionStatus);
mergedTarget.setAssignedDistributionSet(action.getDistributionSet());
final TargetInfo targetInfo = mergedTarget.getTargetInfo();
targetInfo.setUpdateStatus(TargetUpdateStatus.PENDING);
targetRepository.save(mergedTarget);
targetInfoRepository.save(targetInfo);
// in case we canceled an action before for this target, then don't fire
// assignment event
if (!overrideObsoleteUpdateActions.contains(savedAction.getId())) {
final List<SoftwareModule> softwareModules = softwareModuleRepository
.findByAssignedTo(action.getDistributionSet());
// send distribution set assignment event
assignDistributionSetEvent(mergedAction.getTarget(), mergedAction.getId(), softwareModules);
}
return savedAction;
}
@Override
public Action findAction(final Long actionId) {
return actionRepository.findOne(actionId);
}
@Override
public Action findActionWithDetails(final Long actionId) {
return actionRepository.findById(actionId);
}
@Override
public Slice<Action> findActionsByTarget(final Pageable pageable, final Target target) {
return actionRepository.findByTarget(pageable, target);
}
@Override
public List<Action> findActionsByTarget(final Target target) {
return actionRepository.findByTarget(target);
}
@Override
public List<ActionWithStatusCount> findActionsWithStatusCountByTargetOrderByIdDesc(final Target target) {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<ActionWithStatusCount> query = cb.createQuery(ActionWithStatusCount.class);
final Root<Action> actionRoot = query.from(Action.class);
final ListJoin<Action, ActionStatus> actionStatusJoin = actionRoot.join(Action_.actionStatus, JoinType.LEFT);
final Join<Action, DistributionSet> actionDsJoin = actionRoot.join(Action_.distributionSet);
final Join<Action, Rollout> actionRolloutJoin = actionRoot.join(Action_.rollout, JoinType.LEFT);
final CriteriaQuery<ActionWithStatusCount> multiselect = query.distinct(true).multiselect(
actionRoot.get(Action_.id), actionRoot.get(Action_.actionType), actionRoot.get(Action_.active),
actionRoot.get(Action_.forcedTime), actionRoot.get(Action_.status), actionRoot.get(Action_.createdAt),
actionRoot.get(Action_.lastModifiedAt), actionDsJoin.get(DistributionSet_.id),
actionDsJoin.get(DistributionSet_.name), actionDsJoin.get(DistributionSet_.version),
cb.count(actionStatusJoin), actionRolloutJoin.get(Rollout_.name));
multiselect.where(cb.equal(actionRoot.get(Action_.target), target));
multiselect.orderBy(cb.desc(actionRoot.get(Action_.id)));
multiselect.groupBy(actionRoot.get(Action_.id));
return entityManager.createQuery(multiselect).getResultList();
}
@Override
public Slice<Action> findActionsByTarget(final Specification<Action> specifiction, final Target target,
final Pageable pageable) {
return actionRepository.findAll((Specification<Action>) (root, query, cb) -> cb
.and(specifiction.toPredicate(root, query, cb), cb.equal(root.get(Action_.target), target)), pageable);
}
@Override
public Slice<Action> findActionsByTarget(final Target foundTarget, final Pageable pageable) {
return actionRepository.findByTarget(pageable, foundTarget);
}
@Override
public Page<Action> findActiveActionsByTarget(final Pageable pageable, final Target target) {
return actionRepository.findByActiveAndTarget(pageable, target, true);
}
@Override
public List<Action> findActiveActionsByTarget(final Target target) {
return actionRepository.findByActiveAndTarget(target, true);
}
@Override
public List<Action> findInActiveActionsByTarget(final Target target) {
return actionRepository.findByActiveAndTarget(target, false);
}
@Override
public Page<Action> findInActiveActionsByTarget(final Pageable pageable, final Target target) {
return actionRepository.findByActiveAndTarget(pageable, target, false);
}
@Override
public Long countActionsByTarget(final Target target) {
return actionRepository.countByTarget(target);
}
@Override
public Long countActionsByTarget(final Specification<Action> spec, final Target target) {
return actionRepository.count((root, query, cb) -> cb.and(spec.toPredicate(root, query, cb),
cb.equal(root.get(Action_.target), target)));
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public Action forceTargetAction(final Long actionId) {
final Action action = actionRepository.findOne(actionId);
if (action != null && !action.isForced()) {
action.setActionType(ActionType.FORCED);
return actionRepository.save(action);
}
return action;
}
@Override
public Page<ActionStatus> findActionStatusByAction(final Pageable pageReq, final Action action,
final boolean withMessages) {
if (withMessages) {
return actionStatusRepository.getByAction(pageReq, action);
} else {
return actionStatusRepository.findByAction(pageReq, action);
}
}
@Override
public List<Action> findActionsByRolloutGroupParentAndStatus(final Rollout rollout,
final RolloutGroup rolloutGroupParent, final Action.Status actionStatus) {
return actionRepository.findByRolloutAndRolloutGroupParentAndStatus(rollout, rolloutGroupParent, actionStatus);
}
@Override
public List<Action> findActionsByRolloutAndStatus(final Rollout rollout, final Action.Status actionStatus) {
return actionRepository.findByRolloutAndStatus(rollout, actionStatus);
}
}

View File

@@ -0,0 +1,663 @@
/**
* 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 static com.google.common.base.Preconditions.checkNotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.persistence.EntityManager;
import org.eclipse.hawkbit.eventbus.event.DistributionSetTagAssigmentResultEvent;
import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.DistributionSetFilter;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.DistributionSetFilter.DistributionSetFilterBuilder;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityLockedException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata_;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSetTagAssignmentResult;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.DistributionSet_;
import org.eclipse.hawkbit.repository.model.DsMetadataCompositeKey;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.specifications.DistributionSetSpecification;
import org.eclipse.hawkbit.repository.specifications.DistributionSetTypeSpecification;
import org.eclipse.hawkbit.repository.specifications.SpecificationsBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import com.google.common.base.Strings;
import com.google.common.eventbus.EventBus;
/**
* Business facade for managing the {@link DistributionSet}s.
*
*/
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated
@Service
public class JpaDistributionSetManagement implements DistributionSetManagement {
@Autowired
private EntityManager entityManager;
@Autowired
private DistributionSetRepository distributionSetRepository;
@Autowired
private TagManagement tagManagement;
@Autowired
private SystemManagement systemManagement;
@Autowired
private DistributionSetTypeRepository distributionSetTypeRepository;
@Autowired
private DistributionSetMetadataRepository distributionSetMetadataRepository;
@Autowired
private ActionRepository actionRepository;
@Autowired
private EventBus eventBus;
@Autowired
private AfterTransactionCommitExecutor afterCommit;
@Override
public DistributionSet findDistributionSetByIdWithDetails(final Long distid) {
return distributionSetRepository.findOne(DistributionSetSpecification.byId(distid));
}
@Override
public DistributionSet findDistributionSetById(final Long distid) {
return distributionSetRepository.findOne(distid);
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public DistributionSetTagAssignmentResult toggleTagAssignment(final Collection<Long> dsIds, final String tagName) {
final Iterable<DistributionSet> sets = findDistributionSetListWithDetails(dsIds);
final DistributionSetTag myTag = tagManagement.findDistributionSetTag(tagName);
DistributionSetTagAssignmentResult result;
final List<DistributionSet> toBeChangedDSs = new ArrayList<>();
for (final DistributionSet set : sets) {
if (set.getTags().add(myTag)) {
toBeChangedDSs.add(set);
}
}
// un-assignment case
if (toBeChangedDSs.isEmpty()) {
for (final DistributionSet set : sets) {
if (set.getTags().remove(myTag)) {
toBeChangedDSs.add(set);
}
}
result = new DistributionSetTagAssignmentResult(dsIds.size() - toBeChangedDSs.size(), 0,
toBeChangedDSs.size(), Collections.emptyList(), distributionSetRepository.save(toBeChangedDSs),
myTag);
} else {
result = new DistributionSetTagAssignmentResult(dsIds.size() - toBeChangedDSs.size(), toBeChangedDSs.size(),
0, distributionSetRepository.save(toBeChangedDSs), Collections.emptyList(), myTag);
}
final DistributionSetTagAssignmentResult resultAssignment = result;
afterCommit.afterCommit(() -> eventBus.post(new DistributionSetTagAssigmentResultEvent(resultAssignment)));
// no reason to persist the tag
entityManager.detach(myTag);
return result;
}
@Override
public List<DistributionSet> findDistributionSetListWithDetails(final Collection<Long> distributionIdSet) {
return distributionSetRepository.findAll(DistributionSetSpecification.byIds(distributionIdSet));
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public DistributionSet updateDistributionSet(final DistributionSet ds) {
checkNotNull(ds.getId());
final DistributionSet persisted = findDistributionSetByIdWithDetails(ds.getId());
checkDistributionSetSoftwareModulesIsAllowedToModify(ds, persisted.getModules());
return distributionSetRepository.save(ds);
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void deleteDistributionSet(final Long... distributionSetIDs) {
final List<Long> toHardDelete = new ArrayList<>();
final List<Long> assigned = distributionSetRepository.findAssignedDistributionSetsById(distributionSetIDs);
// soft delete assigned
if (!assigned.isEmpty()) {
distributionSetRepository.deleteDistributionSet(assigned.toArray(new Long[assigned.size()]));
}
// mark the rest as hard delete
for (final Long setId : distributionSetIDs) {
if (!assigned.contains(setId)) {
toHardDelete.add(setId);
}
}
// hard delete the rest if exixts
if (!toHardDelete.isEmpty()) {
// don't give the delete statement an empty list, JPA/Oracle cannot
// handle the empty list
distributionSetRepository.deleteByIdIn(toHardDelete);
}
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public DistributionSet createDistributionSet(final DistributionSet dSet) {
prepareDsSave(dSet);
if (dSet.getType() == null) {
dSet.setType(systemManagement.getTenantMetadata().getDefaultDsType());
}
return distributionSetRepository.save(dSet);
}
private void prepareDsSave(final DistributionSet dSet) {
if (dSet.getId() != null) {
throw new EntityAlreadyExistsException("Parameter seems to be an existing, already persisted entity");
}
if (distributionSetRepository.countByNameAndVersion(dSet.getName(), dSet.getVersion()) > 0) {
throw new EntityAlreadyExistsException("DistributionSet with that name and version already exists.");
}
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public List<DistributionSet> createDistributionSets(final Collection<DistributionSet> distributionSets) {
for (final DistributionSet ds : distributionSets) {
prepareDsSave(ds);
if (ds.getType() == null) {
ds.setType(systemManagement.getTenantMetadata().getDefaultDsType());
}
}
return distributionSetRepository.save(distributionSets);
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public DistributionSet assignSoftwareModules(final DistributionSet ds, final Set<SoftwareModule> softwareModules) {
checkDistributionSetSoftwareModulesIsAllowedToModify(ds, softwareModules);
for (final SoftwareModule softwareModule : softwareModules) {
ds.addModule(softwareModule);
}
return distributionSetRepository.save(ds);
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public DistributionSet unassignSoftwareModule(final DistributionSet ds, final SoftwareModule softwareModule) {
final Set<SoftwareModule> softwareModules = new HashSet<>();
softwareModules.add(softwareModule);
ds.removeModule(softwareModule);
checkDistributionSetSoftwareModulesIsAllowedToModify(ds, softwareModules);
return distributionSetRepository.save(ds);
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public DistributionSetType updateDistributionSetType(final DistributionSetType dsType) {
checkNotNull(dsType.getId());
final DistributionSetType persisted = distributionSetTypeRepository.findOne(dsType.getId());
// throw exception if user tries to update a DS type that is already in
// use
if (!persisted.areModuleEntriesIdentical(dsType) && distributionSetRepository.countByType(persisted) > 0) {
throw new EntityReadOnlyException(
String.format("distribution set type %s set is already assigned to targets and cannot be changed",
dsType.getName()));
}
return distributionSetTypeRepository.save(dsType);
}
@Override
public Page<DistributionSetType> findDistributionSetTypesAll(final Specification<DistributionSetType> spec,
final Pageable pageable) {
return distributionSetTypeRepository.findAll(spec, pageable);
}
@Override
public Page<DistributionSetType> findDistributionSetTypesAll(final Pageable pageable) {
return distributionSetTypeRepository.findByDeleted(pageable, false);
}
@Override
public Page<DistributionSet> findDistributionSetsByFilters(final Pageable pageable,
final DistributionSetFilter distributionSetFilter) {
final List<Specification<DistributionSet>> specList = buildDistributionSetSpecifications(distributionSetFilter);
return findByCriteriaAPI(pageable, specList);
}
/**
*
* @param distributionSetFilter
* had details of filters to be applied
* @return a single DistributionSet which is either installed or assigned to
* a specific target or {@code null}.
*/
private DistributionSet findDistributionSetsByFiltersAndInstalledOrAssignedTarget(
final DistributionSetFilter distributionSetFilter) {
final List<Specification<DistributionSet>> specList = buildDistributionSetSpecifications(distributionSetFilter);
if (specList == null || specList.isEmpty()) {
return null;
}
return distributionSetRepository.findOne(SpecificationsBuilder.combineWithAnd(specList));
}
@Override
public Page<DistributionSet> findDistributionSetsAll(final Pageable pageReq, final Boolean deleted,
final Boolean complete) {
final List<Specification<DistributionSet>> specList = new ArrayList<>();
if (deleted != null) {
final Specification<DistributionSet> spec = DistributionSetSpecification.isDeleted(deleted);
specList.add(spec);
}
if (complete != null) {
final Specification<DistributionSet> spec = DistributionSetSpecification.isCompleted(complete);
specList.add(spec);
}
return findByCriteriaAPI(pageReq, specList);
}
@Override
public Page<DistributionSet> findDistributionSetsAll(final Specification<DistributionSet> spec,
final Pageable pageReq, final Boolean deleted) {
final List<Specification<DistributionSet>> specList = new ArrayList<>();
if (deleted != null) {
specList.add(DistributionSetSpecification.isDeleted(deleted));
}
specList.add(spec);
return findByCriteriaAPI(pageReq, specList);
}
@Override
public Page<DistributionSet> findDistributionSetsAllOrderedByLinkTarget(final Pageable pageable,
final DistributionSetFilterBuilder distributionSetFilterBuilder, final String assignedOrInstalled) {
final DistributionSetFilter filterWithInstalledTargets = distributionSetFilterBuilder
.setInstalledTargetId(assignedOrInstalled).setAssignedTargetId(null).build();
final DistributionSet installedDS = findDistributionSetsByFiltersAndInstalledOrAssignedTarget(
filterWithInstalledTargets);
final DistributionSetFilter filterWithAssignedTargets = distributionSetFilterBuilder.setInstalledTargetId(null)
.setAssignedTargetId(assignedOrInstalled).build();
final DistributionSet assignedDS = findDistributionSetsByFiltersAndInstalledOrAssignedTarget(
filterWithAssignedTargets);
final DistributionSetFilter dsFilterWithNoTargetLinked = distributionSetFilterBuilder.setInstalledTargetId(null)
.setAssignedTargetId(null).build();
// first fine the distribution sets filtered by the given filter
// parameters
final Page<DistributionSet> findDistributionSetsByFilters = findDistributionSetsByFilters(pageable,
dsFilterWithNoTargetLinked);
final List<DistributionSet> resultSet = new LinkedList<>(findDistributionSetsByFilters.getContent());
int orderIndex = 0;
if (installedDS != null) {
final boolean remove = resultSet.remove(installedDS);
if (!remove) {
resultSet.remove(resultSet.size() - 1);
}
resultSet.add(orderIndex, installedDS);
orderIndex++;
}
if (assignedDS != null && !assignedDS.equals(installedDS)) {
final boolean remove = resultSet.remove(assignedDS);
if (!remove) {
resultSet.remove(resultSet.size() - 1);
}
resultSet.add(orderIndex, assignedDS);
}
return new PageImpl<>(resultSet, pageable, findDistributionSetsByFilters.getTotalElements());
}
@Override
public DistributionSet findDistributionSetByNameAndVersion(final String distributionName, final String version) {
final Specification<DistributionSet> spec = DistributionSetSpecification
.equalsNameAndVersionIgnoreCase(distributionName, version);
return distributionSetRepository.findOne(spec);
}
@Override
public Iterable<DistributionSet> findDistributionSetList(final Collection<Long> dist) {
return distributionSetRepository.findAll(dist);
}
@Override
public Long countDistributionSetsAll() {
final List<Specification<DistributionSet>> specList = new ArrayList<>();
final Specification<DistributionSet> spec = DistributionSetSpecification.isDeleted(Boolean.FALSE);
specList.add(spec);
return distributionSetRepository.count(SpecificationsBuilder.combineWithAnd(specList));
}
@Override
public Long countDistributionSetTypesAll() {
return distributionSetTypeRepository.countByDeleted(false);
}
@Override
public DistributionSetType findDistributionSetTypeByName(final String name) {
return distributionSetTypeRepository.findOne(DistributionSetTypeSpecification.byName(name));
}
@Override
public DistributionSetType findDistributionSetTypeById(final Long id) {
return distributionSetTypeRepository.findOne(DistributionSetTypeSpecification.byId(id));
}
@Override
public DistributionSetType findDistributionSetTypeByKey(final String key) {
return distributionSetTypeRepository.findOne(DistributionSetTypeSpecification.byKey(key));
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public DistributionSetType createDistributionSetType(final DistributionSetType type) {
if (type.getId() != null) {
throw new EntityAlreadyExistsException("Given type contains an Id!");
}
return distributionSetTypeRepository.save(type);
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void deleteDistributionSetType(final DistributionSetType type) {
if (distributionSetRepository.countByType(type) > 0) {
final DistributionSetType toDelete = entityManager.merge(type);
toDelete.setDeleted(true);
distributionSetTypeRepository.save(toDelete);
} else {
distributionSetTypeRepository.delete(type.getId());
}
}
@Override
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
public DistributionSetMetadata createDistributionSetMetadata(final DistributionSetMetadata metadata) {
if (distributionSetMetadataRepository.exists(metadata.getId())) {
throwMetadataKeyAlreadyExists(metadata.getId().getKey());
}
// merge base distribution set so optLockRevision gets updated and audit
// log written because
// modifying metadata is modifying the base distribution set itself for
// auditing purposes.
entityManager.merge(metadata.getDistributionSet()).setLastModifiedAt(0L);
return distributionSetMetadataRepository.save(metadata);
}
@Override
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
public List<DistributionSetMetadata> createDistributionSetMetadata(
final Collection<DistributionSetMetadata> metadata) {
for (final DistributionSetMetadata distributionSetMetadata : metadata) {
checkAndThrowAlreadyIfDistributionSetMetadataExists(distributionSetMetadata.getId());
}
metadata.forEach(m -> entityManager.merge(m.getDistributionSet()).setLastModifiedAt(-1L));
return (List<DistributionSetMetadata>) distributionSetMetadataRepository.save(metadata);
}
@Override
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
public DistributionSetMetadata updateDistributionSetMetadata(final DistributionSetMetadata metadata) {
// check if exists otherwise throw entity not found exception
findOne(metadata.getId());
// touch it to update the lock revision because we are modifying the
// DS indirectly
entityManager.merge(metadata.getDistributionSet()).setLastModifiedAt(0L);
return distributionSetMetadataRepository.save(metadata);
}
@Override
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
public void deleteDistributionSetMetadata(final DsMetadataCompositeKey id) {
distributionSetMetadataRepository.delete(id);
}
@Override
public Page<DistributionSetMetadata> findDistributionSetMetadataByDistributionSetId(final Long distributionSetId,
final Pageable pageable) {
return distributionSetMetadataRepository.findAll(
(Specification<DistributionSetMetadata>) (root, query, cb) -> cb.equal(
root.get(DistributionSetMetadata_.distributionSet).get(DistributionSet_.id), distributionSetId),
pageable);
}
@Override
public Page<DistributionSetMetadata> findDistributionSetMetadataByDistributionSetId(final Long distributionSetId,
final Specification<DistributionSetMetadata> spec, final Pageable pageable) {
return distributionSetMetadataRepository
.findAll(
(Specification<DistributionSetMetadata>) (root, query,
cb) -> cb.and(
cb.equal(root.get(DistributionSetMetadata_.distributionSet)
.get(DistributionSet_.id), distributionSetId),
spec.toPredicate(root, query, cb)),
pageable);
}
@Override
public DistributionSetMetadata findOne(final DsMetadataCompositeKey id) {
final DistributionSetMetadata findOne = distributionSetMetadataRepository.findOne(id);
if (findOne == null) {
throw new EntityNotFoundException("Metadata with key '" + id.getKey() + "' does not exist");
}
return findOne;
}
@Override
public DistributionSet findDistributionSetByAction(final Action action) {
return distributionSetRepository.findByAction(action);
}
@Override
public boolean isDistributionSetInUse(final DistributionSet distributionSet) {
return actionRepository.countByDistributionSet(distributionSet) > 0;
}
private static List<Specification<DistributionSet>> buildDistributionSetSpecifications(
final DistributionSetFilter distributionSetFilter) {
final List<Specification<DistributionSet>> specList = new ArrayList<>();
Specification<DistributionSet> spec;
if (null != distributionSetFilter.getIsComplete()) {
spec = DistributionSetSpecification.isCompleted(distributionSetFilter.getIsComplete());
specList.add(spec);
}
if (null != distributionSetFilter.getIsDeleted()) {
spec = DistributionSetSpecification.isDeleted(distributionSetFilter.getIsDeleted());
specList.add(spec);
}
if (distributionSetFilter.getType() != null) {
spec = DistributionSetSpecification.byType(distributionSetFilter.getType());
specList.add(spec);
}
if (!Strings.isNullOrEmpty(distributionSetFilter.getSearchText())) {
spec = DistributionSetSpecification.likeNameOrDescriptionOrVersion(distributionSetFilter.getSearchText());
specList.add(spec);
}
if (isDSWithNoTagSelected(distributionSetFilter) || isTagsSelected(distributionSetFilter)) {
spec = DistributionSetSpecification.hasTags(distributionSetFilter.getTagNames(),
distributionSetFilter.getSelectDSWithNoTag());
specList.add(spec);
}
if (distributionSetFilter.getInstalledTargetId() != null) {
spec = DistributionSetSpecification.installedTarget(distributionSetFilter.getInstalledTargetId());
specList.add(spec);
}
if (distributionSetFilter.getAssignedTargetId() != null) {
spec = DistributionSetSpecification.assignedTarget(distributionSetFilter.getAssignedTargetId());
specList.add(spec);
}
return specList;
}
private void checkDistributionSetSoftwareModulesIsAllowedToModify(final DistributionSet distributionSet,
final Set<SoftwareModule> softwareModules) {
if (!new HashSet<SoftwareModule>(distributionSet.getModules()).equals(softwareModules)
&& actionRepository.countByDistributionSet(distributionSet) > 0) {
throw new EntityLockedException(
String.format("distribution set %s:%s is already assigned to targets and cannot be changed",
distributionSet.getName(), distributionSet.getVersion()));
}
}
private static Boolean isDSWithNoTagSelected(final DistributionSetFilter distributionSetFilter) {
if (distributionSetFilter.getSelectDSWithNoTag() != null && distributionSetFilter.getSelectDSWithNoTag()) {
return true;
}
return false;
}
private static Boolean isTagsSelected(final DistributionSetFilter distributionSetFilter) {
if (distributionSetFilter.getTagNames() != null && !distributionSetFilter.getTagNames().isEmpty()) {
return true;
}
return false;
}
/**
* executes findAll with the given {@link DistributionSet}
* {@link Specification}s.
*
* @param pageable
* paging parameter
* @param specList
* list of @link {@link Specification}
* @return the page with the found {@link DistributionSet}
*/
private Page<DistributionSet> findByCriteriaAPI(final Pageable pageable,
final List<Specification<DistributionSet>> specList) {
if (specList == null || specList.isEmpty()) {
return distributionSetRepository.findAll(pageable);
}
return distributionSetRepository.findAll(SpecificationsBuilder.combineWithAnd(specList), pageable);
}
private void checkAndThrowAlreadyIfDistributionSetMetadataExists(final DsMetadataCompositeKey metadataId) {
if (distributionSetMetadataRepository.exists(metadataId)) {
throw new EntityAlreadyExistsException(
"Metadata entry with key '" + metadataId.getKey() + "' already exists");
}
}
private static void throwMetadataKeyAlreadyExists(final String metadataKey) {
throw new EntityAlreadyExistsException("Metadata entry with key '" + metadataKey + "' already exists");
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public List<DistributionSet> assignTag(final Collection<Long> dsIds, final DistributionSetTag tag) {
final List<DistributionSet> allDs = findDistributionSetListWithDetails(dsIds);
allDs.forEach(ds -> ds.getTags().add(tag));
final List<DistributionSet> save = distributionSetRepository.save(allDs);
afterCommit.afterCommit(() -> {
final DistributionSetTagAssignmentResult result = new DistributionSetTagAssignmentResult(0, save.size(), 0,
save, Collections.emptyList(), tag);
eventBus.post(new DistributionSetTagAssigmentResultEvent(result));
});
return save;
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public List<DistributionSet> unAssignAllDistributionSetsByTag(final DistributionSetTag tag) {
return unAssignTag(tag.getAssignedToDistributionSet(), tag);
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public DistributionSet unAssignTag(final Long dsId, final DistributionSetTag distributionSetTag) {
final List<DistributionSet> allDs = findDistributionSetListWithDetails(Arrays.asList(dsId));
final List<DistributionSet> unAssignTag = unAssignTag(allDs, distributionSetTag);
return unAssignTag.isEmpty() ? null : unAssignTag.get(0);
}
private List<DistributionSet> unAssignTag(final Collection<DistributionSet> distributionSets,
final DistributionSetTag tag) {
distributionSets.forEach(ds -> ds.getTags().remove(tag));
return distributionSetRepository.save(distributionSets);
}
}

View File

@@ -0,0 +1,433 @@
/**
* 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.io.Serializable;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.persistence.EntityManager;
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;
import org.eclipse.hawkbit.report.model.DataReportSeries;
import org.eclipse.hawkbit.report.model.DataReportSeriesItem;
import org.eclipse.hawkbit.report.model.InnerOuterDataReportSeries;
import org.eclipse.hawkbit.report.model.SeriesTime;
import org.eclipse.hawkbit.repository.ReportManagement;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSet_;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetInfo_;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.Target_;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
/**
* Service layer for generating hawkBit reports.
*
*/
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated
@Service
public class JpaReportManagement implements ReportManagement {
@Value("${spring.jpa.database}")
private String databaseType;
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM");
private static final String H2_TARGET_CREATED_SQL_TEMPLATE = "SELECT TO_CHAR( DATEADD('second', target0_.created_at / 1000, DATE '1970-01-01'), '%s') AS col_0_0_, count(target0_.controller_id) AS col_1_0_ from sp_target target0_ WHERE TO_CHAR(DATEADD('second', target0_.created_at / 1000, DATE '1970-01-01'),'%s') BETWEEN TO_CHAR('%s', '%s') and TO_CHAR('%s', '%s') AND UPPER(target0_.tenant)=UPPER('%s') GROUP BY TO_CHAR(DATEADD('second', target0_.created_at / 1000, DATE '1970-01-01'), '%s')";
private static final String H2_CONTROLLER_FRRDBACK_SQL_TEMPLATE = "SELECT TO_CHAR(DATEADD('second', action_.created_at / 1000, DATE '1970-01-01'), '%s') AS col_0_0_, count(action_.id) AS col_1_0_ FROM sp_action action_ WHERE TO_CHAR(DATEADD('second', action_.created_at / 1000, DATE '1970-01-01'), '%s') BETWEEN TO_CHAR('%s', '%s') AND TO_CHAR('%s', '%s') AND UPPER(action_.tenant)=UPPER('%s') GROUP BY TO_CHAR(DATEADD('second', action_.created_at / 1000, DATE '1970-01-01'), '%s')";
private static final String MYSQL_TARGET_CREATED_SQL_TEMPLATE = "SELECT DATE_FORMAT(FROM_UNIXTIME(target0_.created_at / 1000), '%s') AS col_0_0_, COUNT(target0_.controller_id) AS col_1_0_ FROM sp_target target0_ WHERE DATE_FORMAT(FROM_UNIXTIME(target0_.created_at / 1000),'%s') BETWEEN DATE_FORMAT('%s', '%s') AND DATE_FORMAT('%s', '%s') AND UPPER(target0_.tenant)=UPPER('%s') GROUP BY DATE_FORMAT(FROM_UNIXTIME(target0_.created_at / 1000), '%s')";
private static final String MYSQL_CONTROLLER_FRRDBACK_SQL_TEMPLATE = "SELECT DATE_FORMAT(FROM_UNIXTIME(action_.created_at / 1000), '%s') AS col_0_0_, COUNT(action_.id) as col_1_0_ FROM sp_action action_ WHERE DATE_FORMAT(FROM_UNIXTIME(action_.created_at / 1000),'%s') BETWEEN DATE_FORMAT('%s', '%s') AND DATE_FORMAT('%s', '%s') AND UPPER(action_.tenant)=UPPER('%s') GROUP BY DATE_FORMAT(FROM_UNIXTIME(action_.created_at / 1000), '%s')";
private static final String MYSQL_DB_TYPE = "MYSQL";
private static final String H2_DB_TYPE = "H2";
@Autowired
private EntityManager entityManager;
@Autowired
private TenantAware tenantAware;
@Override
@Cacheable("targetStatus")
public DataReportSeries<TargetUpdateStatus> targetStatus() {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
final Root<Target> targetRoot = query.from(Target.class);
final Join<Target, TargetInfo> targetInfo = targetRoot.join(Target_.targetInfo);
final Expression<Long> countColumn = cb.count(targetInfo.get(TargetInfo_.targetId));
final CriteriaQuery<Object[]> multiselect = query
.multiselect(targetInfo.get(TargetInfo_.updateStatus), countColumn)
.groupBy(targetInfo.get(TargetInfo_.updateStatus))
.orderBy(cb.desc(targetInfo.get(TargetInfo_.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]))
.collect(Collectors.toList());
return new DataReportSeries<>("Target Status Overview", reportSeriesItems);
}
@Override
@Cacheable("distributionUsageAssigned")
public List<InnerOuterDataReportSeries<String>> distributionUsageAssigned(final int topXEntries) {
// top X entries distribution usage
final CriteriaBuilder cbTopX = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> queryTopX = cbTopX.createQuery(Object[].class);
final Root<DistributionSet> rootTopX = queryTopX.from(DistributionSet.class);
final ListJoin<DistributionSet, Target> joinTopX = rootTopX.join(DistributionSet_.assignedToTargets,
JoinType.LEFT);
final Expression<Long> countColumn = cbTopX.count(joinTopX);
// top x usage query
final CriteriaQuery<Object[]> groupBy = queryTopX
.multiselect(rootTopX.get(DistributionSet_.name), rootTopX.get(DistributionSet_.version), countColumn)
.where(cbTopX.equal(rootTopX.get(DistributionSet_.deleted), false))
.groupBy(rootTopX.get(DistributionSet_.name), rootTopX.get(DistributionSet_.version))
.orderBy(cbTopX.desc(countColumn), cbTopX.asc(rootTopX.get(DistributionSet_.name)));
// | col1 | col2 | col3 |
// | NAME | VER | COUNT |
final List<Object[]> resultListTop = entityManager.createQuery(groupBy).getResultList();
// end of top X entries distribution usage
return mapDistirbutionUsageResultToDataReport(topXEntries, resultListTop);
}
@Override
@Cacheable("distributionUsageInstalled")
public List<InnerOuterDataReportSeries<String>> distributionUsageInstalled(final int topXEntries) {
// top X entries distribution usage
final CriteriaBuilder cbTopX = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> queryTopX = cbTopX.createQuery(Object[].class);
final Root<DistributionSet> rootTopX = queryTopX.from(DistributionSet.class);
final ListJoin<DistributionSet, TargetInfo> joinTopX = rootTopX.join(DistributionSet_.installedAtTargets,
JoinType.LEFT);
final Expression<Long> countColumn = cbTopX.count(joinTopX);
// top x usage query
final CriteriaQuery<Object[]> groupBy = queryTopX
.multiselect(rootTopX.get(DistributionSet_.name), rootTopX.get(DistributionSet_.version), countColumn)
.where(cbTopX.equal(rootTopX.get(DistributionSet_.deleted), false))
.groupBy(rootTopX.get(DistributionSet_.name), rootTopX.get(DistributionSet_.version))
.orderBy(cbTopX.desc(countColumn), cbTopX.asc(rootTopX.get(DistributionSet_.name)));
// | col1 | col2 | col3 |
// | NAME | VER | COUNT |
final List<Object[]> resultListTop = entityManager.createQuery(groupBy).getResultList();
// end of top X entries distribution usage
return mapDistirbutionUsageResultToDataReport(topXEntries, resultListTop);
}
@Override
@Cacheable("targetsCreatedOverPeriod")
public <T extends Serializable> DataReportSeries<T> targetsCreatedOverPeriod(final DateType<T> dateType,
final LocalDateTime from, final LocalDateTime to) {
final Query createNativeQuery = entityManager
.createNativeQuery(getTargetsCreatedQueryTemplate(dateType, from, to));
final List<Object[]> resultList = createNativeQuery.getResultList();
final List<DataReportSeriesItem<T>> reportItems = resultList.stream()
.map(r -> new DataReportSeriesItem<>(dateType.format((String) r[0]), ((Number) r[1]).longValue()))
.collect(Collectors.toList());
return new DataReportSeries<>("CreatedTargets", reportItems);
}
private String getTargetsCreatedQueryTemplate(final DateType<?> dateType, final LocalDateTime from,
final LocalDateTime to) {
switch (databaseType) {
case H2_DB_TYPE:
return String.format(H2_TARGET_CREATED_SQL_TEMPLATE, dateTimeFormatToSqlFormat(dateType),
dateTimeFormatToSqlFormat(dateType), from.format(DATE_FORMAT), dateTimeFormatToSqlFormat(dateType),
to.format(DATE_FORMAT), dateTimeFormatToSqlFormat(dateType), tenantAware.getCurrentTenant(),
dateTimeFormatToSqlFormat(dateType));
case MYSQL_DB_TYPE:
return String.format(MYSQL_TARGET_CREATED_SQL_TEMPLATE, dateTimeFormatToSqlFormat(dateType),
dateTimeFormatToSqlFormat(dateType), from.toString(), dateTimeFormatToSqlFormat(dateType),
to.toString(), dateTimeFormatToSqlFormat(dateType), tenantAware.getCurrentTenant(),
dateTimeFormatToSqlFormat(dateType));
default:
return null;
}
}
private String getFeedbackReceivedQueryTemplate(final DateType<?> dateType, final LocalDateTime from,
final LocalDateTime to) {
switch (databaseType) {
case H2_DB_TYPE:
return String.format(H2_CONTROLLER_FRRDBACK_SQL_TEMPLATE, dateTimeFormatToSqlFormat(dateType),
dateTimeFormatToSqlFormat(dateType), from.format(DATE_FORMAT), dateTimeFormatToSqlFormat(dateType),
to.format(DATE_FORMAT), dateTimeFormatToSqlFormat(dateType), tenantAware.getCurrentTenant(),
dateTimeFormatToSqlFormat(dateType));
case MYSQL_DB_TYPE:
return String.format(MYSQL_CONTROLLER_FRRDBACK_SQL_TEMPLATE, dateTimeFormatToSqlFormat(dateType),
dateTimeFormatToSqlFormat(dateType), from.toString(), dateTimeFormatToSqlFormat(dateType),
to.toString(), dateTimeFormatToSqlFormat(dateType), tenantAware.getCurrentTenant(),
dateTimeFormatToSqlFormat(dateType));
default:
return null;
}
}
@Override
@Cacheable("feedbackReceivedOverTime")
public <T extends Serializable> DataReportSeries<T> feedbackReceivedOverTime(final DateType<T> dateType,
final LocalDateTime from, final LocalDateTime to) {
final Query createNativeQuery = entityManager
.createNativeQuery(getFeedbackReceivedQueryTemplate(dateType, from, to));
final List<Object[]> resultList = createNativeQuery.getResultList();
final List<DataReportSeriesItem<T>> reportItems = resultList.stream()
.map(r -> new DataReportSeriesItem<>(dateType.format((String) r[0]), ((Number) r[1]).longValue()))
.collect(Collectors.toList());
return new DataReportSeries<>("FeedbackRecieved", reportItems);
}
@Override
@Cacheable("targetsLastPoll")
public DataReportSeries<SeriesTime> targetsLastPoll() {
final LocalDateTime now = LocalDateTime.now();
final LocalDateTime beforeHour = now.minusHours(1);
final LocalDateTime beforeDay = now.minusDays(1);
final LocalDateTime beforeWeek = now.minusWeeks(1);
final LocalDateTime beforeMonth = now.minusMonths(1);
final LocalDateTime beforeYear = now.minusYears(1);
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final List<DataReportSeriesItem<SeriesTime>> resultList = new ArrayList<>();
// hours
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.HOUR,
entityManager.createQuery(createCountSelectTargetsLastPoll(cb, beforeHour, now)).getSingleResult()));
// days
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.DAY, entityManager
.createQuery(createCountSelectTargetsLastPoll(cb, beforeDay, beforeHour)).getSingleResult()));
// weeks
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.WEEK, entityManager
.createQuery(createCountSelectTargetsLastPoll(cb, beforeWeek, beforeDay)).getSingleResult()));
// months
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.MONTH, entityManager
.createQuery(createCountSelectTargetsLastPoll(cb, beforeMonth, beforeWeek)).getSingleResult()));
// years
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.YEAR, entityManager
.createQuery(createCountSelectTargetsLastPoll(cb, beforeYear, beforeMonth)).getSingleResult()));
// years
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.MORE_THAN_YEAR,
entityManager.createQuery(createCountSelectTargetsLastPoll(cb, null, beforeYear)).getSingleResult()));
// never
resultList.add(new DataReportSeriesItem<SeriesTime>(SeriesTime.NEVER,
entityManager.createQuery(createCountSelectTargetsLastPoll(cb, null, null)).getSingleResult()));
return new DataReportSeries<>("TargetLastPoll", resultList);
}
private CriteriaQuery<Long> createCountSelectTargetsLastPoll(final CriteriaBuilder cb, final LocalDateTime from,
final LocalDateTime to) {
Long start = null;
Long end = null;
if (from != null) {
start = from.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
}
if (to != null) {
end = to.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
}
// count select statement
final CriteriaQuery<Long> countSelect = cb.createQuery(Long.class);
final Root<Target> countSelectRoot = countSelect.from(Target.class);
final Join<Target, TargetInfo> targetInfoJoin = countSelectRoot.join(Target_.targetInfo);
countSelect.select(cb.count(countSelectRoot));
if (start != null && end != null) {
countSelect.where(cb.between(targetInfoJoin.get(TargetInfo_.lastTargetQuery), start, end));
} else if (from == null && to != null) {
countSelect.where(cb.lessThanOrEqualTo(targetInfoJoin.get(TargetInfo_.lastTargetQuery), end));
} else {
countSelect.where(cb.isNull(targetInfoJoin.get(TargetInfo_.lastTargetQuery)));
}
return countSelect;
}
private List<InnerOuterDataReportSeries<String>> mapDistirbutionUsageResultToDataReport(final int topXEntries,
final List<Object[]> resultListTop) {
final List<InnerOuterDataReportSeries<String>> innerOuterReport = new ArrayList<>();
final Map<DSName, InnerOuter> map = new LinkedHashMap<>();
int topXCounter = 0;
for (final Object[] objects : resultListTop) {
final boolean containsInnerOuter = map.containsKey(new DSName((String) objects[0]));
final String name = containsInnerOuter || topXCounter < topXEntries ? (String) objects[0] : null;
final DSName dsName = new DSName(name);
final String version = containsInnerOuter || topXCounter < topXEntries ? (String) objects[1] : null;
final Long count = (Long) objects[2];
InnerOuter innerouter = map.get(dsName);
if (innerouter == null) {
topXCounter++;
innerouter = new InnerOuter(dsName);
map.put(new DSName(name), innerouter);
}
innerouter.addOuter(new DSName(version), count);
}
for (final InnerOuter inner : map.values()) {
final List<DataReportSeriesItem<String>> outerReportItems = new ArrayList<>();
if (inner.name.getName() != null) {
for (final InnerOuter outer : inner.outer) {
outerReportItems.add(outer.toItem());
}
} else {
outerReportItems.add(new DataReportSeriesItem<String>("misc", inner.count));
}
innerOuterReport.add(new InnerOuterDataReportSeries<String>(
new DataReportSeries<>("DS-Name", Collections.singletonList(inner.toItem())),
new DataReportSeries<>("DS-Version", outerReportItems)));
}
return innerOuterReport;
}
private static final class InnerOuter {
final DSName name;
long count;
final List<InnerOuter> outer;
private InnerOuter(final DSName idName) {
name = idName;
outer = new ArrayList<>();
}
private InnerOuter(final DSName idName, final long count) {
name = idName;
this.count = count;
outer = new ArrayList<>();
}
private void addOuter(final DSName idName, final long count) {
outer.add(new InnerOuter(idName, count));
this.count += count;
}
private DataReportSeriesItem<String> toItem() {
return new DataReportSeriesItem<>(name.getName() != null ? name.getName() : "misc", count);
}
}
/**
* Object contains the name and the id of an entity.
*
*/
private static final class DSName {
private final String name;
/**
* @param id
* the ID of an entity
* @param name
* the name of an entity
*/
private DSName(final String name) {
this.name = name;
}
/**
* @return the name
*/
private String getName() {
return name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (name == null ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) { // NOSONAR - as this is
// generated
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DSName other = (DSName) obj;
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
return true;
}
@Override
public String toString() {
return "DSName [name=" + name + "]";
}
}
private String dateTimeFormatToSqlFormat(final DateType<?> datatype) {
switch (databaseType) {
case H2_DB_TYPE:
return datatype.h2Format();
case MYSQL_DB_TYPE:
return datatype.mySqlFormat();
default:
return null;
}
}
}

View File

@@ -0,0 +1,178 @@
/**
* 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 java.util.Map;
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;
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action_;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.RolloutGroup_;
import org.eclipse.hawkbit.repository.model.RolloutTargetGroup;
import org.eclipse.hawkbit.repository.model.RolloutTargetGroup_;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetWithActionStatus;
import org.eclipse.hawkbit.repository.model.Target_;
import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
import org.springframework.beans.factory.annotation.Autowired;
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.data.jpa.domain.Specification;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
/**
* RolloutGroupManagement to control rollout groups. This service secures all
* the functionality based on the {@link PreAuthorize} annotation on methods.
*/
@Validated
@Service
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public class JpaRolloutGroupManagement implements RolloutGroupManagement {
@Autowired
private RolloutGroupRepository rolloutGroupRepository;
@Autowired
private ActionRepository actionRepository;
@Autowired
private TargetRepository targetRepository;
@Autowired
private EntityManager entityManager;
@Override
public RolloutGroup findRolloutGroupById(final Long rolloutGroupId) {
return rolloutGroupRepository.findOne(rolloutGroupId);
}
@Override
public Page<RolloutGroup> findRolloutGroupsByRolloutId(final Long rolloutId, final Pageable page) {
return rolloutGroupRepository.findByRolloutId(rolloutId, page);
}
@Override
public Page<RolloutGroup> findRolloutGroupsAll(final Rollout rollout,
final Specification<RolloutGroup> specification, final Pageable page) {
return rolloutGroupRepository.findAll((root, query, criteriaBuilder) -> criteriaBuilder.and(
criteriaBuilder.equal(root.get(RolloutGroup_.rollout), rollout),
specification.toPredicate(root, query, criteriaBuilder)), page);
}
@Override
public Page<RolloutGroup> findAllRolloutGroupsWithDetailedStatus(final Long rolloutId, final Pageable page) {
final Page<RolloutGroup> rolloutGroups = rolloutGroupRepository.findByRolloutId(rolloutId, page);
final List<Long> rolloutGroupIds = rolloutGroups.getContent().stream().map(rollout -> rollout.getId())
.collect(Collectors.toList());
final Map<Long, List<TotalTargetCountActionStatus>> allStatesForRollout = getStatusCountItemForRolloutGroup(
rolloutGroupIds);
for (final RolloutGroup rolloutGroup : rolloutGroups) {
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
allStatesForRollout.get(rolloutGroup.getId()), rolloutGroup.getTotalTargets());
rolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus);
}
return rolloutGroups;
}
@Override
public RolloutGroup findRolloutGroupWithDetailedStatus(final Long rolloutGroupId) {
final RolloutGroup rolloutGroup = findRolloutGroupById(rolloutGroupId);
final List<TotalTargetCountActionStatus> rolloutStatusCountItems = actionRepository
.getStatusCountByRolloutGroupId(rolloutGroupId);
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems,
rolloutGroup.getTotalTargets());
rolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus);
return rolloutGroup;
}
private Map<Long, List<TotalTargetCountActionStatus>> getStatusCountItemForRolloutGroup(
final List<Long> rolloutGroupIds) {
final List<TotalTargetCountActionStatus> resultList = actionRepository
.getStatusCountByRolloutGroupId(rolloutGroupIds);
return resultList.stream().collect(Collectors.groupingBy(TotalTargetCountActionStatus::getId));
}
@Override
public Page<Target> findRolloutGroupTargets(final RolloutGroup rolloutGroup,
final Specification<Target> specification, final Pageable page) {
return targetRepository.findAll((root, query, criteriaBuilder) -> {
final ListJoin<Target, RolloutTargetGroup> rolloutTargetJoin = root.join(Target_.rolloutTargetGroup);
return criteriaBuilder.and(specification.toPredicate(root, query, criteriaBuilder),
criteriaBuilder.equal(rolloutTargetJoin.get(RolloutTargetGroup_.rolloutGroup), rolloutGroup));
}, page);
}
@Override
public Page<Target> findRolloutGroupTargets(final RolloutGroup rolloutGroup, final Pageable page) {
if (isRolloutStatusReady(rolloutGroup)) {
// in case of status ready the action has not been created yet and
// the relation information between target and rollout-group is
// stored in the #TargetRolloutGroup.
return targetRepository.findByRolloutTargetGroupRolloutGroupId(rolloutGroup.getId(), page);
}
return targetRepository.findByActionsRolloutGroup(rolloutGroup, page);
}
private static boolean isRolloutStatusReady(final RolloutGroup rolloutGroup) {
return rolloutGroup != null && RolloutStatus.READY.equals(rolloutGroup.getRollout().getStatus());
}
@Override
public Page<TargetWithActionStatus> findAllTargetsWithActionStatus(final PageRequest pageRequest,
final RolloutGroup rolloutGroup) {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
final CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
final Root<RolloutTargetGroup> targetRoot = query.distinct(true).from(RolloutTargetGroup.class);
final Join<RolloutTargetGroup, Target> targetJoin = targetRoot.join(RolloutTargetGroup_.target);
final ListJoin<RolloutTargetGroup, Action> actionJoin = targetRoot.join(RolloutTargetGroup_.actions,
JoinType.LEFT);
final Root<RolloutTargetGroup> countQueryFrom = countQuery.distinct(true).from(RolloutTargetGroup.class);
countQueryFrom.join(RolloutTargetGroup_.target);
countQueryFrom.join(RolloutTargetGroup_.actions, JoinType.LEFT);
countQuery.select(cb.count(countQueryFrom))
.where(cb.equal(countQueryFrom.get(RolloutTargetGroup_.rolloutGroup), rolloutGroup));
final Long totalCount = entityManager.createQuery(countQuery).getSingleResult();
final CriteriaQuery<Object[]> multiselect = query.multiselect(targetJoin, actionJoin.get(Action_.status))
.where(cb.equal(targetRoot.get(RolloutTargetGroup_.rolloutGroup), rolloutGroup));
final List<TargetWithActionStatus> targetWithActionStatus = entityManager.createQuery(multiselect)
.setFirstResult(pageRequest.getOffset()).setMaxResults(pageRequest.getPageSize()).getResultList()
.stream().map(o -> new TargetWithActionStatus((Target) o[0], (Action.Status) o[1]))
.collect(Collectors.toList());
return new PageImpl<>(targetWithActionStatus, pageRequest, totalCount);
}
}

View File

@@ -0,0 +1,653 @@
/**
* 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 java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
import javax.persistence.EntityManager;
import org.eclipse.hawkbit.cache.CacheWriteNotify;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupConditions;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorCondition;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
import org.eclipse.hawkbit.repository.model.RolloutTargetGroup;
import org.eclipse.hawkbit.repository.model.Rollout_;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
import org.eclipse.hawkbit.rollout.condition.RolloutGroupActionEvaluator;
import org.eclipse.hawkbit.rollout.condition.RolloutGroupConditionEvaluator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.Page;
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.data.jpa.repository.Modifying;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;
/**
* RolloutManagement to control rollouts e.g. like creating, starting, resuming
* and pausing rollouts. This service secures all the functionality based on the
* {@link PreAuthorize} annotation on methods.
*/
@Validated
@Service
@EnableScheduling
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public class JpaRolloutManagement implements RolloutManagement {
private static final Logger LOGGER = LoggerFactory.getLogger(RolloutManagement.class);
@Autowired
private EntityManager entityManager;
@Autowired
private RolloutRepository rolloutRepository;
@Autowired
private TargetManagement targetManagement;
@Autowired
private TargetRepository targetRepository;
@Autowired
private RolloutGroupRepository rolloutGroupRepository;
@Autowired
private DeploymentManagement deploymentManagement;
@Autowired
private RolloutTargetGroupRepository rolloutTargetGroupRepository;
@Autowired
private ActionRepository actionRepository;
@Autowired
private ApplicationContext context;
@Autowired
private NoCountPagingRepository criteriaNoCountDao;
@Autowired
private PlatformTransactionManager txManager;
@Autowired
private CacheWriteNotify cacheWriteNotify;
@Autowired
@Qualifier("asyncExecutor")
private Executor executor;
/*
* set which stores the rollouts which are asynchronously creating. This is
* necessary to verify rollouts which maybe stuck during creationg e.g.
* because of database interruption, failures or even application crash.
* !This is not cluster aware!
*/
private static final Set<String> creatingRollouts = ConcurrentHashMap.newKeySet();
/*
* set which stores the rollouts which are asynchronously starting. This is
* necessary to verify rollouts which maybe stuck during starting e.g.
* because of database interruption, failures or even application crash.
* !This is not cluster aware!
*/
private static final Set<String> startingRollouts = ConcurrentHashMap.newKeySet();
@Override
public Page<Rollout> findAll(final Pageable page) {
return rolloutRepository.findAll(page);
}
@Override
public Page<Rollout> findAllWithDetailedStatusByPredicate(final Specification<Rollout> specification,
final Pageable page) {
final Page<Rollout> findAll = rolloutRepository.findAll(specification, page);
setRolloutStatusDetails(findAll);
return findAll;
}
@Override
public Rollout findRolloutById(final Long rolloutId) {
return rolloutRepository.findOne(rolloutId);
}
@Override
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
public Rollout createRollout(final Rollout rollout, final int amountGroup,
final RolloutGroupConditions conditions) {
final Rollout savedRollout = createRollout(rollout, amountGroup);
return createRolloutGroups(amountGroup, conditions, savedRollout);
}
@Override
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
public Rollout createRolloutAsync(final Rollout rollout, final int amountGroup,
final RolloutGroupConditions conditions) {
final Rollout savedRollout = createRollout(rollout, amountGroup);
creatingRollouts.add(savedRollout.getName());
// need to flush the entity manager here to get the ID of the rollout,
// because entity manager is set to FlushMode#Auto, entitymanager will
// flush the Target entity, due the indirect relationship to the Rollout
// entity without set an ID JPA is throwing a Invalid
// 'org.springframework.dao.InvalidDataAccessApiUsageException: During
// synchronization aect was found through a relationship that was not
// marked cascade PERSIST'
entityManager.flush();
executor.execute(() -> {
try {
createRolloutGroupsInNewTransaction(amountGroup, conditions, savedRollout);
} finally {
creatingRollouts.remove(savedRollout.getName());
}
});
return savedRollout;
}
private Rollout createRollout(final Rollout rollout, final int amountGroup) {
verifyRolloutGroupParameter(amountGroup);
final Long totalTargets = targetManagement.countTargetByTargetFilterQuery(rollout.getTargetFilterQuery());
rollout.setTotalTargets(totalTargets.longValue());
return rolloutRepository.save(rollout);
}
private static void verifyRolloutGroupParameter(final int amountGroup) {
if (amountGroup <= 0) {
throw new IllegalArgumentException("the amountGroup must be greater than zero");
} else if (amountGroup > 500) {
throw new IllegalArgumentException("the amountGroup must not be greater than 500");
}
}
private Rollout createRolloutGroupsInNewTransaction(final int amountOfGroups,
final RolloutGroupConditions conditions, final Rollout savedRollout) {
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("creatingRollout");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
return new TransactionTemplate(txManager, def)
.execute(status -> createRolloutGroups(amountOfGroups, conditions, savedRollout));
}
/**
* Method for creating rollout groups and calculating group sizes. Group
* sizes are calculated by dividing the total count of targets through the
* amount of given groups. In same cases this will lead to less rollout
* groups than given by client.
*
* @param amountOfGroups
* the amount of groups
* @param conditions
* the rollout group conditions
* @param savedRollout
* the rollout
* @return the rollout with created groups
*/
private Rollout createRolloutGroups(final int amountOfGroups, final RolloutGroupConditions conditions,
final Rollout savedRollout) {
int pageIndex = 0;
int groupIndex = 0;
final Long totalCount = savedRollout.getTotalTargets();
final int groupSize = (int) Math.ceil((double) totalCount / (double) amountOfGroups);
// validate if the amount of groups that will be created are the amount
// of groups that the client what's to have created.
int amountGroupValidated = amountOfGroups;
final int amountGroupCreation = (int) (Math.ceil((double) totalCount / (double) groupSize));
if (amountGroupCreation == (amountOfGroups - 1)) {
amountGroupValidated--;
}
RolloutGroup lastSavedGroup = null;
while (pageIndex < totalCount) {
groupIndex++;
final String nameAndDesc = "group-" + groupIndex;
final RolloutGroup group = new RolloutGroup();
group.setName(nameAndDesc);
group.setDescription(nameAndDesc);
group.setRollout(savedRollout);
group.setParent(lastSavedGroup);
group.setSuccessCondition(conditions.getSuccessCondition());
group.setSuccessConditionExp(conditions.getSuccessConditionExp());
group.setErrorCondition(conditions.getErrorCondition());
group.setErrorConditionExp(conditions.getErrorConditionExp());
group.setErrorAction(conditions.getErrorAction());
group.setErrorActionExp(conditions.getErrorActionExp());
final RolloutGroup savedGroup = rolloutGroupRepository.save(group);
final Slice<Target> targetGroup = targetManagement.findTargetsAll(savedRollout.getTargetFilterQuery(),
new OffsetBasedPageRequest(pageIndex, groupSize, new Sort(Direction.ASC, "id")));
savedGroup.setTotalTargets(targetGroup.getContent().size());
lastSavedGroup = savedGroup;
targetGroup
.forEach(target -> rolloutTargetGroupRepository.save(new RolloutTargetGroup(savedGroup, target)));
cacheWriteNotify.rolloutGroupCreated(groupIndex, savedRollout.getId(), savedGroup.getId(),
amountGroupValidated, groupIndex);
pageIndex += groupSize;
}
savedRollout.setStatus(RolloutStatus.READY);
return rolloutRepository.save(savedRollout);
}
@Override
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
public Rollout startRollout(final Rollout rollout) {
final Rollout mergedRollout = entityManager.merge(rollout);
checkIfRolloutCanStarted(rollout, mergedRollout);
return doStartRollout(mergedRollout);
}
@Override
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
public Rollout startRolloutAsync(final Rollout rollout) {
final Rollout mergedRollout = entityManager.merge(rollout);
checkIfRolloutCanStarted(rollout, mergedRollout);
mergedRollout.setStatus(RolloutStatus.STARTING);
final Rollout updatedRollout = rolloutRepository.save(mergedRollout);
startingRollouts.add(updatedRollout.getName());
executor.execute(() -> {
try {
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("startingRollout");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
new TransactionTemplate(txManager, def).execute(status -> {
doStartRollout(updatedRollout);
return null;
});
} finally {
startingRollouts.remove(updatedRollout.getName());
}
});
return updatedRollout;
}
private Rollout doStartRollout(final Rollout rollout) {
final DistributionSet distributionSet = rollout.getDistributionSet();
final ActionType actionType = rollout.getActionType();
final long forceTime = rollout.getForcedTime();
final List<RolloutGroup> rolloutGroups = rolloutGroupRepository.findByRolloutOrderByIdAsc(rollout);
for (int iGroup = 0; iGroup < rolloutGroups.size(); iGroup++) {
final RolloutGroup rolloutGroup = rolloutGroups.get(iGroup);
final List<Target> targetGroup = targetRepository.findByRolloutTargetGroupRolloutGroup(rolloutGroup);
// firstgroup can already be started
if (iGroup == 0) {
final List<TargetWithActionType> targetsWithActionType = targetGroup.stream()
.map(t -> new TargetWithActionType(t.getControllerId(), actionType, forceTime))
.collect(Collectors.toList());
deploymentManagement.assignDistributionSet(distributionSet.getId(), targetsWithActionType, rollout,
rolloutGroup);
rolloutGroup.setStatus(RolloutGroupStatus.RUNNING);
} else {
// create only not active actions with status scheduled so they
// can be activated later
deploymentManagement.createScheduledAction(targetGroup, distributionSet, actionType, forceTime, rollout,
rolloutGroup);
rolloutGroup.setStatus(RolloutGroupStatus.SCHEDULED);
}
rolloutGroupRepository.save(rolloutGroup);
}
rollout.setStatus(RolloutStatus.RUNNING);
return rolloutRepository.save(rollout);
}
@Override
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
public void pauseRollout(final Rollout rollout) {
final Rollout mergedRollout = entityManager.merge(rollout);
if (mergedRollout.getStatus() != RolloutStatus.RUNNING) {
throw new RolloutIllegalStateException("Rollout can only be paused in state running but current state is "
+ rollout.getStatus().name().toLowerCase());
}
// setting the complete rollout only in paused state. This is sufficient
// due the currently running groups will be completed and new groups are
// not started until rollout goes back to running state again. The
// periodically check for running rollouts will skip rollouts in pause
// state.
mergedRollout.setStatus(RolloutStatus.PAUSED);
rolloutRepository.save(mergedRollout);
}
@Override
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
public void resumeRollout(final Rollout rollout) {
final Rollout mergedRollout = entityManager.merge(rollout);
if (!(RolloutStatus.PAUSED.equals(mergedRollout.getStatus()))) {
throw new RolloutIllegalStateException("Rollout can only be resumed in state paused but current state is "
+ rollout.getStatus().name().toLowerCase());
}
mergedRollout.setStatus(RolloutStatus.RUNNING);
rolloutRepository.save(mergedRollout);
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED)
@Modifying
public void checkRunningRollouts(final long delayBetweenChecks) {
verifyStuckedRollouts();
final long lastCheck = System.currentTimeMillis();
final int updated = rolloutRepository.updateLastCheck(lastCheck, delayBetweenChecks, RolloutStatus.RUNNING);
if (updated == 0) {
// nothing to check, maybe another instance already checked in
// between
LOGGER.debug("No rolloutcheck necessary for current scheduled check {}, next check at {}", lastCheck,
lastCheck + delayBetweenChecks);
return;
}
final List<Rollout> rolloutsToCheck = rolloutRepository.findByLastCheckAndStatus(lastCheck,
RolloutStatus.RUNNING);
LOGGER.info("Found {} running rollouts to check", rolloutsToCheck.size());
for (final Rollout rollout : rolloutsToCheck) {
LOGGER.debug("Checking rollout {}", rollout);
final List<RolloutGroup> rolloutGroups = rolloutGroupRepository.findByRolloutAndStatus(rollout,
RolloutGroupStatus.RUNNING);
if (rolloutGroups.isEmpty()) {
// no running rollouts, probably there was an error
// somewhere at the latest group. And the latest group has
// been switched from running into error state. So we need
// to find the latest group which
executeLatestRolloutGroup(rollout);
} else {
LOGGER.debug("Rollout {} has {} running groups", rollout.getId(), rolloutGroups.size());
executeRolloutGroups(rollout, rolloutGroups);
}
if (isRolloutComplete(rollout)) {
LOGGER.info("Rollout {} is finished, setting finished status", rollout);
rollout.setStatus(RolloutStatus.FINISHED);
rolloutRepository.save(rollout);
}
}
}
/**
* Verifies and handles stucked rollouts in asynchronous creation or
* starting state. If rollouts are created or started asynchronously it
* might be that they keep in state {@link RolloutStatus#CREATING} or
* {@link RolloutStatus#STARTING} due database or application interruption.
* In case this happens, set the rollout to error state.
*/
private void verifyStuckedRollouts() {
final List<Rollout> rolloutsInCreatingState = rolloutRepository.findByStatus(RolloutStatus.CREATING);
rolloutsInCreatingState.stream().filter(rollout -> !creatingRollouts.contains(rollout.getName()))
.forEach(rollout -> {
LOGGER.warn(
"Determined error during rollout creation of rollout {}, stucking in creating state, setting to status",
rollout, RolloutStatus.ERROR_CREATING);
rollout.setStatus(RolloutStatus.ERROR_CREATING);
rolloutRepository.save(rollout);
});
final List<Rollout> rolloutsInStartingState = rolloutRepository.findByStatus(RolloutStatus.STARTING);
rolloutsInStartingState.stream().filter(rollout -> !startingRollouts.contains(rollout.getName()))
.forEach(rollout -> {
LOGGER.warn(
"Determined error during rollout starting of rollout {}, stucking in starting state, setting to status",
rollout, RolloutStatus.ERROR_STARTING);
rollout.setStatus(RolloutStatus.ERROR_STARTING);
rolloutRepository.save(rollout);
});
}
private void executeRolloutGroups(final Rollout rollout, final List<RolloutGroup> rolloutGroups) {
for (final RolloutGroup rolloutGroup : rolloutGroups) {
// error state check, do we need to stop the whole
// rollout because of error?
final RolloutGroupErrorCondition errorCondition = rolloutGroup.getErrorCondition();
final boolean isError = checkErrorState(rollout, rolloutGroup, errorCondition);
if (isError) {
LOGGER.info("Rollout {} {} has error, calling error action", rollout.getName(), rollout.getId());
callErrorAction(rollout, rolloutGroup);
} else {
// not in error so check finished state, do we need to
// start the next group?
final RolloutGroupSuccessCondition finishedCondition = rolloutGroup.getSuccessCondition();
checkFinishCondition(rollout, rolloutGroup, finishedCondition);
if (isRolloutGroupComplete(rollout, rolloutGroup)) {
rolloutGroup.setStatus(RolloutGroupStatus.FINISHED);
rolloutGroupRepository.save(rolloutGroup);
}
}
}
}
private void executeLatestRolloutGroup(final Rollout rollout) {
final List<RolloutGroup> latestRolloutGroup = rolloutGroupRepository
.findByRolloutAndStatusNotOrderByIdDesc(rollout, RolloutGroupStatus.SCHEDULED);
if (latestRolloutGroup.isEmpty()) {
return;
}
executeRolloutGroupSuccessAction(rollout, latestRolloutGroup.get(0));
}
private void callErrorAction(final Rollout rollout, final RolloutGroup rolloutGroup) {
try {
context.getBean(rolloutGroup.getErrorAction().getBeanName(), RolloutGroupActionEvaluator.class)
.eval(rollout, rolloutGroup, rolloutGroup.getErrorActionExp());
} catch (final BeansException e) {
LOGGER.error("Something bad happend when accessing the error action bean {}",
rolloutGroup.getErrorAction().getBeanName(), e);
}
}
private boolean isRolloutComplete(final Rollout rollout) {
final Long groupsActiveLeft = rolloutGroupRepository.countByRolloutAndStatusOrStatus(rollout,
RolloutGroupStatus.RUNNING, RolloutGroupStatus.SCHEDULED);
return groupsActiveLeft == 0;
}
private boolean isRolloutGroupComplete(final Rollout rollout, final RolloutGroup rolloutGroup) {
final Long actionsLeftForRollout = actionRepository
.countByRolloutAndRolloutGroupAndStatusNotAndStatusNotAndStatusNot(rollout, rolloutGroup,
Action.Status.ERROR, Action.Status.FINISHED, Action.Status.CANCELED);
return actionsLeftForRollout == 0;
}
private boolean checkErrorState(final Rollout rollout, final RolloutGroup rolloutGroup,
final RolloutGroupErrorCondition errorCondition) {
if (errorCondition == null) {
// there is no error condition, so return false, don't have error.
return false;
}
try {
return context.getBean(errorCondition.getBeanName(), RolloutGroupConditionEvaluator.class).eval(rollout,
rolloutGroup, rolloutGroup.getErrorConditionExp());
} catch (final BeansException e) {
LOGGER.error("Something bad happend when accessing the error condition bean {}",
errorCondition.getBeanName(), e);
return false;
}
}
private boolean checkFinishCondition(final Rollout rollout, final RolloutGroup rolloutGroup,
final RolloutGroupSuccessCondition finishCondition) {
LOGGER.trace("Checking finish condition {} on rolloutgroup {}", finishCondition, rolloutGroup);
try {
final boolean isFinished = context
.getBean(finishCondition.getBeanName(), RolloutGroupConditionEvaluator.class)
.eval(rollout, rolloutGroup, rolloutGroup.getSuccessConditionExp());
if (isFinished) {
LOGGER.info("Rolloutgroup {} is finished, starting next group", rolloutGroup);
executeRolloutGroupSuccessAction(rollout, rolloutGroup);
} else {
LOGGER.debug("Rolloutgroup {} is still running", rolloutGroup);
}
return isFinished;
} catch (final BeansException e) {
LOGGER.error("Something bad happend when accessing the finish condition bean {}",
finishCondition.getBeanName(), e);
return false;
}
}
private void executeRolloutGroupSuccessAction(final Rollout rollout, final RolloutGroup rolloutGroup) {
context.getBean(rolloutGroup.getSuccessAction().getBeanName(), RolloutGroupActionEvaluator.class).eval(rollout,
rolloutGroup, rolloutGroup.getSuccessActionExp());
}
@Override
public Long countRolloutsAll() {
return rolloutRepository.count();
}
@Override
public Long countRolloutsAllByFilters(final String searchText) {
return rolloutRepository.count(likeNameOrDescription(searchText));
}
private static Specification<Rollout> likeNameOrDescription(final String searchText) {
return (rolloutRoot, query, criteriaBuilder) -> {
final String searchTextToLower = searchText.toLowerCase();
return criteriaBuilder.or(
criteriaBuilder.like(criteriaBuilder.lower(rolloutRoot.get(Rollout_.name)), searchTextToLower),
criteriaBuilder.like(criteriaBuilder.lower(rolloutRoot.get(Rollout_.description)),
searchTextToLower));
};
}
@Override
public Slice<Rollout> findRolloutByFilters(final Pageable pageable, final String searchText) {
final Specification<Rollout> specs = likeNameOrDescription(searchText);
final Slice<Rollout> findAll = criteriaNoCountDao.findAll(specs, pageable, Rollout.class);
setRolloutStatusDetails(findAll);
return findAll;
}
@Override
public Rollout findRolloutByName(final String rolloutName) {
return rolloutRepository.findByName(rolloutName);
}
/**
* Update rollout details.
*
* @param rollout
* rollout to be updated
*
* @return Rollout updated rollout
*/
@Override
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
public Rollout updateRollout(final Rollout rollout) {
Assert.notNull(rollout.getId());
return rolloutRepository.save(rollout);
}
/**
* Get count of targets in different status in rollout.
*
* @param page
* the page request to sort and limit the result
* @return a list of rollouts with details of targets count for different
* statuses
*
*/
@Override
public Page<Rollout> findAllRolloutsWithDetailedStatus(final Pageable page) {
final Page<Rollout> rollouts = findAll(page);
setRolloutStatusDetails(rollouts);
return rollouts;
}
@Override
public Rollout findRolloutWithDetailedStatus(final Long rolloutId) {
final Rollout rollout = findRolloutById(rolloutId);
final List<TotalTargetCountActionStatus> rolloutStatusCountItems = actionRepository
.getStatusCountByRolloutId(rolloutId);
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems,
rollout.getTotalTargets());
rollout.setTotalTargetCountStatus(totalTargetCountStatus);
return rollout;
}
private Map<Long, List<TotalTargetCountActionStatus>> getStatusCountItemForRollout(final List<Long> rolloutIds) {
final List<TotalTargetCountActionStatus> resultList = actionRepository.getStatusCountByRolloutId(rolloutIds);
return resultList.stream().collect(Collectors.groupingBy(TotalTargetCountActionStatus::getId));
}
private void setRolloutStatusDetails(final Slice<Rollout> rollouts) {
final List<Long> rolloutIds = rollouts.getContent().stream().map(rollout -> rollout.getId())
.collect(Collectors.toList());
final Map<Long, List<TotalTargetCountActionStatus>> allStatesForRollout = getStatusCountItemForRollout(
rolloutIds);
for (final Rollout rollout : rollouts) {
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
allStatesForRollout.get(rollout.getId()), rollout.getTotalTargets());
rollout.setTotalTargetCountStatus(totalTargetCountStatus);
}
}
private static void checkIfRolloutCanStarted(final Rollout rollout, final Rollout mergedRollout) {
if (!(RolloutStatus.READY.equals(mergedRollout.getStatus()))) {
throw new RolloutIllegalStateException("Rollout can only be started in state ready but current state is "
+ rollout.getStatus().name().toLowerCase());
}
}
@Override
public float getFinishedPercentForRunningGroup(final Long rolloutId, final RolloutGroup rolloutGroup) {
final Long totalGroup = rolloutGroup.getTotalTargets();
final Long finished = actionRepository.countByRolloutIdAndRolloutGroupIdAndStatus(rolloutId,
rolloutGroup.getId(), Action.Status.FINISHED);
if (totalGroup == 0) {
// in case e.g. targets has been deleted we don't have any actions
// left for this group, so the group is finished
return 100;
}
// calculate threshold
return ((float) finished / (float) totalGroup) * 100;
}
}

View File

@@ -0,0 +1,170 @@
/**
* 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 org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.model.TenantConfiguration;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationValidatorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationContext;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
/**
* Central tenant configuration management operations of the SP server.
*/
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated
@Service
public class JpaTenantConfigurationManagement implements EnvironmentAware, TenantConfigurationManagement {
@Autowired
private TenantConfigurationRepository tenantConfigurationRepository;
@Autowired
private ApplicationContext applicationContext;
private static final ConfigurableConversionService conversionService = new DefaultConversionService();
private Environment environment;
@Override
@Cacheable(value = "tenantConfiguration", key = "#configurationKey.getKeyName()")
public <T> TenantConfigurationValue<T> getConfigurationValue(final TenantConfigurationKey configurationKey,
final Class<T> propertyType) {
validateTenantConfigurationDataType(configurationKey, propertyType);
final TenantConfiguration tenantConfiguration = tenantConfigurationRepository
.findByKey(configurationKey.getKeyName());
return buildTenantConfigurationValueByKey(configurationKey, propertyType, tenantConfiguration);
}
/**
* Validates the data type of the tenant configuration. If it is possible to
* cast to the given data type.
*
* @param configurationKey
* the key
* @param propertyType
* the class
*/
static <T> void validateTenantConfigurationDataType(final TenantConfigurationKey configurationKey,
final Class<T> propertyType) {
if (!configurationKey.getDataType().isAssignableFrom(propertyType)) {
throw new TenantConfigurationValidatorException(
String.format("Cannot parse the database value of type %s into the type %s.",
configurationKey.getDataType(), propertyType));
}
}
@Override
public <T> TenantConfigurationValue<T> buildTenantConfigurationValueByKey(
final TenantConfigurationKey configurationKey, final Class<T> propertyType,
final TenantConfiguration tenantConfiguration) {
if (tenantConfiguration != null) {
return TenantConfigurationValue.<T> builder().isGlobal(false).createdBy(tenantConfiguration.getCreatedBy())
.createdAt(tenantConfiguration.getCreatedAt())
.lastModifiedAt(tenantConfiguration.getLastModifiedAt())
.lastModifiedBy(tenantConfiguration.getLastModifiedBy())
.value(conversionService.convert(tenantConfiguration.getValue(), propertyType)).build();
} else if (configurationKey.getDefaultKeyName() != null) {
return TenantConfigurationValue.<T> builder().isGlobal(true).createdBy(null).createdAt(null)
.lastModifiedAt(null).lastModifiedBy(null)
.value(getGlobalConfigurationValue(configurationKey, propertyType)).build();
}
return null;
}
@Override
public TenantConfigurationValue<?> getConfigurationValue(final TenantConfigurationKey configurationKey) {
return getConfigurationValue(configurationKey, configurationKey.getDataType());
}
@Override
public <T> T getGlobalConfigurationValue(final TenantConfigurationKey configurationKey,
final Class<T> propertyType) {
if (!configurationKey.getDataType().isAssignableFrom(propertyType)) {
throw new TenantConfigurationValidatorException(
String.format("Cannot parse the database value of type %s into the type %s.",
configurationKey.getDataType(), propertyType));
}
final T valueInProperties = environment.getProperty(configurationKey.getDefaultKeyName(), propertyType);
if (valueInProperties == null) {
return conversionService.convert(configurationKey.getDefaultValue(), propertyType);
}
return valueInProperties;
}
@Override
@CacheEvict(value = "tenantConfiguration", key = "#configurationKey.getKeyName()")
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
public <T> TenantConfigurationValue<T> addOrUpdateConfiguration(final TenantConfigurationKey configurationKey,
final T value) {
if (!configurationKey.getDataType().isAssignableFrom(value.getClass())) {
throw new TenantConfigurationValidatorException(String.format(
"Cannot parse the value %s of type %s into the type %s defined by the configuration key.", value,
value.getClass(), configurationKey.getDataType()));
}
configurationKey.validate(applicationContext, value);
TenantConfiguration tenantConfiguration = tenantConfigurationRepository
.findByKey(configurationKey.getKeyName());
if (tenantConfiguration == null) {
tenantConfiguration = new TenantConfiguration(configurationKey.getKeyName(), value.toString());
} else {
tenantConfiguration.setValue(value.toString());
}
final TenantConfiguration updatedTenantConfiguration = tenantConfigurationRepository.save(tenantConfiguration);
final Class<T> clazzT = (Class<T>) value.getClass();
return TenantConfigurationValue.<T> builder().isGlobal(false)
.createdBy(updatedTenantConfiguration.getCreatedBy())
.createdAt(updatedTenantConfiguration.getCreatedAt())
.lastModifiedAt(updatedTenantConfiguration.getLastModifiedAt())
.lastModifiedBy(updatedTenantConfiguration.getLastModifiedBy())
.value(conversionService.convert(updatedTenantConfiguration.getValue(), clazzT)).build();
}
@Override
@CacheEvict(value = "tenantConfiguration", key = "#configurationKey.getKeyName()")
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
public void deleteConfiguration(final TenantConfigurationKey configurationKey) {
tenantConfigurationRepository.deleteByKey(configurationKey.getKeyName());
}
@Override
public void setEnvironment(final Environment environment) {
this.environment = environment;
}
}

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import java.util.List;
import java.util.Optional;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import java.io.Serializable;
import java.util.List;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import java.util.List;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import java.util.List;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import org.eclipse.hawkbit.repository.model.RolloutTargetGroup;
import org.eclipse.hawkbit.repository.model.RolloutTargetGroupId;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -27,6 +27,7 @@ import javax.persistence.criteria.Root;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.CustomSoftwareModule;
@@ -307,7 +308,7 @@ public class SoftwareManagement {
private void deleteGridFsArtifacts(final SoftwareModule swModule) {
for (final LocalArtifact localArtifact : swModule.getLocalArtifacts()) {
artifactManagement.deleteGridFsArtifact(localArtifact);
artifactManagement.deleteLocalArtifact(localArtifact);
}
}

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import java.util.List;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import java.util.List;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.springframework.data.domain.Page;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import java.lang.reflect.Method;
import java.math.BigDecimal;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import static com.google.common.base.Preconditions.checkNotNull;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import java.util.ArrayList;
import java.util.List;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.springframework.data.domain.Page;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import java.util.Collection;
import java.util.List;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import java.net.URI;
import java.util.ArrayList;
@@ -33,6 +33,7 @@ import org.eclipse.hawkbit.Constants;
import org.eclipse.hawkbit.eventbus.event.TargetTagAssigmentResultEvent;
import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSet_;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import java.util.Collection;
import java.util.List;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import java.util.List;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import java.util.List;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import java.lang.reflect.Method;

View File

@@ -6,7 +6,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import java.util.List;

View File

@@ -6,13 +6,16 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
package org.eclipse.hawkbit.repository.jpa;
import java.util.Optional;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.report.model.TenantUsage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -43,7 +46,8 @@ public class TenantStatsManagement {
* to collect for
* @return collected statistics
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN)
public TenantUsage getStatsOfTenant(final String tenant) {
final TenantUsage result = new TenantUsage(tenant);

View File

@@ -8,7 +8,7 @@
*/
package org.eclipse.hawkbit.repository.model.helper;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.jpa.SystemManagement;
import org.springframework.beans.factory.annotation.Autowired;
/**

View File

@@ -10,8 +10,8 @@ package org.eclipse.hawkbit.rollout.condition;
import java.util.concurrent.Callable;
import org.eclipse.hawkbit.repository.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.jpa.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;

View File

@@ -11,7 +11,7 @@ package org.eclipse.hawkbit.rollout.condition;
import java.util.List;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.jpa.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;

View File

@@ -8,7 +8,7 @@
*/
package org.eclipse.hawkbit.rollout.condition;
import org.eclipse.hawkbit.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.ActionRepository;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;

View File

@@ -8,7 +8,7 @@
*/
package org.eclipse.hawkbit.rollout.condition;
import org.eclipse.hawkbit.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.ActionRepository;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;

View File

@@ -16,34 +16,34 @@ import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.eclipse.hawkbit.cache.TenantAwareCacheManager;
import org.eclipse.hawkbit.repository.ActionRepository;
import org.eclipse.hawkbit.repository.ActionStatusRepository;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.DistributionSetTagRepository;
import org.eclipse.hawkbit.repository.DistributionSetTypeRepository;
import org.eclipse.hawkbit.repository.ExternalArtifactRepository;
import org.eclipse.hawkbit.repository.LocalArtifactRepository;
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.repository.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.RolloutRepository;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.SoftwareModuleMetadataRepository;
import org.eclipse.hawkbit.repository.SoftwareModuleRepository;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeRepository;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TagManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetInfoRepository;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TargetRepository;
import org.eclipse.hawkbit.repository.TargetTagRepository;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.TenantMetaDataRepository;
import org.eclipse.hawkbit.repository.jpa.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.ActionStatusRepository;
import org.eclipse.hawkbit.repository.jpa.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.DistributionSetTagRepository;
import org.eclipse.hawkbit.repository.jpa.DistributionSetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.ExternalArtifactRepository;
import org.eclipse.hawkbit.repository.jpa.LocalArtifactRepository;
import org.eclipse.hawkbit.repository.jpa.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.jpa.RolloutRepository;
import org.eclipse.hawkbit.repository.jpa.SoftwareManagement;
import org.eclipse.hawkbit.repository.jpa.SoftwareModuleMetadataRepository;
import org.eclipse.hawkbit.repository.jpa.SoftwareModuleRepository;
import org.eclipse.hawkbit.repository.jpa.SoftwareModuleTypeRepository;
import org.eclipse.hawkbit.repository.jpa.SystemManagement;
import org.eclipse.hawkbit.repository.jpa.TagManagement;
import org.eclipse.hawkbit.repository.jpa.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.jpa.TargetInfoRepository;
import org.eclipse.hawkbit.repository.jpa.TargetManagement;
import org.eclipse.hawkbit.repository.jpa.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.TargetTagRepository;
import org.eclipse.hawkbit.repository.jpa.TenantMetaDataRepository;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.utils.RepositoryDataGenerator.DatabaseCleanupUtil;

View File

@@ -18,8 +18,8 @@ import java.util.UUID;
import org.apache.commons.io.IOUtils;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.jpa.SoftwareManagement;
import org.eclipse.hawkbit.repository.jpa.TargetManagement;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSetType;

View File

@@ -58,14 +58,14 @@ public class ControllerManagementTest extends AbstractIntegrationTest {
System.currentTimeMillis());
actionStatusMessage.addMessage("foobar");
savedAction.setStatus(Status.RUNNING);
controllerManagament.addUpdateActionStatus(actionStatusMessage, savedAction);
controllerManagament.addUpdateActionStatus(actionStatusMessage);
assertThat(targetManagement.findTargetByControllerID("4712").getTargetInfo().getUpdateStatus())
.isEqualTo(TargetUpdateStatus.PENDING);
actionStatusMessage = new ActionStatus(savedAction, Action.Status.FINISHED, System.currentTimeMillis());
actionStatusMessage.addMessage(RandomStringUtils.randomAscii(512));
savedAction.setStatus(Status.FINISHED);
controllerManagament.addUpdateActionStatus(actionStatusMessage, savedAction);
controllerManagament.addUpdateActionStatus(actionStatusMessage);
assertThat(targetManagement.findTargetByControllerID("4712").getTargetInfo().getUpdateStatus())
.isEqualTo(TargetUpdateStatus.IN_SYNC);
@@ -111,21 +111,21 @@ public class ControllerManagementTest extends AbstractIntegrationTest {
final ActionStatus actionStatusMessage = new ActionStatus(savedAction, Action.Status.RUNNING,
System.currentTimeMillis());
actionStatusMessage.addMessage("running");
savedAction = controllerManagament.addUpdateActionStatus(actionStatusMessage, savedAction);
savedAction = controllerManagament.addUpdateActionStatus(actionStatusMessage);
assertThat(targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus())
.isEqualTo(TargetUpdateStatus.PENDING);
final ActionStatus actionStatusMessage2 = new ActionStatus(savedAction, Action.Status.ERROR,
System.currentTimeMillis());
actionStatusMessage2.addMessage("error");
savedAction = controllerManagament.addUpdateActionStatus(actionStatusMessage2, savedAction);
savedAction = controllerManagament.addUpdateActionStatus(actionStatusMessage2);
assertThat(targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus())
.isEqualTo(TargetUpdateStatus.ERROR);
final ActionStatus actionStatusMessage3 = new ActionStatus(savedAction, Action.Status.FINISHED,
System.currentTimeMillis());
actionStatusMessage3.addMessage("finish");
controllerManagament.addUpdateActionStatus(actionStatusMessage3, savedAction);
controllerManagament.addUpdateActionStatus(actionStatusMessage3);
targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus();

View File

@@ -27,6 +27,7 @@ import org.eclipse.hawkbit.eventbus.event.CancelTargetAssignmentEvent;
import org.eclipse.hawkbit.eventbus.event.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.exception.ForceQuitActionNotAllowedException;
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
import org.eclipse.hawkbit.repository.jpa.TargetRepository;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
@@ -104,7 +105,7 @@ public class DeploymentManagementTest extends AbstractIntegrationTest {
@Test
@Description("Ensures that distribution sets can assigned and unassigned to a distribution set tag. Not exists distribution set will be ignored for the assignment.")
public void assignAndUnassignDistributionSetToTag() {
final List<Long> assignDS = new ArrayList<Long>();
final List<Long> assignDS = new ArrayList<>();
for (int i = 0; i < 4; i++) {
assignDS.add(TestDataUtil.generateDistributionSet("DS" + i, "1.0", softwareManagement,
distributionSetManagement, new ArrayList<DistributionSetTag>()).getId());
@@ -199,8 +200,8 @@ public class DeploymentManagementTest extends AbstractIntegrationTest {
secondAction = deploymentManagement.findActionWithDetails(secondAction.getId());
// confirm cancellation
secondAction.setStatus(Status.CANCELED);
controllerManagement.addCancelActionStatus(
new ActionStatus(secondAction, Status.CANCELED, System.currentTimeMillis()), secondAction);
controllerManagement
.addCancelActionStatus(new ActionStatus(secondAction, Status.CANCELED, System.currentTimeMillis()));
assertThat(actionStatusRepository.findAll()).as("wrong size of actions status").hasSize(4);
assertThat(targetManagement.findTargetByControllerID("4712").getAssignedDistributionSet()).as("wrong ds")
.isEqualTo(dsFirst);
@@ -213,8 +214,8 @@ public class DeploymentManagementTest extends AbstractIntegrationTest {
firstAction = deploymentManagement.findActionWithDetails(firstAction.getId());
// confirm cancellation
firstAction.setStatus(Status.CANCELED);
controllerManagement.addCancelActionStatus(
new ActionStatus(firstAction, Status.CANCELED, System.currentTimeMillis()), firstAction);
controllerManagement
.addCancelActionStatus(new ActionStatus(firstAction, Status.CANCELED, System.currentTimeMillis()));
assertThat(actionStatusRepository.findAll()).as("wrong size of action status").hasSize(6);
assertThat(targetManagement.findTargetByControllerID("4712").getAssignedDistributionSet())
.as("wrong assigned ds").isEqualTo(dsInstalled);
@@ -256,8 +257,8 @@ public class DeploymentManagementTest extends AbstractIntegrationTest {
// confirm cancellation
firstAction = deploymentManagement.findActionWithDetails(firstAction.getId());
firstAction.setStatus(Status.CANCELED);
controllerManagement.addCancelActionStatus(
new ActionStatus(firstAction, Status.CANCELED, System.currentTimeMillis()), firstAction);
controllerManagement
.addCancelActionStatus(new ActionStatus(firstAction, Status.CANCELED, System.currentTimeMillis()));
assertThat(actionStatusRepository.findAll()).as("wrong size of action status").hasSize(4);
assertThat(targetManagement.findTargetByControllerID("4712").getAssignedDistributionSet())
.as("wrong assigned ds").isEqualTo(dsSecond);
@@ -273,8 +274,8 @@ public class DeploymentManagementTest extends AbstractIntegrationTest {
.as("wrong assigned ds").isEqualTo(dsSecond);
// confirm cancellation
secondAction.setStatus(Status.CANCELED);
controllerManagement.addCancelActionStatus(
new ActionStatus(secondAction, Status.CANCELED, System.currentTimeMillis()), secondAction);
controllerManagement
.addCancelActionStatus(new ActionStatus(secondAction, Status.CANCELED, System.currentTimeMillis()));
// cancelled success -> back to dsInstalled
assertThat(targetManagement.findTargetByControllerID("4712").getAssignedDistributionSet())
.as("wrong installed ds").isEqualTo(dsInstalled);
@@ -742,7 +743,7 @@ public class DeploymentManagementTest extends AbstractIntegrationTest {
for (final String msg : msgs) {
statusMessages.addMessage(msg);
}
controllerManagament.addUpdateActionStatus(statusMessages, updActA);
controllerManagament.addUpdateActionStatus(statusMessages);
return targetManagement.findTargetByControllerID(t.getControllerId());
}
@@ -786,7 +787,7 @@ public class DeploymentManagementTest extends AbstractIntegrationTest {
final Action action = updAct.getContent().get(0);
action.setStatus(Status.FINISHED);
final ActionStatus statusMessage = new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), "");
controllerManagament.addUpdateActionStatus(statusMessage, action);
controllerManagament.addUpdateActionStatus(statusMessage);
targ = targetManagement.findTargetByControllerID(targ.getControllerId());

View File

@@ -821,7 +821,7 @@ public class DistributionSetManagementTest extends AbstractIntegrationTest {
for (final String msg : msgs) {
statusMessages.addMessage(msg);
}
controllerManagament.addUpdateActionStatus(statusMessages, updActA);
controllerManagament.addUpdateActionStatus(statusMessages);
return targetManagement.findTargetByControllerID(t.getControllerId());
}

View File

@@ -558,7 +558,7 @@ public class ReportManagementTest extends AbstractIntegrationTest {
for (final String msg : msgs) {
statusMessages.addMessage(msg);
}
controllerManagament.addUpdateActionStatus(statusMessages, updActA);
controllerManagament.addUpdateActionStatus(statusMessages);
return targetManagement.findTargetByControllerID(t.getControllerId());
}

View File

@@ -18,6 +18,7 @@ import java.util.concurrent.Callable;
import org.eclipse.hawkbit.AbstractIntegrationTest;
import org.eclipse.hawkbit.TestDataUtil;
import org.eclipse.hawkbit.repository.jpa.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
@@ -136,8 +137,8 @@ public class RolloutManagementTest extends AbstractIntegrationTest {
// 50%
final Action action = runningActions.get(0);
action.setStatus(Status.FINISHED);
controllerManagament.addUpdateActionStatus(
new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), ""), action);
controllerManagament
.addUpdateActionStatus(new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), ""));
// check running rollouts again, now the finish condition should be hit
// and should start the next group
@@ -178,8 +179,8 @@ public class RolloutManagementTest extends AbstractIntegrationTest {
// finish actions with error
for (final Action action : runningActions) {
action.setStatus(Status.ERROR);
controllerManagament.addUpdateActionStatus(
new ActionStatus(action, Status.ERROR, System.currentTimeMillis(), ""), action);
controllerManagament
.addUpdateActionStatus(new ActionStatus(action, Status.ERROR, System.currentTimeMillis(), ""));
}
// check running rollouts again, now the error condition should be hit
@@ -220,8 +221,8 @@ public class RolloutManagementTest extends AbstractIntegrationTest {
// finish actions with error
for (final Action action : runningActions) {
action.setStatus(Status.ERROR);
controllerManagament.addUpdateActionStatus(
new ActionStatus(action, Status.ERROR, System.currentTimeMillis(), ""), action);
controllerManagament
.addUpdateActionStatus(new ActionStatus(action, Status.ERROR, System.currentTimeMillis(), ""));
}
// check running rollouts again, now the error condition should be hit
@@ -977,8 +978,8 @@ public class RolloutManagementTest extends AbstractIntegrationTest {
final List<Action> runningActions = deploymentManagement.findActionsByRolloutAndStatus(rollout, Status.RUNNING);
for (final Action action : runningActions) {
action.setStatus(status);
controllerManagament.addUpdateActionStatus(new ActionStatus(action, status, System.currentTimeMillis(), ""),
action);
controllerManagament
.addUpdateActionStatus(new ActionStatus(action, status, System.currentTimeMillis(), ""));
}
return runningActions.size();
}
@@ -989,14 +990,13 @@ public class RolloutManagementTest extends AbstractIntegrationTest {
assertThat(runningActions.size()).isGreaterThanOrEqualTo(amountOfTargetsToGetChanged);
for (int i = 0; i < amountOfTargetsToGetChanged; i++) {
controllerManagament.addUpdateActionStatus(
new ActionStatus(runningActions.get(i), status, System.currentTimeMillis(), ""),
runningActions.get(i));
new ActionStatus(runningActions.get(i), status, System.currentTimeMillis(), ""));
}
return runningActions.size();
}
private Map<TotalTargetCountStatus.Status, Long> createInitStatusMap() {
final Map<TotalTargetCountStatus.Status, Long> map = new HashMap<TotalTargetCountStatus.Status, Long>();
final Map<TotalTargetCountStatus.Status, Long> map = new HashMap<>();
for (final TotalTargetCountStatus.Status status : TotalTargetCountStatus.Status.values()) {
map.put(status, 0L);
}

View File

@@ -21,6 +21,7 @@ import org.eclipse.hawkbit.AbstractIntegrationTest;
import org.eclipse.hawkbit.TestDataUtil;
import org.eclipse.hawkbit.repository.DistributionSetFilter.DistributionSetFilterBuilder;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.jpa.TagManagement;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSetTagAssignmentResult;

View File

@@ -13,6 +13,7 @@ import static org.junit.Assert.fail;
import org.eclipse.hawkbit.AbstractIntegrationTest;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.jpa.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.junit.Test;

View File

@@ -96,7 +96,7 @@ public class TargetManagementSearchTest extends AbstractIntegrationTest {
final Action action = deploymentManagement.findActionWithDetails(actionId);
action.setStatus(Status.FINISHED);
controllerManagament.addUpdateActionStatus(
new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), "message"), action);
new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), "message"));
deploymentManagement.assignDistributionSet(setA.getId(), installedC);
final List<TargetUpdateStatus> unknown = new ArrayList<>();
@@ -745,7 +745,7 @@ public class TargetManagementSearchTest extends AbstractIntegrationTest {
final Action action = deploymentManagement.findActionWithDetails(actionId);
action.setStatus(Status.FINISHED);
controllerManagament.addUpdateActionStatus(
new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), "message"), action);
new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), "message"));
});
deploymentManagement.assignDistributionSet(assignedSet, assignedtargets);
@@ -773,7 +773,7 @@ public class TargetManagementSearchTest extends AbstractIntegrationTest {
final Action action = deploymentManagement.findActionWithDetails(actionId);
action.setStatus(Status.FINISHED);
controllerManagament.addUpdateActionStatus(
new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), "message"), action);
new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), "message"));
});
deploymentManagement.assignDistributionSet(assignedSet, installedtargets);
@@ -802,7 +802,7 @@ public class TargetManagementSearchTest extends AbstractIntegrationTest {
final Action action = deploymentManagement.findActionWithDetails(actionId);
action.setStatus(Status.FINISHED);
controllerManagament.addUpdateActionStatus(
new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), "message"), action);
new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), "message"));
});
deploymentManagement.assignDistributionSet(assignedSet, installedtargets);
@@ -840,7 +840,7 @@ public class TargetManagementSearchTest extends AbstractIntegrationTest {
for (final String msg : msgs) {
statusMessages.addMessage(msg);
}
controllerManagament.addUpdateActionStatus(statusMessages, updActA);
controllerManagament.addUpdateActionStatus(statusMessages);
return targetManagement.findTargetByControllerID(t.getControllerId());
}

View File

@@ -187,7 +187,7 @@ public class TargetManagementTest extends AbstractIntegrationTest {
final Action action = deploymentManagement.findActionWithDetails(result.getActions().get(0));
action.setStatus(Status.FINISHED);
controllerManagament.addUpdateActionStatus(
new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), "message"), action);
new ActionStatus(action, Status.FINISHED, System.currentTimeMillis(), "message"));
deploymentManagement.assignDistributionSet(set2.getId(), "4711");
target = targetManagement.findTargetByControllerIDWithDetails("4711");

View File

@@ -75,7 +75,7 @@ public class RSQLRolloutGroupFields extends AbstractIntegrationTest {
}
private void assertRSQLQuery(final String rsqlParam, final long expcetedTargets) {
final Page<RolloutGroup> findTargetPage = rolloutGroupManagement.findRolloutGroupsByPredicate(rollout,
final Page<RolloutGroup> findTargetPage = rolloutGroupManagement.findRolloutGroupsAll(rollout,
RSQLUtility.parse(rsqlParam, RolloutGroupFields.class), new PageRequest(0, 100));
final long countTargetsAll = findTargetPage.getTotalElements();
assertThat(findTargetPage).isNotNull();

View File

@@ -30,9 +30,9 @@ import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.DistributionSetAssignmentResult;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.TagManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.jpa.SoftwareManagement;
import org.eclipse.hawkbit.repository.jpa.TagManagement;
import org.eclipse.hawkbit.repository.jpa.TargetManagement;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
@@ -207,14 +207,14 @@ public final class RepositoryDataGenerator {
download.setAction(action);
download.setStatus(Status.DOWNLOAD);
download.addMessage("Controller started download.");
action = controllerManagement.addUpdateActionStatus(download, action);
action = controllerManagement.addUpdateActionStatus(download);
// warning
final ActionStatus warning = new ActionStatus();
warning.setAction(action);
warning.setStatus(Status.WARNING);
warning.addMessage("Some warning: " + jlorem.words(new Random().nextInt(50)));
action = controllerManagement.addUpdateActionStatus(warning, action);
action = controllerManagement.addUpdateActionStatus(warning);
// garbage
for (int i = 0; i < new Random().nextInt(10); i++) {
@@ -222,13 +222,13 @@ public final class RepositoryDataGenerator {
running.setAction(action);
running.setStatus(Status.RUNNING);
running.addMessage("Still running: " + jlorem.words(new Random().nextInt(50)));
action = controllerManagement.addUpdateActionStatus(running, action);
action = controllerManagement.addUpdateActionStatus(running);
for (int g = 0; g < new Random().nextInt(5); g++) {
final ActionStatus rand = new ActionStatus();
rand.setAction(action);
rand.setStatus(Status.RUNNING);
rand.addMessage(jlorem.words(new Random().nextInt(50)));
action = controllerManagement.addUpdateActionStatus(rand, action);
action = controllerManagement.addUpdateActionStatus(rand);
}
}
@@ -241,13 +241,13 @@ public final class RepositoryDataGenerator {
if (incrementAndGet % 5 == 0) {
close.setStatus(Status.ERROR);
close.addMessage("Controller reported CLOSED with ERROR!");
action = controllerManagement.addUpdateActionStatus(close, action);
action = controllerManagement.addUpdateActionStatus(close);
}
// with OK
else {
close.setStatus(Status.FINISHED);
close.addMessage("Controller reported CLOSED with OK!");
action = controllerManagement.addUpdateActionStatus(close, action);
action = controllerManagement.addUpdateActionStatus(close);
}
index++;
@@ -266,7 +266,7 @@ public final class RepositoryDataGenerator {
close.setAction(action);
close.setStatus(Status.FINISHED);
close.addMessage("Controller reported CLOSED with OK!");
action = controllerManagement.addUpdateActionStatus(close, action);
action = controllerManagement.addUpdateActionStatus(close);
}
}
@@ -339,8 +339,8 @@ public final class RepositoryDataGenerator {
// garbage DS
LOG.debug("initDemoRepo - start now DS garbage");
TestDataUtil.generateDistributionSets("Generic Software Package", sizeMultiplikator,
softwareManagement, distributionSetManagement);
TestDataUtil.generateDistributionSets("Generic Software Package", sizeMultiplikator, softwareManagement,
distributionSetManagement);
LOG.debug("initDemoRepo - DS garbage finished");
LOG.debug("initDemoRepo - start now Extra Software Modules and types");

View File

@@ -149,7 +149,7 @@ public class ArtifactStoreController {
actionStatus.addMessage(
ControllerManagement.SERVER_MESSAGE_PREFIX + "Target downloads: " + request.getRequestURI());
}
controllerManagement.addActionStatusMessage(actionStatus);
controllerManagement.addInformationalActionStatus(actionStatus);
return action;
}

View File

@@ -30,8 +30,8 @@ import org.eclipse.hawkbit.controller.model.DeploymentBase;
import org.eclipse.hawkbit.controller.model.Result.FinalResult;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.SoftwareManagement;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
@@ -150,7 +150,7 @@ public class RootController {
return new ResponseEntity<>(
DataConversionHelper.fromTarget(target, controllerManagement.findActionByTargetAndActive(target),
controllerManagement.findPollingTime(), tenantAware),
controllerManagement.getPollingTime(), tenantAware),
HttpStatus.OK);
}
@@ -222,7 +222,7 @@ public class RootController {
statusMessage.addMessage(
ControllerManagement.SERVER_MESSAGE_PREFIX + "Target downloads " + request.getRequestURI());
}
controllerManagement.addActionStatusMessage(statusMessage);
controllerManagement.addInformationalActionStatus(statusMessage);
return action;
}
@@ -371,8 +371,7 @@ public class RootController {
return new ResponseEntity<>(HttpStatus.GONE);
}
controllerManagement.addUpdateActionStatus(generateUpdateStatus(feedback, targetid, feedback.getId(), action),
action);
controllerManagement.addUpdateActionStatus(generateUpdateStatus(feedback, targetid, feedback.getId(), action));
return new ResponseEntity<>(HttpStatus.OK);
@@ -546,7 +545,7 @@ public class RootController {
}
controllerManagement
.addCancelActionStatus(generateActionCancelStatus(feedback, target, feedback.getId(), action), action);
.addCancelActionStatus(generateActionCancelStatus(feedback, target, feedback.getId(), action));
return new ResponseEntity<>(HttpStatus.OK);
}

View File

@@ -16,8 +16,8 @@ import java.util.List;
import org.eclipse.hawkbit.repository.DistributionSetAssignmentResult;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.SoftwareManagement;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DistributionSetType;

View File

@@ -18,12 +18,12 @@ import org.eclipse.hawkbit.repository.DistributionSetAssignmentResult;
import org.eclipse.hawkbit.repository.DistributionSetFields;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.DistributionSetMetadataFields;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TargetWithActionType;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.SoftwareManagement;
import org.eclipse.hawkbit.repository.jpa.SystemManagement;
import org.eclipse.hawkbit.repository.jpa.TargetManagement;
import org.eclipse.hawkbit.repository.jpa.TargetWithActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DsMetadataCompositeKey;

View File

@@ -13,8 +13,8 @@ import java.util.stream.Collectors;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.TagFields;
import org.eclipse.hawkbit.repository.TagManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.TagManagement;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSetTagAssignmentResult;

View File

@@ -14,8 +14,8 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.SoftwareManagement;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.rest.resource.api.DistributionSetTypeRestApi;

View File

@@ -12,8 +12,8 @@ import java.util.List;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.DistributionSetTypeFields;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.SoftwareManagement;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
@@ -69,7 +69,7 @@ public class DistributionSetTypeResource implements DistributionSetTypeRestApi {
final Slice<DistributionSetType> findModuleTypessAll;
Long countModulesAll;
if (rsqlParam != null) {
findModuleTypessAll = distributionSetManagement.findDistributionSetTypesByPredicate(
findModuleTypessAll = distributionSetManagement.findDistributionSetTypesAll(
RSQLUtility.parse(rsqlParam, DistributionSetTypeFields.class), pageable);
countModulesAll = ((Page<DistributionSetType>) findModuleTypessAll).getTotalElements();
} else {

View File

@@ -13,8 +13,8 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.SoftwareManagement;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.rest.resource.helper.RestResourceConversionHelper;

View File

@@ -178,7 +178,7 @@ public class RolloutResource implements RolloutRestApi {
final Page<RolloutGroup> findRolloutGroupsAll;
if (rsqlParam != null) {
findRolloutGroupsAll = this.rolloutGroupManagement.findRolloutGroupsByPredicate(rollout,
findRolloutGroupsAll = this.rolloutGroupManagement.findRolloutGroupsAll(rollout,
RSQLUtility.parse(rsqlParam, RolloutGroupFields.class), pageable);
} else {
findRolloutGroupsAll = this.rolloutGroupManagement.findRolloutGroupsByRolloutId(rolloutId, pageable);

View File

@@ -14,8 +14,8 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.SoftwareManagement;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule;

View File

@@ -12,10 +12,10 @@ import java.io.IOException;
import java.util.List;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.SoftwareModuleFields;
import org.eclipse.hawkbit.repository.SoftwareModuleMetadataFields;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.SoftwareManagement;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;

View File

@@ -10,9 +10,9 @@ package org.eclipse.hawkbit.rest.resource;
import java.util.List;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeFields;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.SoftwareManagement;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;

View File

@@ -16,8 +16,8 @@ import java.util.stream.Collectors;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.report.model.SystemUsageReport;
import org.eclipse.hawkbit.report.model.TenantUsage;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.jpa.SystemManagement;
import org.eclipse.hawkbit.rest.resource.model.systemmanagement.CacheRest;
import org.eclipse.hawkbit.rest.resource.model.systemmanagement.SystemStatisticsRest;
import org.eclipse.hawkbit.rest.resource.model.systemmanagement.TenantSystemUsageRest;

View File

@@ -19,8 +19,8 @@ import org.eclipse.hawkbit.repository.ActionFields;
import org.eclipse.hawkbit.repository.ActionStatusFields;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.TargetManagement;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.ActionStatus;

View File

@@ -12,9 +12,9 @@ import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.repository.TagFields;
import org.eclipse.hawkbit.repository.TagManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.TagManagement;
import org.eclipse.hawkbit.repository.jpa.TargetManagement;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult;

View File

@@ -30,12 +30,12 @@ import org.eclipse.hawkbit.AbstractIntegrationTest;
import org.eclipse.hawkbit.MockMvcResultPrinter;
import org.eclipse.hawkbit.TestDataUtil;
import org.eclipse.hawkbit.WithUser;
import org.eclipse.hawkbit.repository.ActionRepository;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.SoftwareManagement;
import org.eclipse.hawkbit.repository.jpa.TargetManagement;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
@@ -890,7 +890,7 @@ public class DistributionSetResourceTest extends AbstractIntegrationTest {
for (final String msg : msgs) {
statusMessages.addMessage(msg);
}
controllerManagament.addUpdateActionStatus(statusMessages, updActA);
controllerManagament.addUpdateActionStatus(statusMessages);
return targetManagement.findTargetByControllerID(t.getControllerId());
}

View File

@@ -108,8 +108,7 @@ public class TargetResourceTest extends AbstractIntegrationTest {
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId);
actions.get(0).setStatus(Status.FINISHED);
controllerManagament.addUpdateActionStatus(
new ActionStatus(actions.get(0), Status.FINISHED, System.currentTimeMillis(), "testmessage"),
actions.get(0));
new ActionStatus(actions.get(0), Status.FINISHED, System.currentTimeMillis(), "testmessage"));
final PageRequest pageRequest = new PageRequest(0, 1000, Direction.ASC, ActionFields.ID.getFieldName());
final ActionStatus status = deploymentManagement
@@ -1284,8 +1283,8 @@ public class TargetResourceTest extends AbstractIntegrationTest {
final Pageable pageReq = new PageRequest(0, 100);
final Action action = actionRepository.findByDistributionSet(pageReq, savedSet).getContent().get(0);
final ActionStatus actionStatus = new ActionStatus(action, Status.FINISHED, 0l);
controllerManagement.addUpdateActionStatus(actionStatus, action);
final ActionStatus actionStatus = new ActionStatus(action, Status.FINISHED, 0L);
controllerManagement.addUpdateActionStatus(actionStatus);
}
/**

View File

@@ -12,7 +12,7 @@ import java.util.List;
import java.util.Map;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.jpa.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;

View File

@@ -14,7 +14,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.jpa.SoftwareManagement;
import org.eclipse.hawkbit.ui.artifacts.event.UploadArtifactUIEvent;
import org.eclipse.hawkbit.ui.artifacts.state.ArtifactUploadState;
import org.eclipse.hawkbit.ui.artifacts.state.CustomFile;

View File

@@ -12,8 +12,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.jpa.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.jpa.SoftwareManagement;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.ui.common.UserDetailsFormatter;

View File

@@ -10,7 +10,7 @@ package org.eclipse.hawkbit.ui.artifacts.smtable;
import java.io.Serializable;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.jpa.SoftwareManagement;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleEvent;
import org.eclipse.hawkbit.ui.common.SoftwareModuleTypeBeanQuery;

View File

@@ -12,7 +12,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.jpa.SoftwareManagement;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.ui.artifacts.event.SMFilterEvent;
import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleEvent;

View File

@@ -13,8 +13,8 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.SpPermissionChecker;
import org.eclipse.hawkbit.repository.jpa.SoftwareManagement;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleTypeEvent;
import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleTypeEvent.SoftwareModuleTypeEnum;

View File

@@ -10,7 +10,7 @@ package org.eclipse.hawkbit.ui.artifacts.smtype;
import java.io.Serializable;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.jpa.SoftwareManagement;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.ui.artifacts.event.SMFilterEvent;
import org.eclipse.hawkbit.ui.artifacts.state.ArtifactUploadState;

View File

@@ -13,7 +13,7 @@ import java.util.List;
import java.util.Map;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.jpa.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.SpringContextHelper;

View File

@@ -11,8 +11,8 @@ package org.eclipse.hawkbit.ui.common;
import java.util.List;
import java.util.Map;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.jpa.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.jpa.SoftwareManagement;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.SpringContextHelper;

View File

@@ -10,7 +10,7 @@ package org.eclipse.hawkbit.ui.common.tagdetails;
import org.eclipse.hawkbit.eventbus.event.TargetTagCreatedBulkEvent;
import org.eclipse.hawkbit.eventbus.event.TargetTagDeletedEvent;
import org.eclipse.hawkbit.repository.TagManagement;
import org.eclipse.hawkbit.repository.jpa.TagManagement;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.springframework.beans.factory.annotation.Autowired;

View File

@@ -19,7 +19,7 @@ import org.eclipse.hawkbit.eventbus.event.DistributionSetTagCreatedBulkEvent;
import org.eclipse.hawkbit.eventbus.event.DistributionSetTagDeletedEvent;
import org.eclipse.hawkbit.eventbus.event.DistributionSetTagUpdateEvent;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.TagManagement;
import org.eclipse.hawkbit.repository.jpa.TagManagement;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSetTagAssignmentResult;

View File

@@ -16,7 +16,7 @@ import java.util.stream.Collectors;
import org.eclipse.hawkbit.eventbus.event.TargetTagAssigmentResultEvent;
import org.eclipse.hawkbit.eventbus.event.TargetTagUpdateEvent;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.jpa.TargetManagement;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult;

Some files were not shown because too many files have changed in this diff Show More