Split into separate maven modules.
Signed-off-by: Kai Zimmermann <kai.zimmermann@bosch-si.com>
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
<!--
|
||||
|
||||
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
|
||||
|
||||
-->
|
||||
<FindBugsFilter>
|
||||
<Match>
|
||||
<Method name="~_persistence_[sg]et.*" />
|
||||
</Match>
|
||||
</FindBugsFilter>
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* A constant class which holds only static constants used within the SP server.
|
||||
*
|
||||
*/
|
||||
public final class Constants {
|
||||
|
||||
/**
|
||||
* Defines the maximum entries for SQL in-statement. Many database have
|
||||
* limit of elements within an SQL in-statement, e.g. Oracle with maximum
|
||||
* 1000 elements. So everywhere we are using in-statements in our SQL
|
||||
* queries we need to split these elements for the in-statements by this
|
||||
* number.
|
||||
*/
|
||||
public static final int MAX_ENTRIES_IN_STATEMENT = 999;
|
||||
|
||||
/**
|
||||
* Constant class only private constructor.
|
||||
*/
|
||||
private Constants() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.transaction.Transaction;
|
||||
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.persistence.config.PersistenceUnitProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.orm.jpa.EntityManagerHolder;
|
||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
/**
|
||||
* {@link JpaTransactionManager} that sets the
|
||||
* {@link TenantAware#getCurrentTenant()} in the eclipselink session. This has
|
||||
* to be done in eclipselink after a {@link Transaction} has been started.
|
||||
*
|
||||
*/
|
||||
public class MultiTenantJpaTransactionManager extends JpaTransactionManager {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Autowired
|
||||
private transient TenantAware tenantAware;
|
||||
|
||||
@Override
|
||||
protected void doBegin(final Object transaction, final TransactionDefinition definition) {
|
||||
super.doBegin(transaction, definition);
|
||||
|
||||
final String currentTenant = tenantAware.getCurrentTenant();
|
||||
if (currentTenant != null) {
|
||||
final EntityManagerHolder emHolder = (EntityManagerHolder) TransactionSynchronizationManager
|
||||
.getResource(getEntityManagerFactory());
|
||||
final EntityManager em = emHolder.getEntityManager();
|
||||
em.setProperty(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, currentTenant.toUpperCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
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.model.helper.AfterTransactionCommitExecutorHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.CacheManagerHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.SecurityTokenGeneratorHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.SystemManagementHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.SystemSecurityContextHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.TenantAwareHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
|
||||
import org.eclipse.hawkbit.security.SecurityTokenGenerator;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
|
||||
import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
|
||||
|
||||
/**
|
||||
* General configuration for the SP Repository.
|
||||
*
|
||||
*/
|
||||
@EnableJpaRepositories(basePackages = { "org.eclipse.hawkbit.repository.jpa" })
|
||||
@EnableTransactionManagement
|
||||
@EnableJpaAuditing
|
||||
@EnableAspectJAutoProxy
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
@EnableAutoConfiguration
|
||||
public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
|
||||
/**
|
||||
* @return the {@link SystemSecurityContext} singleton bean which make it
|
||||
* accessible in beans which cannot access the service directly,
|
||||
* e.g. JPA entities.
|
||||
*/
|
||||
@Bean
|
||||
public SystemSecurityContextHolder systemSecurityContextHolder() {
|
||||
return SystemSecurityContextHolder.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link TenantConfigurationManagement} singleton bean which
|
||||
* make it accessible in beans which cannot access the service
|
||||
* directly, e.g. JPA entities.
|
||||
*/
|
||||
@Bean
|
||||
public TenantConfigurationManagementHolder tenantConfigurationManagementHolder() {
|
||||
return TenantConfigurationManagementHolder.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link SystemManagementHolder} singleton bean which holds the
|
||||
* current {@link SystemManagement} service and make it accessible
|
||||
* in beans which cannot access the service directly, e.g. JPA
|
||||
* entities.
|
||||
*/
|
||||
@Bean
|
||||
public SystemManagementHolder systemManagementHolder() {
|
||||
return SystemManagementHolder.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link TenantAwareHolder} singleton bean which holds the
|
||||
* current {@link TenantAware} service and make it accessible in
|
||||
* beans which cannot access the service directly, e.g. JPA
|
||||
* entities.
|
||||
*/
|
||||
@Bean
|
||||
public TenantAwareHolder tenantAwareHolder() {
|
||||
return TenantAwareHolder.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link SecurityTokenGeneratorHolder} singleton bean which
|
||||
* holds the current {@link SecurityTokenGenerator} service and make
|
||||
* it accessible in beans which cannot access the service via
|
||||
* injection
|
||||
*/
|
||||
@Bean
|
||||
public SecurityTokenGeneratorHolder securityTokenGeneratorHolder() {
|
||||
return SecurityTokenGeneratorHolder.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the singleton instance of the {@link CacheManagerHolder}
|
||||
*/
|
||||
@Bean
|
||||
public CacheManagerHolder cacheManagerHolder() {
|
||||
return CacheManagerHolder.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the singleton instance of the
|
||||
* {@link AfterTransactionCommitExecutorHolder}
|
||||
*/
|
||||
@Bean
|
||||
public AfterTransactionCommitExecutorHolder afterTransactionCommitExecutorHolder() {
|
||||
return AfterTransactionCommitExecutorHolder.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the validation processor bean.
|
||||
*
|
||||
* @return the {@link MethodValidationPostProcessor}
|
||||
*/
|
||||
@Bean
|
||||
public MethodValidationPostProcessor methodValidationPostProcessor() {
|
||||
return new MethodValidationPostProcessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link ExceptionMappingAspectHandler} aspect bean
|
||||
*/
|
||||
@Bean
|
||||
public ExceptionMappingAspectHandler createRepositoryExceptionHandlerAdvice() {
|
||||
return new ExceptionMappingAspectHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
|
||||
return new EclipseLinkJpaVendorAdapter();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> getVendorProperties() {
|
||||
|
||||
final Map<String, Object> properties = new HashMap<>();
|
||||
// Turn off dynamic weaving to disable LTW lookup in static weaving mode
|
||||
properties.put("eclipselink.weaving", "false");
|
||||
// needed for reports
|
||||
properties.put("eclipselink.jdbc.allow-native-sql-queries", "true");
|
||||
// flyway
|
||||
properties.put("eclipselink.ddl-generation", "none");
|
||||
|
||||
properties.put("eclipselink.persistence-context.flush-mode", "auto");
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link MultiTenantJpaTransactionManager} bean.
|
||||
*
|
||||
* @see org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration#transactionManager()
|
||||
* @return a new {@link PlatformTransactionManager}
|
||||
*/
|
||||
@Override
|
||||
@Bean
|
||||
public PlatformTransactionManager transactionManager() {
|
||||
return new MultiTenantJpaTransactionManager();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* 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.aspects;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.transaction.TransactionManager;
|
||||
|
||||
import org.aspectj.lang.annotation.AfterThrowing;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.eclipse.hawkbit.exception.GenericSpServerException;
|
||||
import org.eclipse.hawkbit.repository.exception.ConcurrentModificationException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
|
||||
import org.springframework.orm.jpa.JpaSystemException;
|
||||
import org.springframework.orm.jpa.JpaVendorAdapter;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.transaction.TransactionSystemException;
|
||||
|
||||
/**
|
||||
* {@link Aspect} catches persistence exceptions and wraps them to custom
|
||||
* specific exceptions Additionally it checks and prevents access to certain
|
||||
* packages. Logging aspect which logs the call stack
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Aspect
|
||||
public class ExceptionMappingAspectHandler implements Ordered {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ExceptionMappingAspectHandler.class);
|
||||
|
||||
private static final Map<String, String> EXCEPTION_MAPPING = new HashMap<>();
|
||||
|
||||
/**
|
||||
* this is required to enable a certain order of exception and to select the
|
||||
* most specific mappable exception according to the type hierarchy of the
|
||||
* exception.
|
||||
*/
|
||||
private static final List<Class<?>> MAPPED_EXCEPTION_ORDER = new ArrayList<>();
|
||||
|
||||
@Autowired
|
||||
private JpaVendorAdapter jpaVendorAdapter;
|
||||
|
||||
private final SQLStateSQLExceptionTranslator sqlStateExceptionTranslator = new SQLStateSQLExceptionTranslator();
|
||||
|
||||
static {
|
||||
MAPPED_EXCEPTION_ORDER.add(DuplicateKeyException.class);
|
||||
MAPPED_EXCEPTION_ORDER.add(DataIntegrityViolationException.class);
|
||||
MAPPED_EXCEPTION_ORDER.add(ConcurrencyFailureException.class);
|
||||
MAPPED_EXCEPTION_ORDER.add(AccessDeniedException.class);
|
||||
|
||||
EXCEPTION_MAPPING.put(DuplicateKeyException.class.getName(), EntityAlreadyExistsException.class.getName());
|
||||
EXCEPTION_MAPPING.put(DataIntegrityViolationException.class.getName(),
|
||||
EntityAlreadyExistsException.class.getName());
|
||||
|
||||
EXCEPTION_MAPPING.put(ConcurrencyFailureException.class.getName(),
|
||||
ConcurrentModificationException.class.getName());
|
||||
EXCEPTION_MAPPING.put(AccessDeniedException.class.getName(), InsufficientPermissionException.class.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* catch exceptions of the {@link TransactionManager} and wrap them to
|
||||
* custom exceptions.
|
||||
*
|
||||
* @param ex
|
||||
* the thrown and catched exception
|
||||
* @throws Throwable
|
||||
*/
|
||||
@AfterThrowing(pointcut = "( execution( * org.springframework.transaction..*.*(..)) "
|
||||
+ " || execution( * org.eclipse.hawkbit.repository.*.*(..)) "
|
||||
+ " || execution( * org.eclipse.hawkbit.controller.*.*(..)) "
|
||||
+ " || execution( * org.eclipse.hawkbit.rest.resource.*.*(..)) "
|
||||
+ " || execution( * org.eclipse.hawkbit.service.*.*(..)) )", throwing = "ex")
|
||||
// Exception squid:S00112 - Is aspectJ proxy
|
||||
@SuppressWarnings({ "squid:S00112" })
|
||||
public void catchAndWrapJpaExceptionsService(final Exception ex) throws Throwable {
|
||||
LOG.trace("exception occured", ex);
|
||||
Exception translatedAccessException = translateEclipseLinkExceptionIfPossible(ex);
|
||||
|
||||
if (translatedAccessException == null && ex instanceof TransactionSystemException) {
|
||||
final TransactionSystemException systemException = (TransactionSystemException) ex;
|
||||
translatedAccessException = translateEclipseLinkExceptionIfPossible(
|
||||
(Exception) systemException.getOriginalException());
|
||||
}
|
||||
|
||||
if (translatedAccessException == null) {
|
||||
translatedAccessException = ex;
|
||||
}
|
||||
|
||||
Exception mappingException = translatedAccessException;
|
||||
|
||||
LOG.trace("translated excpetion is", translatedAccessException);
|
||||
for (final Class<?> mappedEx : MAPPED_EXCEPTION_ORDER) {
|
||||
|
||||
if (mappedEx.isAssignableFrom(translatedAccessException.getClass())) {
|
||||
if (!EXCEPTION_MAPPING.containsKey(mappedEx.getName())) {
|
||||
LOG.error("there is no mapping configured for exception class {}", mappedEx.getName());
|
||||
mappingException = new GenericSpServerException(ex);
|
||||
} else {
|
||||
mappingException = (Exception) Class.forName(EXCEPTION_MAPPING.get(mappedEx.getName()))
|
||||
.getConstructor(Throwable.class).newInstance(ex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
LOG.trace("mapped exception {} to {}", translatedAccessException.getClass(), mappingException.getClass());
|
||||
throw mappingException;
|
||||
}
|
||||
|
||||
private DataAccessException translateEclipseLinkExceptionIfPossible(final Exception exception) {
|
||||
final DataAccessException translatedAccessException = jpaVendorAdapter.getJpaDialect()
|
||||
.translateExceptionIfPossible((RuntimeException) exception);
|
||||
return translateSQLStateExceptionIfPossible(translatedAccessException);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* There is no EclipseLinkExceptionTranslator. So we have to check and
|
||||
* translate the exception by the sql error code. Luckily, there we can use
|
||||
* {@link SQLStateSQLExceptionTranslator} if we can get a
|
||||
* {@link SQLException}.
|
||||
*
|
||||
* @param accessException
|
||||
* the base access exception from jpa
|
||||
* @return the translated accessException
|
||||
*/
|
||||
private DataAccessException translateSQLStateExceptionIfPossible(final DataAccessException accessException) {
|
||||
if (!(accessException instanceof JpaSystemException)) {
|
||||
return accessException;
|
||||
}
|
||||
|
||||
final SQLException ex = findSqlException((JpaSystemException) accessException);
|
||||
|
||||
if (ex == null) {
|
||||
return accessException;
|
||||
}
|
||||
|
||||
return sqlStateExceptionTranslator.translate(null, null, ex);
|
||||
}
|
||||
|
||||
private static SQLException findSqlException(final JpaSystemException jpaSystemException) {
|
||||
Throwable exception = jpaSystemException.getCause();
|
||||
while (exception != null) {
|
||||
final Throwable cause = exception.getCause();
|
||||
if (cause instanceof SQLException) {
|
||||
return (SQLException) cause;
|
||||
}
|
||||
exception = cause;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 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.cache;
|
||||
|
||||
import static java.lang.annotation.ElementType.FIELD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.eclipse.hawkbit.eventbus.CacheFieldEntityListener;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.data.annotation.Transient;
|
||||
|
||||
/**
|
||||
* Marks an field within a JPA entity as transient and this field should be
|
||||
* loaded from a configured {@link CacheManager} by using the JPA entity
|
||||
* listeners.
|
||||
*
|
||||
*
|
||||
*
|
||||
* @see CacheFieldEntityListener
|
||||
*/
|
||||
@Target({ FIELD })
|
||||
@Retention(RUNTIME)
|
||||
@Transient
|
||||
public @interface CacheField {
|
||||
|
||||
/**
|
||||
* @return the cache key name for this cacheable field which is used to
|
||||
* store the value.
|
||||
*/
|
||||
String key();
|
||||
|
||||
}
|
||||
56
hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/cache/CacheKeys.java
vendored
Normal file
56
hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/cache/CacheKeys.java
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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.cache;
|
||||
|
||||
import org.eclipse.hawkbit.eventbus.CacheFieldEntityListener;
|
||||
|
||||
/**
|
||||
* Constants for cache keys used in multiple classes.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* @see CacheFieldEntityListener
|
||||
* @see CacheWriteNotify
|
||||
* @see CacheField
|
||||
*
|
||||
*/
|
||||
public final class CacheKeys {
|
||||
|
||||
/**
|
||||
* The cache key name for the {@link UpdateActionStatus} download progress
|
||||
* event.
|
||||
*/
|
||||
public static final String DOWNLOAD_PROGRESS_PERCENT = "download.progress.percent";
|
||||
public static final String ROLLOUT_GROUP_CREATED = "rollout.group.created";
|
||||
public static final String ROLLOUT_GROUP_TOTAL = "rollout.group.total";
|
||||
|
||||
/**
|
||||
* utility class only private constructor.
|
||||
*/
|
||||
private CacheKeys() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* calculates the cache key for a specific entity. The cache key must be
|
||||
* different for each different identifiable entity by its ID.
|
||||
*
|
||||
* @param entityId
|
||||
* the ID of the entity to build the specific cache key for this
|
||||
* entity
|
||||
* @param cacheKey
|
||||
* the cache key for the field to be cached
|
||||
* @return the combined cache key based on the given {@code entityId} and
|
||||
* {@code cacheKey}
|
||||
*/
|
||||
public static String entitySpecificCacheKey(final String entityId, final String cacheKey) {
|
||||
return entityId + "." + cacheKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* 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.cache;
|
||||
|
||||
import org.eclipse.hawkbit.eventbus.event.DownloadProgressEvent;
|
||||
import org.eclipse.hawkbit.eventbus.event.RolloutGroupCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
* An service which combines the functionality for functional use cases to write
|
||||
* into the cache an notify the writing to the cache to the {@link EventBus}.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Service
|
||||
public class CacheWriteNotify {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final int DOWNLOAD_PROGRESS_MAX = 100;
|
||||
|
||||
@Autowired
|
||||
private CacheManager cacheManager;
|
||||
|
||||
@Autowired
|
||||
private EventBus eventBus;
|
||||
|
||||
@Autowired
|
||||
private TenantAware tenantAware;
|
||||
|
||||
/**
|
||||
* writes the download progress in percentage into the cache
|
||||
* {@link CacheKeys#DOWNLOAD_PROGRESS_PERCENT} and notifies the
|
||||
* {@link EventBus} with a {@link DownloadProgressEvent}.
|
||||
*
|
||||
* @param statusId
|
||||
* the ID of the {@link ActionStatus}
|
||||
* @param progressPercent
|
||||
* the progress in percentage which must be between 0-100
|
||||
*/
|
||||
public void downloadProgressPercent(final long statusId, final int progressPercent) {
|
||||
|
||||
final Cache cache = cacheManager.getCache(Action.class.getName());
|
||||
final String cacheKey = CacheKeys.entitySpecificCacheKey(String.valueOf(statusId),
|
||||
CacheKeys.DOWNLOAD_PROGRESS_PERCENT);
|
||||
if (progressPercent < DOWNLOAD_PROGRESS_MAX) {
|
||||
cache.put(cacheKey, progressPercent);
|
||||
} else {
|
||||
// in case we reached progress 100 delete the cache value again
|
||||
// because otherwise he will
|
||||
// keep there forever
|
||||
cache.evict(cacheKey);
|
||||
}
|
||||
|
||||
eventBus.post(new DownloadProgressEvent(tenantAware.getCurrentTenant(), statusId, progressPercent));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the {@link CacheKeys#ROLLOUT_GROUP_CREATED} and
|
||||
* {@link CacheKeys#ROLLOUT_GROUP_TOTAL} into the cache and notfies the
|
||||
* {@link EventBus} with a {@link RolloutGroupCreatedEvent}.
|
||||
*
|
||||
* @param revision
|
||||
* the revision of the event
|
||||
* @param rolloutId
|
||||
* the ID of the rollout the group has been created
|
||||
* @param rolloutGroupId
|
||||
* the ID of the rollout group which has been created
|
||||
* @param totalRolloutGroup
|
||||
* the total number of rollout groups for this rollout
|
||||
* @param createdRolloutGroup
|
||||
* the number of already created groups of the rollout
|
||||
*/
|
||||
public void rolloutGroupCreated(final long revision, final Long rolloutId, final Long rolloutGroupId,
|
||||
final int totalRolloutGroup, final int createdRolloutGroup) {
|
||||
|
||||
final Cache cache = cacheManager.getCache(Rollout.class.getName());
|
||||
final String cacheKeyGroupTotal = CacheKeys.entitySpecificCacheKey(String.valueOf(rolloutId),
|
||||
CacheKeys.ROLLOUT_GROUP_TOTAL);
|
||||
final String cacheKeyGroupCreated = CacheKeys.entitySpecificCacheKey(String.valueOf(rolloutId),
|
||||
CacheKeys.ROLLOUT_GROUP_CREATED);
|
||||
if (createdRolloutGroup < totalRolloutGroup) {
|
||||
cache.put(cacheKeyGroupTotal, totalRolloutGroup);
|
||||
cache.put(cacheKeyGroupCreated, createdRolloutGroup);
|
||||
} else {
|
||||
// in case we reached progress 100 delete the cache value again
|
||||
// because otherwise he will keep there forever
|
||||
cache.evict(cacheKeyGroupTotal);
|
||||
cache.evict(cacheKeyGroupCreated);
|
||||
}
|
||||
eventBus.post(new RolloutGroupCreatedEvent(tenantAware.getCurrentTenant(), revision, rolloutId, rolloutGroupId,
|
||||
totalRolloutGroup, createdRolloutGroup));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cacheManager
|
||||
* the cacheManager to set
|
||||
*/
|
||||
void setCacheManager(final CacheManager cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param eventBus
|
||||
* the eventBus to set
|
||||
*/
|
||||
void setEventBus(final EventBus eventBus) {
|
||||
this.eventBus = eventBus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tenantAware
|
||||
* the tenantAware to set
|
||||
*/
|
||||
void setTenantAware(final TenantAware tenantAware) {
|
||||
this.tenantAware = tenantAware;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* 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.eventbus;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import javax.persistence.PostLoad;
|
||||
import javax.persistence.PostRemove;
|
||||
|
||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||
import org.eclipse.hawkbit.cache.CacheField;
|
||||
import org.eclipse.hawkbit.cache.CacheKeys;
|
||||
import org.eclipse.hawkbit.repository.model.helper.CacheManagerHolder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.Cache.ValueWrapper;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.hateoas.Identifiable;
|
||||
|
||||
/**
|
||||
* An JPA entity listener which enriches the JPA entity fields which are
|
||||
* annotated with {@link CacheField} with values from the {@link CacheManager}.
|
||||
* Only JPA entities which are implementing {@link Identifiable} can be handled
|
||||
* by this entity listener cause the cache keys are calculated with the ID of
|
||||
* the entity.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class CacheFieldEntityListener {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(CacheFieldEntityListener.class);
|
||||
|
||||
/**
|
||||
* enriches the JPA entities after loading the entity from the SQL database.
|
||||
*
|
||||
* @param target
|
||||
* the target which has been loaded from the database
|
||||
*/
|
||||
@PostLoad
|
||||
public void postLoad(final Object target) {
|
||||
if (target instanceof Identifiable) {
|
||||
final CacheManager cacheManager = CacheManagerHolder.getInstance().getCacheManager();
|
||||
@SuppressWarnings("rawtypes")
|
||||
final String id = ((Identifiable) target).getId().toString();
|
||||
final Class<? extends Object> type = target.getClass();
|
||||
findCacheFields(type, id, new CacheFieldCallback() {
|
||||
@Override
|
||||
public void fromCache(final Field field, final String cacheKey, final Serializable id)
|
||||
throws IllegalAccessException {
|
||||
final Cache cache = cacheManager.getCache(type.getName());
|
||||
final ValueWrapper valueWrapper = cache
|
||||
.get(CacheKeys.entitySpecificCacheKey(id.toString(), cacheKey));
|
||||
if (valueWrapper != null && valueWrapper.get() != null) {
|
||||
FieldUtils.writeField(field, target, valueWrapper.get(), true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes the associated cache fields from the cache when deleted the
|
||||
* entity from the database.
|
||||
*
|
||||
* @param target
|
||||
* the entity which has been deleted
|
||||
*/
|
||||
@PostRemove
|
||||
public void postDelete(final Object target) {
|
||||
if (target instanceof Identifiable) {
|
||||
final CacheManager cacheManager = CacheManagerHolder.getInstance().getCacheManager();
|
||||
@SuppressWarnings("rawtypes")
|
||||
final String id = ((Identifiable) target).getId().toString();
|
||||
final Class<? extends Object> type = target.getClass();
|
||||
findCacheFields(type, id, new CacheFieldCallback() {
|
||||
@Override
|
||||
public void fromCache(final Field field, final String cacheKey, final Serializable id)
|
||||
throws IllegalAccessException {
|
||||
final Cache cache = cacheManager.getCache(type.getName());
|
||||
cache.evict(CacheKeys.entitySpecificCacheKey(id.toString(), cacheKey));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void findCacheFields(final Class<? extends Object> type, final Serializable id,
|
||||
final CacheFieldCallback callback) {
|
||||
final Field[] declaredFields = type.getDeclaredFields();
|
||||
for (final Field field : declaredFields) {
|
||||
if (field.getAnnotation(CacheField.class) != null) {
|
||||
try {
|
||||
final CacheField annotation = field.getAnnotation(CacheField.class);
|
||||
callback.fromCache(field, annotation.key(), id);
|
||||
} catch (final IllegalAccessException e) {
|
||||
LOGGER.error("cannot access the field {} for the entity {}, ignoring the cacheable field", field,
|
||||
type, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private interface CacheFieldCallback {
|
||||
/**
|
||||
* callback methods which is called by the
|
||||
* {@link CacheFieldEntityListener#findCacheFields(Class, Serializable, CacheFieldCallback)}
|
||||
* in case a field is annotated with {@link CacheField}.
|
||||
*
|
||||
* @param field
|
||||
* the field which is annotaed with {@link CacheField}
|
||||
* @param cacheKey
|
||||
* the configured cache key in the annotation
|
||||
* {@link CacheField#key()}
|
||||
* @param id
|
||||
* the ID of the entity
|
||||
* @throws IllegalAccessException
|
||||
* in case the field cannot be accessed
|
||||
*/
|
||||
void fromCache(final Field field, final String cacheKey, Serializable id) throws IllegalAccessException;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* 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.eventbus;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
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.jpa.TargetRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetInfo;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
* An aspect implementation which wraps the necessary repository services for
|
||||
* saving {@link TenantAwareBaseEntity}s to publish create or update events.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Service
|
||||
@Aspect
|
||||
public class EntityChangeEventListener {
|
||||
|
||||
@Autowired
|
||||
private EventBus eventBus;
|
||||
|
||||
@Autowired
|
||||
private TenantAware tenantAware;
|
||||
|
||||
@Autowired
|
||||
private EntityManager entityManager;
|
||||
|
||||
@Autowired
|
||||
private AfterTransactionCommitExecutor afterCommit;
|
||||
|
||||
/**
|
||||
* In case the a {@link Target} is created a corresponding
|
||||
* {@link TargetInfo} is created as well. We need the {@link TargetInfo}
|
||||
* information in the target created event. So we are listening to the
|
||||
* {@link TargetInfo} creation to indicate if an Target has been created.
|
||||
*
|
||||
* @param joinpoint
|
||||
* the aspect join point
|
||||
* @return the object of the {@link ProceedingJoinPoint#proceed()}
|
||||
* @throws Throwable
|
||||
* in case exception happens in the
|
||||
* {@link ProceedingJoinPoint#proceed()}
|
||||
*/
|
||||
@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 {
|
||||
final boolean isNew = isTargetInfoNew(joinpoint.getArgs()[0]);
|
||||
final Object result = joinpoint.proceed();
|
||||
if (result instanceof TargetInfo) {
|
||||
if (isNew) {
|
||||
notifyTargetCreated(entityManager.merge(entityManager.merge(((TargetInfo) result).getTarget())));
|
||||
} else {
|
||||
notifyTargetInfoChanged((TargetInfo) result);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy method around the delete method of the {@link TargetRepository} to
|
||||
* notify the {@link TargetDeletedEvent} in case targets has been deleted.
|
||||
*
|
||||
* @param joinpoint
|
||||
* the aspect join point
|
||||
* @return the object of the {@link ProceedingJoinPoint#proceed()}
|
||||
* @throws Throwable
|
||||
* in case exception happens in the
|
||||
* {@link ProceedingJoinPoint#proceed()}
|
||||
*/
|
||||
@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 {
|
||||
final String currentTenant = tenantAware.getCurrentTenant();
|
||||
final Object result = joinpoint.proceed();
|
||||
final Collection<Long> targetIds = (Collection<Long>) joinpoint.getArgs()[0];
|
||||
targetIds.forEach(targetId -> notifyTargetDeleted(currentTenant, targetId));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy method around the delete method of the {@link TargetRepository} to
|
||||
* notify the {@link TargetDeletedEvent} in case targets has been deleted.
|
||||
*
|
||||
* @param joinpoint
|
||||
* the aspect join point
|
||||
* @return the object of the {@link ProceedingJoinPoint#proceed()}
|
||||
* @throws Throwable
|
||||
* in case exception happens in the
|
||||
* {@link ProceedingJoinPoint#proceed()}
|
||||
*/
|
||||
@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();
|
||||
final Object result = joinpoint.proceed();
|
||||
final Object param = joinpoint.getArgs()[0];
|
||||
// delete by id
|
||||
if (param instanceof Long) {
|
||||
notifyTargetDeleted(currentTenant, (Long) param);
|
||||
} else if (param instanceof Target) {
|
||||
notifyTargetDeleted(currentTenant, ((Target) param).getId());
|
||||
} else if (param instanceof Iterable) {
|
||||
((Iterable<Target>) param).forEach(target -> notifyTargetDeleted(currentTenant, target.getId()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void notifyTargetCreated(final Target t) {
|
||||
afterCommit.afterCommit(() -> eventBus.post(new TargetCreatedEvent(t)));
|
||||
|
||||
}
|
||||
|
||||
private void notifyTargetInfoChanged(final TargetInfo targetInfo) {
|
||||
afterCommit.afterCommit(() -> eventBus.post(new TargetInfoUpdateEvent(targetInfo)));
|
||||
}
|
||||
|
||||
private void notifyTargetDeleted(final String tenant, final Long targetId) {
|
||||
afterCommit.afterCommit(() -> eventBus.post(new TargetDeletedEvent(tenant, targetId)));
|
||||
}
|
||||
|
||||
private static boolean isTargetInfoNew(final Object targetInfo) {
|
||||
return ((JpaTargetInfo) targetInfo).isNew();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* 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.eventbus;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.eventbus.event.AbstractPropertyChangeEvent;
|
||||
import org.eclipse.hawkbit.eventbus.event.ActionCreatedEvent;
|
||||
import org.eclipse.hawkbit.eventbus.event.ActionPropertyChangeEvent;
|
||||
import org.eclipse.hawkbit.eventbus.event.RolloutGroupPropertyChangeEvent;
|
||||
import org.eclipse.hawkbit.eventbus.event.RolloutPropertyChangeEvent;
|
||||
import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.eclipse.hawkbit.repository.model.helper.AfterTransactionCommitExecutorHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.EventBusHolder;
|
||||
import org.eclipse.persistence.descriptors.DescriptorEvent;
|
||||
import org.eclipse.persistence.descriptors.DescriptorEventAdapter;
|
||||
import org.eclipse.persistence.internal.sessions.ObjectChangeSet;
|
||||
import org.eclipse.persistence.queries.UpdateObjectQuery;
|
||||
import org.eclipse.persistence.sessions.changesets.DirectToFieldChangeRecord;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
* Listens to change in property values of an entity.
|
||||
*
|
||||
*/
|
||||
public class EntityPropertyChangeListener extends DescriptorEventAdapter {
|
||||
|
||||
@Override
|
||||
public void postInsert(final DescriptorEvent event) {
|
||||
if (event.getObject().getClass().equals(Action.class)) {
|
||||
final Action action = (Action) event.getObject();
|
||||
if (action.getRollout() != null) {
|
||||
final EventBus eventBus = getEventBus();
|
||||
final AfterTransactionCommitExecutor afterCommit = getAfterTransactionCommmitExecutor();
|
||||
afterCommit.afterCommit(() -> eventBus.post(new ActionCreatedEvent(action)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postUpdate(final DescriptorEvent event) {
|
||||
if (event.getObject().getClass().equals(Action.class)) {
|
||||
getAfterTransactionCommmitExecutor().afterCommit(() -> getEventBus().post(
|
||||
new ActionPropertyChangeEvent((Action) event.getObject(), getChangeSet(Action.class, event))));
|
||||
} else if (event.getObject().getClass().equals(Rollout.class)) {
|
||||
getAfterTransactionCommmitExecutor().afterCommit(() -> getEventBus().post(
|
||||
new RolloutPropertyChangeEvent((Rollout) event.getObject(), getChangeSet(Rollout.class, event))));
|
||||
} else if (event.getObject().getClass().equals(RolloutGroup.class)) {
|
||||
getAfterTransactionCommmitExecutor().afterCommit(
|
||||
() -> getEventBus().post(new RolloutGroupPropertyChangeEvent((RolloutGroup) event.getObject(),
|
||||
getChangeSet(RolloutGroup.class, event))));
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends TenantAwareBaseEntity> Map<String, AbstractPropertyChangeEvent<T>.Values> getChangeSet(
|
||||
final Class<T> clazz, final DescriptorEvent event) {
|
||||
final T rolloutGroup = clazz.cast(event.getObject());
|
||||
final ObjectChangeSet changeSet = ((UpdateObjectQuery) event.getQuery()).getObjectChangeSet();
|
||||
return changeSet.getChanges().stream().filter(record -> record instanceof DirectToFieldChangeRecord)
|
||||
.map(record -> (DirectToFieldChangeRecord) record)
|
||||
.collect(Collectors.toMap(record -> record.getAttribute(),
|
||||
record -> new AbstractPropertyChangeEvent<T>(rolloutGroup, null).new Values(
|
||||
record.getOldValue(), record.getNewValue())));
|
||||
}
|
||||
|
||||
private AfterTransactionCommitExecutor getAfterTransactionCommmitExecutor() {
|
||||
return AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit();
|
||||
}
|
||||
|
||||
private EventBus getEventBus() {
|
||||
return EventBusHolder.getInstance().getEventBus();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* 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.eventbus;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.eclipse.hawkbit.eventbus.EventSubscriber;
|
||||
import org.eclipse.hawkbit.eventbus.event.ActionCreatedEvent;
|
||||
import org.eclipse.hawkbit.eventbus.event.ActionPropertyChangeEvent;
|
||||
import org.eclipse.hawkbit.eventbus.event.Event;
|
||||
import org.eclipse.hawkbit.eventbus.event.RolloutChangeEvent;
|
||||
import org.eclipse.hawkbit.eventbus.event.RolloutGroupChangeEvent;
|
||||
import org.eclipse.hawkbit.eventbus.event.RolloutGroupCreatedEvent;
|
||||
import org.eclipse.hawkbit.eventbus.event.RolloutGroupPropertyChangeEvent;
|
||||
import org.eclipse.hawkbit.eventbus.event.RolloutPropertyChangeEvent;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
|
||||
import com.google.common.eventbus.AllowConcurrentEvents;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
|
||||
/**
|
||||
* Collects and merges fine grained events to more generic events and push them
|
||||
* in a fixed delay on the events bus. This helps for code which are not
|
||||
* interested in all fine grained events, e.g. UI code. The UI code is not
|
||||
* interested in handling the flood of events, so collecting the events and
|
||||
* merge them to one event together and post them in a fixed interval is easier
|
||||
* to consume e.g. for push notifcations on UI.
|
||||
*
|
||||
* @author Michael Hirsch
|
||||
*
|
||||
*/
|
||||
@EventSubscriber
|
||||
public class EventMerger {
|
||||
|
||||
private static final Set<RolloutEventKey> rolloutEvents = ConcurrentHashMap.newKeySet();
|
||||
private static final Set<RolloutEventKey> rolloutGroupEvents = ConcurrentHashMap.newKeySet();
|
||||
|
||||
@Autowired
|
||||
private EventBus eventBus;
|
||||
|
||||
/**
|
||||
* Checks if there are events to publish in the fixed interval.
|
||||
*/
|
||||
@Scheduled(initialDelay = 10000, fixedDelay = 2000)
|
||||
public void rolloutEventScheduler() {
|
||||
final Iterator<RolloutEventKey> rolloutIterator = rolloutEvents.iterator();
|
||||
while (rolloutIterator.hasNext()) {
|
||||
final RolloutEventKey eventKey = rolloutIterator.next();
|
||||
eventBus.post(new RolloutChangeEvent(1, eventKey.tenant, eventKey.rolloutId));
|
||||
rolloutIterator.remove();
|
||||
}
|
||||
|
||||
final Iterator<RolloutEventKey> rolloutGroupIterator = rolloutGroupEvents.iterator();
|
||||
while (rolloutGroupIterator.hasNext()) {
|
||||
final RolloutEventKey eventKey = rolloutGroupIterator.next();
|
||||
eventBus.post(new RolloutGroupChangeEvent(1, eventKey.tenant, eventKey.rolloutId, eventKey.rolloutGroupId));
|
||||
rolloutGroupIterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the event bus to retrieve all necessary events to collect and
|
||||
* merge.
|
||||
*
|
||||
* @param event
|
||||
* the event on the event bus
|
||||
*/
|
||||
@Subscribe
|
||||
@AllowConcurrentEvents
|
||||
public void onEvent(final Event event) {
|
||||
Long rolloutId = null;
|
||||
Long rolloutGroupId = null;
|
||||
if (event instanceof ActionCreatedEvent) {
|
||||
rolloutId = getRolloutId(((ActionCreatedEvent) event).getEntity().getRollout());
|
||||
rolloutGroupId = getRolloutGroupId(((ActionCreatedEvent) event).getEntity().getRolloutGroup());
|
||||
} else if (event instanceof ActionPropertyChangeEvent) {
|
||||
rolloutId = getRolloutId(((ActionPropertyChangeEvent) event).getEntity().getRollout());
|
||||
rolloutGroupId = getRolloutGroupId(((ActionPropertyChangeEvent) event).getEntity().getRolloutGroup());
|
||||
} else if (event instanceof RolloutPropertyChangeEvent) {
|
||||
rolloutId = ((RolloutPropertyChangeEvent) event).getEntity().getId();
|
||||
} else if (event instanceof RolloutGroupCreatedEvent) {
|
||||
rolloutId = ((RolloutGroupCreatedEvent) event).getRolloutId();
|
||||
rolloutGroupId = ((RolloutGroupCreatedEvent) event).getRolloutGroupId();
|
||||
} else if (event instanceof RolloutGroupPropertyChangeEvent) {
|
||||
final RolloutGroup rolloutGroup = ((RolloutGroupPropertyChangeEvent) event).getEntity();
|
||||
rolloutId = rolloutGroup.getRollout().getId();
|
||||
rolloutGroupId = rolloutGroup.getId();
|
||||
}
|
||||
|
||||
if (rolloutId != null) {
|
||||
rolloutEvents.add(new RolloutEventKey(rolloutId, event.getTenant()));
|
||||
if (rolloutGroupId != null) {
|
||||
rolloutGroupEvents.add(new RolloutEventKey(rolloutId, rolloutGroupId, event.getTenant()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Long getRolloutGroupId(final RolloutGroup rolloutGroup) {
|
||||
if (rolloutGroup != null) {
|
||||
return rolloutGroup.getId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Long getRolloutId(final Rollout rollout) {
|
||||
if (rollout != null) {
|
||||
return rollout.getId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rollout key in the concurrent set to be hold.
|
||||
*
|
||||
* @author Michael Hirsch
|
||||
*
|
||||
*/
|
||||
private static final class RolloutEventKey {
|
||||
private final Long rolloutId;
|
||||
private final String tenant;
|
||||
private final Long rolloutGroupId;
|
||||
|
||||
private RolloutEventKey(final Long rolloutId, final Long rolloutGroupId, final String tenant) {
|
||||
this.rolloutGroupId = rolloutGroupId;
|
||||
this.rolloutId = rolloutId;
|
||||
this.tenant = tenant;
|
||||
}
|
||||
|
||||
private RolloutEventKey(final Long rolloutId, final String tenant) {
|
||||
this(rolloutId, null, tenant);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {// NOSONAR - as this is generated
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((rolloutGroupId == null) ? 0 : rolloutGroupId.hashCode());
|
||||
result = prime * result + ((rolloutId == null) ? 0 : rolloutId.hashCode());
|
||||
result = prime * result + ((tenant == null) ? 0 : tenant.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 RolloutEventKey other = (RolloutEventKey) obj;
|
||||
if (rolloutGroupId == null) {
|
||||
if (other.rolloutGroupId != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!rolloutGroupId.equals(other.rolloutGroupId)) {
|
||||
return false;
|
||||
}
|
||||
if (rolloutId == null) {
|
||||
if (other.rolloutId != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!rolloutId.equals(other.rolloutId)) {
|
||||
return false;
|
||||
}
|
||||
if (tenant == null) {
|
||||
if (other.tenant != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!tenant.equals(other.tenant)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 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.executor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
/**
|
||||
*
|
||||
* A Service which calls register runnable. This runnables will executed after a
|
||||
* successful spring transaction commit.The class is thread safe.
|
||||
*/
|
||||
@Service
|
||||
public class AfterTransactionCommitDefaultServiceExecutor extends TransactionSynchronizationAdapter
|
||||
implements AfterTransactionCommitExecutor {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AfterTransactionCommitDefaultServiceExecutor.class);
|
||||
private static final ThreadLocal<List<Runnable>> THREAD_LOCAL_RUNNABLES = new ThreadLocal<>();
|
||||
|
||||
@Override
|
||||
// Exception squid:S1217 - Is aspectJ proxy
|
||||
@SuppressWarnings({ "squid:S1217" })
|
||||
public void afterCommit() {
|
||||
final List<Runnable> afterCommitRunnables = THREAD_LOCAL_RUNNABLES.get();
|
||||
LOGGER.debug("Transaction successfully committed, executing {} runnables", afterCommitRunnables.size());
|
||||
for (final Runnable afterCommitRunnable : afterCommitRunnables) {
|
||||
LOGGER.debug("Executing runnable {}", afterCommitRunnable);
|
||||
try {
|
||||
afterCommitRunnable.run();
|
||||
} catch (final RuntimeException e) {
|
||||
LOGGER.error("Failed to execute runnable " + afterCommitRunnable, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCommit(final Runnable runnable) {
|
||||
LOGGER.debug("Submitting new runnable {} to run after transaction commit", runnable);
|
||||
if (TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
List<Runnable> localRunnables = THREAD_LOCAL_RUNNABLES.get();
|
||||
if (localRunnables == null) {
|
||||
localRunnables = new ArrayList<>();
|
||||
THREAD_LOCAL_RUNNABLES.set(localRunnables);
|
||||
TransactionSynchronizationManager.registerSynchronization(this);
|
||||
}
|
||||
localRunnables.add(runnable);
|
||||
return;
|
||||
}
|
||||
LOGGER.info("Transaction synchronization is NOT ACTIVE/ INACTIVE. Executing right now runnable {}", runnable);
|
||||
runnable.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "squid:S1217" })
|
||||
public void afterCompletion(final int status) {
|
||||
final String transactionStatus = status == STATUS_COMMITTED ? "COMMITTED" : "ROLLEDBACK";
|
||||
LOGGER.debug("Transaction completed after commit with status {}", transactionStatus);
|
||||
THREAD_LOCAL_RUNNABLES.remove();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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.executor;
|
||||
|
||||
/**
|
||||
*
|
||||
* A interface to register a runnable, which will be executed after a successful
|
||||
* spring transaction.
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface AfterTransactionCommitExecutor {
|
||||
|
||||
/**
|
||||
* Register a runnable which will be executed after a successful spring
|
||||
* transaction.
|
||||
*
|
||||
* @param runnable
|
||||
* the after commit runnable
|
||||
*/
|
||||
void afterCommit(Runnable runnable);
|
||||
}
|
||||
@@ -0,0 +1,396 @@
|
||||
/**
|
||||
* 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.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
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.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.jpa.repository.EntityGraph;
|
||||
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link Action} repository.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>, JpaSpecificationExecutor<JpaAction> {
|
||||
/**
|
||||
* Retrieves an Action with all lazy attributes.
|
||||
*
|
||||
* @param actionId
|
||||
* the ID of the action
|
||||
* @return the found {@link Action}
|
||||
*/
|
||||
@EntityGraph(value = "Action.all", type = EntityGraphType.LOAD)
|
||||
JpaAction findById(Long actionId);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link Action}s which are referring the given
|
||||
* {@link DistributionSet}.
|
||||
*
|
||||
* @param pageable
|
||||
* page parameters
|
||||
* @param ds
|
||||
* the {@link DistributionSet} on which will be filtered
|
||||
* @return the found {@link Action}s
|
||||
*/
|
||||
Page<Action> findByDistributionSet(final Pageable pageable, final JpaDistributionSet ds);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link Action}s which are referring the given
|
||||
* {@link Target}.
|
||||
*
|
||||
* @param pageable
|
||||
* page parameters
|
||||
* @param target
|
||||
* the target to find assigned actions
|
||||
* @return the found {@link Action}s
|
||||
*/
|
||||
Slice<Action> findByTarget(Pageable pageable, JpaTarget target);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link Action}s which are active and referring the given
|
||||
* {@link Target} in a specified order. Loads also the lazy
|
||||
* {@link Action#getDistributionSet()} field.
|
||||
*
|
||||
* @param pageable
|
||||
* page parameters
|
||||
* @param target
|
||||
* the target to find assigned actions
|
||||
* @param active
|
||||
* the action active flag
|
||||
* @return the found {@link Action}s
|
||||
*/
|
||||
@EntityGraph(value = "Action.ds", type = EntityGraphType.LOAD)
|
||||
List<Action> findByTargetAndActiveOrderByIdAsc(final JpaTarget target, boolean active);
|
||||
|
||||
/**
|
||||
* Retrieves latest {@link UpdateAction} for given target and
|
||||
* {@link SoftwareModule}.
|
||||
*
|
||||
* @param targetId
|
||||
* to search for
|
||||
* @param module
|
||||
* to search for
|
||||
* @return action if there is one with assigned target and module is part of
|
||||
* assigned {@link DistributionSet}.
|
||||
*/
|
||||
@Query("Select a from JpaAction a join a.distributionSet ds join ds.modules modul where a.target.controllerId = :target and modul = :module order by a.id desc")
|
||||
List<Action> findActionByTargetAndSoftwareModule(@Param("target") final String targetId,
|
||||
@Param("module") JpaSoftwareModule module);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link UpdateAction}s which are referring the given
|
||||
* {@link DistributionSet} and {@link Target}.
|
||||
*
|
||||
* @param pageable
|
||||
* page parameters
|
||||
* @param target
|
||||
* is the assigned target
|
||||
* @param ds
|
||||
* the {@link DistributionSet} on which will be filtered
|
||||
* @return the found {@link UpdateAction}s
|
||||
*/
|
||||
@Query("Select a from JpaAction a where a.target = :target and a.distributionSet = :ds order by a.id")
|
||||
Page<Action> findByTargetAndDistributionSet(final Pageable pageable, @Param("target") final JpaTarget target,
|
||||
@Param("ds") JpaDistributionSet ds);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link Action}s of a specific target, without pagination
|
||||
* ordered by action ID.
|
||||
*
|
||||
* @param target
|
||||
* to search for
|
||||
* @return a list of actions according to the searched target
|
||||
*/
|
||||
@Query("Select a from JpaAction a where a.target = :target order by a.id")
|
||||
List<Action> findByTarget(@Param("target") final JpaTarget target);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link Action}s of a specific target and given active flag
|
||||
* ordered by action ID.
|
||||
*
|
||||
* @param pageable
|
||||
* the pagination parameter
|
||||
* @param target
|
||||
* to search for
|
||||
* @param active
|
||||
* {@code true} for all actions which are currently active,
|
||||
* {@code false} for inactive
|
||||
* @return a paged list of actions ordered by action ID
|
||||
*/
|
||||
@Query("Select a from JpaAction a where a.target = :target and a.active= :active order by a.id")
|
||||
Page<Action> findByActiveAndTarget(Pageable pageable, @Param("target") JpaTarget target,
|
||||
@Param("active") boolean active);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link Action}s of a specific target and given active flag
|
||||
* ordered by action ID. Loads also the lazy
|
||||
* {@link Action#getDistributionSet()} field.
|
||||
*
|
||||
* @param target
|
||||
* to search for
|
||||
* @param active
|
||||
* {@code true} for all actions which are currently active,
|
||||
* {@code false} for inactive
|
||||
* @return a list of actions ordered by action ID
|
||||
*/
|
||||
@EntityGraph(value = "Action.ds", type = EntityGraphType.LOAD)
|
||||
@Query("Select a from JpaAction a where a.target = :target and a.active= :active order by a.id")
|
||||
List<Action> findByActiveAndTarget(@Param("target") JpaTarget target, @Param("active") boolean active);
|
||||
|
||||
/**
|
||||
* Updates all {@link Action} to inactive for all targets with given ID.
|
||||
*
|
||||
* @param keySet
|
||||
* the list of actions to set inactive
|
||||
* @param targetsIds
|
||||
* the IDs of the targets according to the action to set in
|
||||
* active
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Query("UPDATE JpaAction a SET a.active = false WHERE a IN :keySet AND a.target IN :targetsIds")
|
||||
void setToInactive(@Param("keySet") List<JpaAction> keySet, @Param("targetsIds") List<Long> targetsIds);
|
||||
|
||||
/**
|
||||
* Switches the status of actions from one specific status into another,
|
||||
* only if the actions are in a specific status. This should be a atomar
|
||||
* operation.
|
||||
*
|
||||
* @param statusToSet
|
||||
* the new status the actions should get
|
||||
* @param targetIds
|
||||
* the IDs of the targets of the actions which are affected
|
||||
* @param active
|
||||
* the active flag of the actions which should be affected
|
||||
* @param currentStatus
|
||||
* the current status of the actions which are affected
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Query("UPDATE JpaAction a SET a.status = :statusToSet WHERE a.target IN :targetsIds AND a.active = :active AND a.status = :currentStatus AND a.distributionSet.requiredMigrationStep = false")
|
||||
void switchStatus(@Param("statusToSet") Action.Status statusToSet, @Param("targetsIds") List<Long> targetIds,
|
||||
@Param("active") boolean active, @Param("currentStatus") Action.Status currentStatus);
|
||||
|
||||
/**
|
||||
* Switches the status of actions from one specific status into another,
|
||||
* only if the actions are in a specific status. This should be a atomar
|
||||
* operation.
|
||||
*
|
||||
* @param statusToSet
|
||||
* the new status the actions should get
|
||||
* @param rollout
|
||||
* the rollout of the actions which are affected
|
||||
* @param active
|
||||
* the active flag of the actions which should be affected
|
||||
* @param currentStatus
|
||||
* the current status of the actions which are affected
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Query("UPDATE JpaAction a SET a.status = :statusToSet WHERE a.rollout = :rollout AND a.active = :active AND a.status = :currentStatus")
|
||||
void switchStatus(@Param("statusToSet") Action.Status statusToSet, @Param("rollout") JpaRollout rollout,
|
||||
@Param("active") boolean active, @Param("currentStatus") Action.Status currentStatus);
|
||||
|
||||
/**
|
||||
*
|
||||
* Retrieves all {@link Action}s which are active and referring to the given
|
||||
* target Ids and distribution set required migration step.
|
||||
*
|
||||
* @param targetIds
|
||||
* the IDs of targets for the actions
|
||||
* @param notStatus
|
||||
* the status which the actions should not have
|
||||
* @return the found list of {@link Action}s
|
||||
*/
|
||||
@Query("SELECT a FROM JpaAction a WHERE a.active = true AND a.distributionSet.requiredMigrationStep = false AND a.target IN ?1 AND a.status != ?2")
|
||||
List<JpaAction> findByActiveAndTargetIdInAndActionStatusNotEqualToAndDistributionSetRequiredMigrationStep(
|
||||
Collection<Long> targetIds, Action.Status notStatus);
|
||||
|
||||
/**
|
||||
* Counts all {@link Action}s referring to the given target.
|
||||
*
|
||||
* @param target
|
||||
* the target to count the {@link Action}s
|
||||
* @return the count of actions referring to the given target
|
||||
*/
|
||||
Long countByTarget(JpaTarget target);
|
||||
|
||||
@Override
|
||||
@CacheEvict(value = "feedbackReceivedOverTime", allEntries = true)
|
||||
<S extends JpaAction> List<S> save(Iterable<S> entities);
|
||||
|
||||
@Override
|
||||
@CacheEvict(value = "feedbackReceivedOverTime", allEntries = true)
|
||||
<S extends JpaAction> S save(S entity);
|
||||
|
||||
/**
|
||||
* Counts all {@link Action}s referring to the given DistributionSet.
|
||||
*
|
||||
* @param distributionSet
|
||||
* DistributionSet to count the {@link Action}s from
|
||||
* @return the count of actions referring to the given distributionSet
|
||||
*/
|
||||
Long countByDistributionSet(JpaDistributionSet distributionSet);
|
||||
|
||||
/**
|
||||
* Counts all {@link Action}s referring to the given rollout.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout to count the {@link Action}s from
|
||||
* @return the count of actions referring to the given rollout
|
||||
*/
|
||||
Long countByRollout(JpaRollout rollout);
|
||||
|
||||
/**
|
||||
* Counts all actions referring to a given rollout and rolloutgroup which
|
||||
* are currently not in the given status. An in-clause statement does not
|
||||
* work with the spring-data, so this is specific usecase regarding to the
|
||||
* rollout-management to find out actions which are not in specific states.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout the actions are belong to
|
||||
* @param rolloutGroup
|
||||
* the rolloutgroup the actions are belong to
|
||||
* @param notStatus1
|
||||
* the status the action should not have
|
||||
* @param notStatus2
|
||||
* the status the action should not have
|
||||
* @param notStatus3
|
||||
* the status the action should not have
|
||||
* @return the count of actions referring the rollout and rolloutgroup and
|
||||
* are not in given states
|
||||
*/
|
||||
Long countByRolloutAndRolloutGroupAndStatusNotAndStatusNotAndStatusNot(JpaRollout rollout,
|
||||
JpaRolloutGroup rolloutGroup, Status notStatus1, Status notStatus2, Status notStatus3);
|
||||
|
||||
/**
|
||||
* Counts all actions referring to a given rollout and rolloutgroup.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout the actions belong to
|
||||
* @param rolloutGroup
|
||||
* the rolloutgroup the actions belong to
|
||||
* @return the count of actions referring to a rollout and rolloutgroup
|
||||
*/
|
||||
Long countByRolloutAndRolloutGroup(JpaRollout rollout, JpaRolloutGroup rolloutGroup);
|
||||
|
||||
/**
|
||||
* Counts all actions referring to a given rollout, rolloutgroup and status.
|
||||
*
|
||||
* @param rolloutId
|
||||
* the ID of rollout the actions belong to
|
||||
* @param rolloutGroupId
|
||||
* the ID rolloutgroup the actions belong to
|
||||
* @param status
|
||||
* the status the actions should have
|
||||
* @return the count of actions referring to a rollout, rolloutgroup and are
|
||||
* in a given status
|
||||
*/
|
||||
Long countByRolloutIdAndRolloutGroupIdAndStatus(Long rolloutId, Long rolloutGroupId, Action.Status status);
|
||||
|
||||
/**
|
||||
* Retrieving all actions referring to a given rollout with a specific
|
||||
* action as parent reference and a specific status.
|
||||
*
|
||||
* Finding all actions of a specific rolloutgroup parent relation.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout the actions belong to
|
||||
* @param rolloutGroupParent
|
||||
* the parent rolloutgroup the actions should reference
|
||||
* @param actionStatus
|
||||
* the status the actions have
|
||||
* @return the actions referring a specific rollout and a specific parent
|
||||
* rolloutgroup in a specific status
|
||||
*/
|
||||
List<Action> findByRolloutAndRolloutGroupParentAndStatus(JpaRollout rollout, JpaRolloutGroup rolloutGroupParent,
|
||||
Status actionStatus);
|
||||
|
||||
/**
|
||||
* Retrieves all actions for a specific rollout and in a specific status.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout the actions beglong to
|
||||
* @param actionStatus
|
||||
* the status of the actions
|
||||
* @return the actions referring a specific rollout an in a specific status
|
||||
*/
|
||||
List<Action> findByRolloutAndStatus(JpaRollout rollout, Status actionStatus);
|
||||
|
||||
/**
|
||||
* Get list of objects which has details of status and count of targets in
|
||||
* each status in specified rollout.
|
||||
*
|
||||
* @param rolloutId
|
||||
* id of {@link Rollout}
|
||||
* @return list of objects with status and target count
|
||||
*/
|
||||
@Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus( a.rollout.id, a.status , COUNT(a.target)) FROM JpaAction a WHERE a.rollout.id IN ?1 GROUP BY a.rollout.id,a.status")
|
||||
List<TotalTargetCountActionStatus> getStatusCountByRolloutId(List<Long> rolloutId);
|
||||
|
||||
/**
|
||||
* Get list of objects which has details of status and count of targets in
|
||||
* each status in specified rollout.
|
||||
*
|
||||
* @param rolloutId
|
||||
* id of {@link Rollout}
|
||||
* @return list of objects with status and target count
|
||||
*/
|
||||
@Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus( a.rollout.id, a.status , COUNT(a.target)) FROM JpaAction a WHERE a.rollout.id = ?1 GROUP BY a.rollout.id,a.status")
|
||||
List<TotalTargetCountActionStatus> getStatusCountByRolloutId(Long rolloutId);
|
||||
|
||||
/**
|
||||
* Get list of objects which has details of status and count of targets in
|
||||
* each status in specified rollout group.
|
||||
*
|
||||
* @param rolloutGroupId
|
||||
* id of {@link RolloutGroup}
|
||||
* @return list of objects with status and target count
|
||||
*/
|
||||
@Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus(a.rolloutGroup.id, a.status , COUNT(a.target)) FROM JpaAction a WHERE a.rolloutGroup.id = ?1 GROUP BY a.rolloutGroup.id, a.status")
|
||||
List<TotalTargetCountActionStatus> getStatusCountByRolloutGroupId(Long rolloutGroupId);
|
||||
|
||||
/**
|
||||
* Get list of objects which has details of status and count of targets in
|
||||
* each status in specified rollout group.
|
||||
*
|
||||
* @param rolloutGroupId
|
||||
* list of id of {@link RolloutGroup}
|
||||
* @return list of objects with status and target count
|
||||
*/
|
||||
@Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus(a.rolloutGroup.id, a.status , COUNT(a.target)) FROM JpaAction a WHERE a.rolloutGroup.id IN ?1 GROUP BY a.rolloutGroup.id, a.status")
|
||||
List<TotalTargetCountActionStatus> getStatusCountByRolloutGroupId(List<Long> rolloutGroupId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* 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.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.EntityGraph;
|
||||
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link ActionStatus} repository.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface ActionStatusRepository
|
||||
extends BaseEntityRepository<JpaActionStatus, Long>, JpaSpecificationExecutor<JpaActionStatus> {
|
||||
|
||||
/**
|
||||
* Counts {@link ActionStatus} entries of given {@link Action} in
|
||||
* repository.
|
||||
*
|
||||
* @param action
|
||||
* to count status entries
|
||||
* @return number of actions in repository
|
||||
*/
|
||||
Long countByAction(JpaAction action);
|
||||
|
||||
/**
|
||||
* Counts {@link ActionStatus} entries of given {@link Action} with given
|
||||
* {@link Status} in repository.
|
||||
*
|
||||
* @param action
|
||||
* to count status entries
|
||||
* @param status
|
||||
* to filter for
|
||||
* @return number of actions in repository
|
||||
*/
|
||||
Long countByActionAndStatus(JpaAction action, Status status);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link ActionStatus} entries from repository of given
|
||||
* {@link Action}.
|
||||
*
|
||||
* @param pageReq
|
||||
* parameters
|
||||
* @param action
|
||||
* of the status entries
|
||||
* @return pages list of {@link ActionStatus} entries
|
||||
*/
|
||||
Page<ActionStatus> findByAction(Pageable pageReq, JpaAction action);
|
||||
|
||||
/**
|
||||
* Finds all status updates for the defined action and target including
|
||||
* {@link ActionStatus#getMessages()}.
|
||||
*
|
||||
* @param pageReq
|
||||
* for page configuration
|
||||
* @param target
|
||||
* to look for
|
||||
* @param action
|
||||
* to look for
|
||||
* @return Page with found targets
|
||||
*/
|
||||
@EntityGraph(value = "ActionStatus.withMessages", type = EntityGraphType.LOAD)
|
||||
Page<ActionStatus> getByAction(Pageable pageReq, JpaAction action);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 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 org.eclipse.hawkbit.repository.jpa.model.AbstractJpaTenantAwareBaseEntity;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.repository.NoRepositoryBean;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Command repository operations for all {@link TenantAwareBaseEntity}s.
|
||||
*
|
||||
* @param <T>
|
||||
* type if the entity type
|
||||
* @param <I>
|
||||
* of the entity type
|
||||
*/
|
||||
@NoRepositoryBean
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface BaseEntityRepository<T extends AbstractJpaTenantAwareBaseEntity, I extends Serializable>
|
||||
extends PagingAndSortingRepository<T, I> {
|
||||
|
||||
/**
|
||||
* Deletes all {@link TenantAwareBaseEntity} of a given tenant.
|
||||
*
|
||||
* @param tenant
|
||||
* to delete data from
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
void deleteByTenantIgnoreCase(String tenant);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* 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.stream.Collectors;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
|
||||
/**
|
||||
* Utility class for deployment related topics.
|
||||
*
|
||||
*/
|
||||
public final class DeploymentHelper {
|
||||
|
||||
private DeploymentHelper() {
|
||||
// utility class
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal helper method used only inside service level. As a result is no
|
||||
* additional security necessary.
|
||||
*
|
||||
* @param target
|
||||
* to update
|
||||
* @param status
|
||||
* of the target
|
||||
* @param setInstalledDate
|
||||
* to set
|
||||
* @param entityManager
|
||||
* for the operation
|
||||
* @param targetInfoRepository
|
||||
* for the operation
|
||||
*
|
||||
* @return updated target
|
||||
*/
|
||||
static JpaTarget updateTargetInfo(@NotNull final JpaTarget target, @NotNull final TargetUpdateStatus status,
|
||||
final boolean setInstalledDate, final TargetInfoRepository targetInfoRepository,
|
||||
final EntityManager entityManager) {
|
||||
final JpaTargetInfo ts = (JpaTargetInfo) target.getTargetInfo();
|
||||
ts.setUpdateStatus(status);
|
||||
|
||||
if (setInstalledDate) {
|
||||
ts.setInstallationDate(System.currentTimeMillis());
|
||||
}
|
||||
targetInfoRepository.save(ts);
|
||||
return entityManager.merge(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called, when cancellation has been successful. It sets the
|
||||
* action to canceled, resets the meta data of the target and in case there
|
||||
* is a new action this action is triggered.
|
||||
*
|
||||
* @param action
|
||||
* the action which is set to canceled
|
||||
* @param actionRepository
|
||||
* for the operation
|
||||
* @param targetManagement
|
||||
* for the operation
|
||||
* @param entityManager
|
||||
* for the operation
|
||||
* @param targetInfoRepository
|
||||
* for the operation
|
||||
*/
|
||||
static void successCancellation(final JpaAction action, final ActionRepository actionRepository,
|
||||
final TargetManagement targetManagement, final TargetInfoRepository targetInfoRepository,
|
||||
final EntityManager entityManager) {
|
||||
|
||||
// set action inactive
|
||||
action.setActive(false);
|
||||
action.setStatus(Status.CANCELED);
|
||||
|
||||
final JpaTarget target = (JpaTarget) action.getTarget();
|
||||
final List<Action> nextActiveActions = actionRepository.findByTargetAndActiveOrderByIdAsc(target, true).stream()
|
||||
.filter(a -> !a.getId().equals(action.getId())).collect(Collectors.toList());
|
||||
|
||||
if (nextActiveActions.isEmpty()) {
|
||||
target.setAssignedDistributionSet(target.getTargetInfo().getInstalledDistributionSet());
|
||||
updateTargetInfo(target, TargetUpdateStatus.IN_SYNC, false, targetInfoRepository, entityManager);
|
||||
} else {
|
||||
target.setAssignedDistributionSet(nextActiveActions.get(0).getDistributionSet());
|
||||
}
|
||||
targetManagement.updateTarget(target);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* 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.jpa.model.DsMetadataCompositeKey;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link DistributionSetMetadata} repository.
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface DistributionSetMetadataRepository
|
||||
extends PagingAndSortingRepository<JpaDistributionSetMetadata, DsMetadataCompositeKey>,
|
||||
JpaSpecificationExecutor<JpaDistributionSetMetadata> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* 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.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.Tag;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link DistributionSet} repository.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface DistributionSetRepository
|
||||
extends BaseEntityRepository<JpaDistributionSet, Long>, JpaSpecificationExecutor<JpaDistributionSet> {
|
||||
|
||||
/**
|
||||
* Finds {@link DistributionSet}s by assigned {@link Tag}.
|
||||
*
|
||||
* @param tag
|
||||
* to be found
|
||||
* @return list of found {@link DistributionSet}s
|
||||
*/
|
||||
@Query(value = "Select Distinct ds from JpaDistributionSet ds join ds.tags dst where dst = :tag")
|
||||
List<JpaDistributionSet> findByTag(@Param("tag") final JpaDistributionSetTag tag);
|
||||
|
||||
/**
|
||||
* deletes the {@link DistributionSet}s with the given IDs.
|
||||
*
|
||||
* @param ids
|
||||
* to be deleted
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Query("update JpaDistributionSet d set d.deleted = 1 where d.id in :ids")
|
||||
void deleteDistributionSet(@Param("ids") Long... ids);
|
||||
|
||||
@Override
|
||||
List<JpaDistributionSet> findAll(Iterable<Long> ids);
|
||||
|
||||
/**
|
||||
* deletes {@link DistributionSet}s by the given IDs.
|
||||
*
|
||||
* @param ids
|
||||
* List of IDs of {@link DistributionSet}s to be deleted
|
||||
* @return number of affected/deleted records
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
|
||||
@Query("DELETE FROM JpaDistributionSet d WHERE d.id IN ?1")
|
||||
int deleteByIdIn(Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* Finds {@link DistributionSet}s where given {@link SoftwareModule} is
|
||||
* assigned.
|
||||
*
|
||||
* @param module
|
||||
* to search for
|
||||
* @return {@link List} of found {@link DistributionSet}s
|
||||
*/
|
||||
List<DistributionSet> findByModules(JpaSoftwareModule module);
|
||||
|
||||
/**
|
||||
* Finds {@link DistributionSet}s based on given ID if they are not assigned
|
||||
* yet to an {@link UpdateAction}, i.e. unused.
|
||||
*
|
||||
* @param ids
|
||||
* to search for
|
||||
* @return
|
||||
*/
|
||||
@Query("select ac.distributionSet.id from JpaAction ac where ac.distributionSet.id in :ids")
|
||||
List<Long> findAssignedDistributionSetsById(@Param("ids") Long... ids);
|
||||
|
||||
/**
|
||||
* Saves all given {@link DistributionSet}s.
|
||||
*
|
||||
* @param entities
|
||||
* @return the saved entities
|
||||
* @throws IllegalArgumentException
|
||||
* in case the given entity is (@literal null}.
|
||||
*
|
||||
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable)
|
||||
*/
|
||||
@Override
|
||||
<S extends JpaDistributionSet> List<S> save(Iterable<S> entities);
|
||||
|
||||
/**
|
||||
* Finds the distribution set for a specific action.
|
||||
*
|
||||
* @param action
|
||||
* the action associated with the distribution set to find
|
||||
* @return the distribution set associated with the given action
|
||||
*/
|
||||
@Query("select DISTINCT d from JpaDistributionSet d join fetch d.modules m join d.actions a where a = :action")
|
||||
JpaDistributionSet findByAction(@Param("action") JpaAction action);
|
||||
|
||||
/**
|
||||
* Counts {@link DistributionSet} instances of given type in the repository.
|
||||
*
|
||||
* @param type
|
||||
* to search for
|
||||
* @return number of found {@link DistributionSet}s
|
||||
*/
|
||||
long countByType(JpaDistributionSetType type);
|
||||
|
||||
/**
|
||||
* Counts {@link DistributionSet} with given
|
||||
* {@link DistributionSet#getName()} and
|
||||
* {@link DistributionSet#getVersion()}.
|
||||
*
|
||||
* @param name
|
||||
* to search for
|
||||
* @param version
|
||||
* to search for
|
||||
* @return number of found {@link DistributionSet}s
|
||||
*/
|
||||
long countByNameAndVersion(String name, String version);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* 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 org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link TargetTag} repository.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface DistributionSetTagRepository
|
||||
extends BaseEntityRepository<JpaDistributionSetTag, Long>, JpaSpecificationExecutor<JpaDistributionSetTag> {
|
||||
/**
|
||||
* deletes the {@link DistributionSet} with the given name.
|
||||
*
|
||||
* @param tagName
|
||||
* to be deleted
|
||||
* @return 1 if tag was deleted
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
Long deleteByName(final String tagName);
|
||||
|
||||
/**
|
||||
* find {@link DistributionSetTag} by its name.
|
||||
*
|
||||
* @param tagName
|
||||
* to filter on
|
||||
* @return the {@link DistributionSetTag} if found, otherwise null
|
||||
*/
|
||||
JpaDistributionSetTag findByNameEquals(final String tagName);
|
||||
|
||||
/**
|
||||
* Returns all instances of the type.
|
||||
*
|
||||
* @return all entities
|
||||
*/
|
||||
@Override
|
||||
List<JpaDistributionSetTag> findAll();
|
||||
|
||||
@Override
|
||||
<S extends JpaDistributionSetTag> List<S> save(Iterable<S> entities);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* 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.jpa.model.JpaDistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link PagingAndSortingRepository} for {@link DistributionSetType}.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface DistributionSetTypeRepository
|
||||
extends BaseEntityRepository<JpaDistributionSetType, Long>, JpaSpecificationExecutor<JpaDistributionSetType> {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param pageable
|
||||
* page parameters
|
||||
* @param isDeleted
|
||||
* to <code>true</code> if only soft deleted entries of
|
||||
* <code>false</code> if undeleted ones
|
||||
* @return list of found {@link DistributionSetType}s
|
||||
*/
|
||||
Page<JpaDistributionSetType> findByDeleted(Pageable pageable, boolean isDeleted);
|
||||
|
||||
/**
|
||||
* @param isDeleted
|
||||
* to <code>true</code> if only marked as deleted have to be
|
||||
* count or all undeleted.
|
||||
* @return number of {@link DistributionSetType}s in the repository.
|
||||
*/
|
||||
Long countByDeleted(boolean isDeleted);
|
||||
|
||||
/**
|
||||
* Counts all distribution set type where a specific software module type is
|
||||
* assigned to.
|
||||
*
|
||||
* @param softwareModuleType
|
||||
* the software module type to count the distribution set type
|
||||
* which has this software module type assigned
|
||||
* @return the number of {@link DistributionSetType}s in the repository
|
||||
* assigned to the given software module type
|
||||
*/
|
||||
Long countByElementsSmType(JpaSoftwareModuleType softwareModuleType);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* 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.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Custom repository implementation as standard spring repository fails as of
|
||||
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=415027 .
|
||||
*
|
||||
*/
|
||||
@Service
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public class EclipseLinkTargetInfoRepository implements TargetInfoRepository {
|
||||
|
||||
@Autowired
|
||||
private EntityManager entityManager;
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public void setTargetUpdateStatus(final TargetUpdateStatus status, final List<Long> targets) {
|
||||
final Query query = entityManager.createQuery(
|
||||
"update JpaTargetInfo ti set ti.updateStatus = :status where ti.targetId in :targets and ti.updateStatus != :status");
|
||||
query.setParameter("targets", targets);
|
||||
query.setParameter("status", status);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true)
|
||||
public <S extends JpaTargetInfo> S save(final S entity) {
|
||||
|
||||
if (entity.isNew()) {
|
||||
entityManager.persist(entity);
|
||||
return entity;
|
||||
} else {
|
||||
return entityManager.merge(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true)
|
||||
public void deleteByTargetIdIn(final Collection<Long> targetIDs) {
|
||||
final javax.persistence.Query query = entityManager
|
||||
.createQuery("DELETE FROM JpaTargetInfo ti where ti.targetId IN :target");
|
||||
query.setParameter("target", targetIDs);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 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.jpa.model.JpaExternalArtifactProvider;
|
||||
import org.eclipse.hawkbit.repository.model.ExternalArtifactProvider;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Repository for {@link ExternalArtifactProvider}.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface ExternalArtifactProviderRepository extends BaseEntityRepository<JpaExternalArtifactProvider, Long> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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.jpa.model.JpaExternalArtifact;
|
||||
import org.eclipse.hawkbit.repository.model.ExternalArtifact;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link ExternalArtifact} repository.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface ExternalArtifactRepository extends BaseEntityRepository<JpaExternalArtifact, Long> {
|
||||
|
||||
/**
|
||||
* Searches for external artifact for a base software module.
|
||||
*
|
||||
* @param pageReq
|
||||
* Pageable
|
||||
* @param swId
|
||||
* software module id
|
||||
*
|
||||
* @return Page<ExternalArtifact>
|
||||
*/
|
||||
Page<JpaExternalArtifact> findBySoftwareModuleId(Pageable pageReq, final Long swId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
/**
|
||||
* 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.jpa.model.JpaExternalArtifact;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaExternalArtifactProvider;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaLocalArtifact;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.SoftwareModuleSpecification;
|
||||
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.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 JpaExternalArtifact(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 JpaExternalArtifactProvider(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((JpaSoftwareModule) 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(((JpaLocalArtifact) 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 {}", ((JpaLocalArtifact) existing).getGridFsFileName());
|
||||
artifactRepository.deleteBySha1(((JpaLocalArtifact) existing).getGridFsFileName());
|
||||
} catch (final ArtifactStoreException e) {
|
||||
throw new ArtifactDeleteFailedException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public void deleteLocalArtifact(final Long id) {
|
||||
final JpaLocalArtifact existing = localArtifactRepository.findOne(id);
|
||||
|
||||
if (null == existing) {
|
||||
return;
|
||||
}
|
||||
|
||||
deleteLocalArtifact(existing);
|
||||
|
||||
existing.getSoftwareModule().removeArtifact(existing);
|
||||
softwareModuleRepository.save((JpaSoftwareModule) 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<JpaSoftwareModule> 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(((JpaLocalArtifact) artifact).getGridFsFileName());
|
||||
if (result == null) {
|
||||
throw new GridFSDBFileNotFoundException(((JpaLocalArtifact) artifact).getGridFsFileName());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private LocalArtifact storeArtifactMetadata(final SoftwareModule softwareModule, final String providedFilename,
|
||||
final DbArtifact result, final LocalArtifact existing) {
|
||||
JpaLocalArtifact artifact = (JpaLocalArtifact) existing;
|
||||
if (existing == null) {
|
||||
artifact = new JpaLocalArtifact(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);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public LocalArtifact createLocalArtifact(final InputStream inputStream, final Long moduleId, final String filename,
|
||||
final boolean overrideExisting) {
|
||||
return createLocalArtifact(inputStream, moduleId, filename, null, null, overrideExisting, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,465 @@
|
||||
/**
|
||||
* 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.ArrayList;
|
||||
import java.util.Collection;
|
||||
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.TargetManagement;
|
||||
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.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
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.TenantConfiguration;
|
||||
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.Propagation;
|
||||
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,
|
||||
(JpaSoftwareModule) 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((JpaTarget) target, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SoftwareModule> findSoftwareModulesByDistributionSet(final DistributionSet distributionSet) {
|
||||
return new ArrayList<>(softwareModuleRepository.findByAssignedTo((JpaDistributionSet) 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<JpaTarget> spec = (targetRoot, query, cb) -> cb
|
||||
.equal(targetRoot.get(JpaTarget_.controllerId), controllerId);
|
||||
|
||||
JpaTarget target = targetRepository.findOne(spec);
|
||||
|
||||
if (target == null) {
|
||||
target = new JpaTarget(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 JpaTargetInfo mtargetInfo = (JpaTargetInfo) 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 JpaAction action = (JpaAction) 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((JpaActionStatus) actionStatus);
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public Action addUpdateActionStatus(@NotNull final ActionStatus actionStatus) {
|
||||
final JpaAction action = (JpaAction) 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((JpaActionStatus) actionStatus, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link TargetUpdateStatus} based on given {@link ActionStatus}.
|
||||
*
|
||||
* @param actionStatus
|
||||
* @param action
|
||||
* @return
|
||||
*/
|
||||
private Action handleAddUpdateActionStatus(final JpaActionStatus actionStatus, final JpaAction action) {
|
||||
LOG.debug("addUpdateActionStatus for action {}", action.getId());
|
||||
|
||||
final JpaAction mergedAction = entityManager.merge(action);
|
||||
JpaTarget mergedTarget = (JpaTarget) 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 JpaAction mergedAction, final JpaTarget mergedTarget) {
|
||||
mergedAction.setActive(false);
|
||||
mergedAction.setStatus(Status.ERROR);
|
||||
mergedTarget.setAssignedDistributionSet(null);
|
||||
targetManagement.updateTarget(mergedTarget);
|
||||
}
|
||||
|
||||
private void checkForToManyStatusEntries(final JpaAction 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 JpaTarget target, final JpaAction action) {
|
||||
action.setActive(false);
|
||||
action.setStatus(Status.FINISHED);
|
||||
final JpaTargetInfo targetInfo = (JpaTargetInfo) target.getTargetInfo();
|
||||
final JpaDistributionSet ds = (JpaDistributionSet) entityManager.merge(action.getDistributionSet());
|
||||
targetInfo.setInstalledDistributionSet(ds);
|
||||
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 JpaTarget target = targetRepository.findByControllerId(controllerId);
|
||||
|
||||
if (target == null) {
|
||||
throw new EntityNotFoundException(controllerId);
|
||||
}
|
||||
|
||||
final JpaTargetInfo targetInfo = (JpaTargetInfo) target.getTargetInfo();
|
||||
targetInfo.getControllerAttributes().putAll(data);
|
||||
|
||||
if (targetInfo.getControllerAttributes().size() > securityProperties.getDos()
|
||||
.getMaxAttributeEntriesPerTarget()) {
|
||||
LOG_DOS.info("Target tries to insert more than the allowed number of entries ({}). DOS attack anticipated!",
|
||||
securityProperties.getDos().getMaxAttributeEntriesPerTarget());
|
||||
throw new ToManyAttributeEntriesException(
|
||||
String.valueOf(securityProperties.getDos().getMaxAttributeEntriesPerTarget()));
|
||||
}
|
||||
|
||||
targetInfo.setLastTargetQuery(System.currentTimeMillis());
|
||||
targetInfo.setRequestControllerAttributes(false);
|
||||
return targetRepository.save(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public Action registerRetrieved(final Action action, final String message) {
|
||||
return handleRegisterRetrieved((JpaAction) 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 JpaAction 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<JpaActionStatus> actionStatusRoot = queryActionStatus.from(JpaActionStatus.class);
|
||||
final CriteriaQuery<Object[]> query = queryActionStatus
|
||||
.multiselect(actionStatusRoot.get(JpaActionStatus_.id), actionStatusRoot.get(JpaActionStatus_.status))
|
||||
.where(cb.equal(actionStatusRoot.get(JpaActionStatus_.action), action))
|
||||
.orderBy(cb.desc(actionStatusRoot.get(JpaActionStatus_.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() || !Status.RETRIEVED.equals(resultList.get(0)[1])) {
|
||||
// document that the status has been retrieved
|
||||
actionStatusRepository
|
||||
.save(new JpaActionStatus(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 JpaAction 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((JpaActionStatus) statusMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSecurityTokenByControllerId(final String controllerId) {
|
||||
final Target target = targetRepository.findByControllerId(controllerId);
|
||||
return target != null ? target.getSecurityToken() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public TargetInfo updateLastTargetQuery(final TargetInfo target, final URI address) {
|
||||
return updateTargetStatus(target, null, System.currentTimeMillis(), address);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public ActionStatus generateActionStatus() {
|
||||
return new JpaActionStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public ActionStatus generateActionStatus(final Action action, final Status status, final Long occurredAt,
|
||||
final String message) {
|
||||
return new JpaActionStatus((JpaAction) action, status, occurredAt, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public ActionStatus generateActionStatus(final Action action, final Status status, final Long occurredAt,
|
||||
final Collection<String> messages) {
|
||||
|
||||
final ActionStatus result = new JpaActionStatus((JpaAction) action, status, occurredAt, null);
|
||||
messages.forEach(result::addMessage);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public ActionStatus generateActionStatus(final Action action, final Status status, final Long occurredAt) {
|
||||
return new JpaActionStatus(action, status, occurredAt);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,700 @@
|
||||
/**
|
||||
* 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.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
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.ActionFields;
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetWithActionType;
|
||||
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.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaActionWithStatusCount;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.ActionWithStatusCount;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
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.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.rsql.RSQLUtility;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
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.PageImpl;
|
||||
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.Propagation;
|
||||
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 {@link 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;
|
||||
|
||||
@Autowired
|
||||
private SystemSecurityContext systemSecurityContext;
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Modifying
|
||||
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
|
||||
public DistributionSetAssignmentResult assignDistributionSet(final DistributionSet pset,
|
||||
final List<Target> targets) {
|
||||
|
||||
return assignDistributionSetByTargetId((JpaDistributionSet) 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 Collection<TargetWithActionType> targets) {
|
||||
final JpaDistributionSet 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 Collection<TargetWithActionType> targets, final Rollout rollout, final RolloutGroup rolloutGroup) {
|
||||
final JpaDistributionSet 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, (JpaRollout) rollout, (JpaRolloutGroup) 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 JpaDistributionSet set,
|
||||
final Collection<TargetWithActionType> targetsWithActionType, final JpaRollout rollout,
|
||||
final JpaRolloutGroup 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<JpaTarget> 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, JpaAction> 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 JpaActionStatus actionStatus = new JpaActionStatus();
|
||||
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<JpaSoftwareModule> 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<JpaTarget> targets, final Set<Long> targetIdsCancellList,
|
||||
final Map<String, JpaAction> targetIdsToActions, final List<JpaSoftwareModule> softwareModules) {
|
||||
targets.stream().filter(t -> !!!targetIdsCancellList.contains(t.getId()))
|
||||
.forEach(t -> assignDistributionSetEvent(t, targetIdsToActions.get(t.getControllerId()).getId(),
|
||||
softwareModules));
|
||||
}
|
||||
|
||||
private static JpaAction createTargetAction(final Map<String, TargetWithActionType> targetsWithActionMap,
|
||||
final JpaTarget target, final JpaDistributionSet set, final JpaRollout rollout,
|
||||
final JpaRolloutGroup rolloutGroup) {
|
||||
final JpaAction actionForTarget = new JpaAction();
|
||||
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 JpaTarget target, final Long actionId,
|
||||
final List<JpaSoftwareModule> modules) {
|
||||
((JpaTargetInfo) target.getTargetInfo()).setUpdateStatus(TargetUpdateStatus.PENDING);
|
||||
final String targetSecurityToken = systemSecurityContext.runAsSystem(() -> target.getSecurityToken());
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
final Collection<SoftwareModule> softwareModules = (Collection) modules;
|
||||
afterCommit.afterCommit(() -> {
|
||||
eventBus.post(new TargetInfoUpdateEvent(target.getTargetInfo()));
|
||||
eventBus.post(new TargetAssignDistributionSetEvent(target.getOptLockRevision(), target.getTenant(),
|
||||
target.getControllerId(), actionId, softwareModules, target.getTargetInfo().getAddress(),
|
||||
targetSecurityToken));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<JpaAction> activeActions = actionRepository
|
||||
.findByActiveAndTargetIdInAndActionStatusNotEqualToAndDistributionSetRequiredMigrationStep(targetsIds,
|
||||
Action.Status.CANCELING);
|
||||
activeActions.forEach(action -> {
|
||||
action.setStatus(Status.CANCELING);
|
||||
// document that the status has been retrieved
|
||||
|
||||
actionStatusRepository.save(new JpaActionStatus(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 JpaDistributionSet 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 JpaAction myAction = (JpaAction) 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 JpaActionStatus(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 JpaAction mergedAction = (JpaAction) 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 JpaActionStatus(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 Collection<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 JpaAction action = new JpaAction();
|
||||
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 JpaAction mergedAction = (JpaAction) entityManager.merge(action);
|
||||
final JpaTarget mergedTarget = (JpaTarget) 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 JpaActionStatus actionStatus = new JpaActionStatus();
|
||||
actionStatus.setAction(action);
|
||||
actionStatus.setOccurredAt(action.getCreatedAt());
|
||||
actionStatus.setStatus(Status.RUNNING);
|
||||
actionStatusRepository.save(actionStatus);
|
||||
|
||||
mergedTarget.setAssignedDistributionSet(action.getDistributionSet());
|
||||
final JpaTargetInfo targetInfo = (JpaTargetInfo) 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<JpaSoftwareModule> softwareModules = softwareModuleRepository
|
||||
.findByAssignedTo((JpaDistributionSet) action.getDistributionSet());
|
||||
// send distribution set assignment event
|
||||
|
||||
assignDistributionSetEvent((JpaTarget) 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, (JpaTarget) target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> findActionsByTarget(final Target target) {
|
||||
return actionRepository.findByTarget((JpaTarget) target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ActionWithStatusCount> findActionsWithStatusCountByTargetOrderByIdDesc(final Target target) {
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
final CriteriaQuery<JpaActionWithStatusCount> query = cb.createQuery(JpaActionWithStatusCount.class);
|
||||
final Root<JpaAction> actionRoot = query.from(JpaAction.class);
|
||||
final ListJoin<JpaAction, JpaActionStatus> actionStatusJoin = actionRoot.join(JpaAction_.actionStatus,
|
||||
JoinType.LEFT);
|
||||
final Join<JpaAction, JpaDistributionSet> actionDsJoin = actionRoot.join(JpaAction_.distributionSet);
|
||||
final Join<JpaAction, JpaRollout> actionRolloutJoin = actionRoot.join(JpaAction_.rollout, JoinType.LEFT);
|
||||
|
||||
final CriteriaQuery<JpaActionWithStatusCount> multiselect = query.distinct(true).multiselect(
|
||||
actionRoot.get(JpaAction_.id), actionRoot.get(JpaAction_.actionType), actionRoot.get(JpaAction_.active),
|
||||
actionRoot.get(JpaAction_.forcedTime), actionRoot.get(JpaAction_.status),
|
||||
actionRoot.get(JpaAction_.createdAt), actionRoot.get(JpaAction_.lastModifiedAt),
|
||||
actionDsJoin.get(JpaDistributionSet_.id), actionDsJoin.get(JpaDistributionSet_.name),
|
||||
actionDsJoin.get(JpaDistributionSet_.version), cb.count(actionStatusJoin),
|
||||
actionRolloutJoin.get(JpaRollout_.name));
|
||||
multiselect.where(cb.equal(actionRoot.get(JpaAction_.target), target));
|
||||
multiselect.orderBy(cb.desc(actionRoot.get(JpaAction_.id)));
|
||||
multiselect.groupBy(actionRoot.get(JpaAction_.id));
|
||||
return new ArrayList<>(entityManager.createQuery(multiselect).getResultList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Action> findActionsByTarget(final String rsqlParam, final Target target, final Pageable pageable) {
|
||||
final Specification<JpaAction> specification = RSQLUtility.parse(rsqlParam, ActionFields.class);
|
||||
|
||||
return convertAcPage(actionRepository.findAll((Specification<JpaAction>) (root, query, cb) -> cb
|
||||
.and(specification.toPredicate(root, query, cb), cb.equal(root.get(JpaAction_.target), target)),
|
||||
pageable), pageable);
|
||||
}
|
||||
|
||||
private static Page<Action> convertAcPage(final Page<JpaAction> findAll, final Pageable pageable) {
|
||||
return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Action> findActionsByTarget(final Target foundTarget, final Pageable pageable) {
|
||||
return actionRepository.findByTarget(pageable, (JpaTarget) foundTarget);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Action> findActiveActionsByTarget(final Pageable pageable, final Target target) {
|
||||
return actionRepository.findByActiveAndTarget(pageable, (JpaTarget) target, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> findActiveActionsByTarget(final Target target) {
|
||||
return actionRepository.findByActiveAndTarget((JpaTarget) target, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> findInActiveActionsByTarget(final Target target) {
|
||||
return actionRepository.findByActiveAndTarget((JpaTarget) target, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Action> findInActiveActionsByTarget(final Pageable pageable, final Target target) {
|
||||
return actionRepository.findByActiveAndTarget(pageable, (JpaTarget) target, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countActionsByTarget(final Target target) {
|
||||
return actionRepository.countByTarget((JpaTarget) target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countActionsByTarget(final String rsqlParam, final Target target) {
|
||||
final Specification<JpaAction> spec = RSQLUtility.parse(rsqlParam, ActionFields.class);
|
||||
|
||||
return actionRepository.count((root, query, cb) -> cb.and(spec.toPredicate(root, query, cb),
|
||||
cb.equal(root.get(JpaAction_.target), target)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public Action forceTargetAction(final Long actionId) {
|
||||
final JpaAction 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) {
|
||||
return actionStatusRepository.findByAction(pageReq, (JpaAction) action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<ActionStatus> findActionStatusByActionWithMessages(final Pageable pageReq, final Action action) {
|
||||
return actionStatusRepository.getByAction(pageReq, (JpaAction) action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> findActionsByRolloutGroupParentAndStatus(final Rollout rollout,
|
||||
final RolloutGroup rolloutGroupParent, final Action.Status actionStatus) {
|
||||
return actionRepository.findByRolloutAndRolloutGroupParentAndStatus((JpaRollout) rollout,
|
||||
(JpaRolloutGroup) rolloutGroupParent, actionStatus);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> findActionsByRolloutAndStatus(final Rollout rollout, final Action.Status actionStatus) {
|
||||
return actionRepository.findByRolloutAndStatus((JpaRollout) rollout, actionStatus);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public Action generateAction() {
|
||||
return new JpaAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<ActionStatus> findActionStatusAll(final Pageable pageable) {
|
||||
return convertAcSPage(actionStatusRepository.findAll(pageable), pageable);
|
||||
}
|
||||
|
||||
private static Page<ActionStatus> convertAcSPage(final Page<JpaActionStatus> findAll, final Pageable pageable) {
|
||||
return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,785 @@
|
||||
/**
|
||||
* 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 java.util.stream.Collectors;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import org.eclipse.hawkbit.eventbus.event.DistributionSetTagAssigmentResultEvent;
|
||||
import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetFields;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetFilter;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetFilter.DistributionSetFilterBuilder;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetMetadataFields;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetTypeFields;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.repository.TagManagement;
|
||||
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.jpa.model.DsMetadataCompositeKey;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetSpecification;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetTypeSpecification;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
|
||||
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.DistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetTagAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.rsql.RSQLUtility;
|
||||
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.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link DistributionSetManagement}.
|
||||
*
|
||||
*/
|
||||
@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 List<JpaDistributionSet> sets = findDistributionSetListWithDetails(dsIds);
|
||||
final DistributionSetTag myTag = tagManagement.findDistributionSetTag(tagName);
|
||||
|
||||
DistributionSetTagAssignmentResult result;
|
||||
final List<JpaDistributionSet> toBeChangedDSs = new ArrayList<>();
|
||||
for (final JpaDistributionSet set : sets) {
|
||||
if (set.getTags().add(myTag)) {
|
||||
toBeChangedDSs.add(set);
|
||||
}
|
||||
}
|
||||
|
||||
// un-assignment case
|
||||
if (toBeChangedDSs.isEmpty()) {
|
||||
for (final JpaDistributionSet set : sets) {
|
||||
if (set.getTags().remove(myTag)) {
|
||||
toBeChangedDSs.add(set);
|
||||
}
|
||||
}
|
||||
result = new DistributionSetTagAssignmentResult(dsIds.size() - toBeChangedDSs.size(), 0,
|
||||
toBeChangedDSs.size(), Collections.emptyList(),
|
||||
new ArrayList<>(distributionSetRepository.save(toBeChangedDSs)), myTag);
|
||||
} else {
|
||||
result = new DistributionSetTagAssignmentResult(dsIds.size() - toBeChangedDSs.size(), toBeChangedDSs.size(),
|
||||
0, new ArrayList<>(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;
|
||||
}
|
||||
|
||||
private List<JpaDistributionSet> 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((JpaDistributionSet) ds, persisted.getModules());
|
||||
return distributionSetRepository.save((JpaDistributionSet) 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((JpaDistributionSet) 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());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
final Collection<JpaDistributionSet> toSave = (Collection) distributionSets;
|
||||
|
||||
return new ArrayList<>(distributionSetRepository.save(toSave));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public DistributionSet assignSoftwareModules(final DistributionSet ds, final Set<SoftwareModule> softwareModules) {
|
||||
checkDistributionSetSoftwareModulesIsAllowedToModify((JpaDistributionSet) ds, softwareModules);
|
||||
for (final SoftwareModule softwareModule : softwareModules) {
|
||||
ds.addModule(softwareModule);
|
||||
}
|
||||
return distributionSetRepository.save((JpaDistributionSet) 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((JpaDistributionSet) ds, softwareModules);
|
||||
return distributionSetRepository.save((JpaDistributionSet) ds);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public DistributionSetType updateDistributionSetType(final DistributionSetType dsType) {
|
||||
checkNotNull(dsType.getId());
|
||||
|
||||
final JpaDistributionSetType 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((JpaDistributionSetType) dsType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<DistributionSetType> findDistributionSetTypesAll(final String rsqlParam, final Pageable pageable) {
|
||||
final Specification<JpaDistributionSetType> spec = RSQLUtility.parse(rsqlParam,
|
||||
DistributionSetTypeFields.class);
|
||||
|
||||
return convertDsTPage(distributionSetTypeRepository.findAll(spec, pageable));
|
||||
}
|
||||
|
||||
private static Page<DistributionSetType> convertDsTPage(final Page<JpaDistributionSetType> findAll) {
|
||||
return new PageImpl<>(new ArrayList<>(findAll.getContent()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<DistributionSetType> findDistributionSetTypesAll(final Pageable pageable) {
|
||||
return convertDsTPage(distributionSetTypeRepository.findByDeleted(pageable, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<DistributionSet> findDistributionSetsByFilters(final Pageable pageable,
|
||||
final DistributionSetFilter distributionSetFilter) {
|
||||
final List<Specification<JpaDistributionSet>> specList = buildDistributionSetSpecifications(
|
||||
distributionSetFilter);
|
||||
return convertDsPage(findByCriteriaAPI(pageable, specList), pageable);
|
||||
}
|
||||
|
||||
private static Page<DistributionSet> convertDsPage(final Page<JpaDistributionSet> findAll,
|
||||
final Pageable pageable) {
|
||||
return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @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<JpaDistributionSet>> specList = buildDistributionSetSpecifications(
|
||||
distributionSetFilter);
|
||||
if (specList == null || specList.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return distributionSetRepository.findOne(SpecificationsBuilder.combineWithAnd(specList));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<DistributionSet> findDistributionSetsByDeletedAndOrCompleted(final Pageable pageReq,
|
||||
final Boolean deleted, final Boolean complete) {
|
||||
final List<Specification<JpaDistributionSet>> specList = new ArrayList<>();
|
||||
|
||||
if (deleted != null) {
|
||||
final Specification<JpaDistributionSet> spec = DistributionSetSpecification.isDeleted(deleted);
|
||||
specList.add(spec);
|
||||
}
|
||||
|
||||
if (complete != null) {
|
||||
final Specification<JpaDistributionSet> spec = DistributionSetSpecification.isCompleted(complete);
|
||||
specList.add(spec);
|
||||
}
|
||||
|
||||
return convertDsPage(findByCriteriaAPI(pageReq, specList), pageReq);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<DistributionSet> findDistributionSetsAll(final String rsqlParam, final Pageable pageReq,
|
||||
final Boolean deleted) {
|
||||
|
||||
final Specification<JpaDistributionSet> spec = RSQLUtility.parse(rsqlParam, DistributionSetFields.class);
|
||||
|
||||
final List<Specification<JpaDistributionSet>> specList = new ArrayList<>();
|
||||
if (deleted != null) {
|
||||
specList.add(DistributionSetSpecification.isDeleted(deleted));
|
||||
}
|
||||
specList.add(spec);
|
||||
return convertDsPage(findByCriteriaAPI(pageReq, specList), pageReq);
|
||||
}
|
||||
|
||||
@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<JpaDistributionSet> spec = DistributionSetSpecification
|
||||
.equalsNameAndVersionIgnoreCase(distributionName, version);
|
||||
return distributionSetRepository.findOne(spec);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DistributionSet> findDistributionSetsAll(final Collection<Long> dist) {
|
||||
return new ArrayList<>(distributionSetRepository.findAll(DistributionSetSpecification.byIds(dist)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countDistributionSetsAll() {
|
||||
|
||||
final List<Specification<JpaDistributionSet>> specList = new LinkedList<>();
|
||||
|
||||
final Specification<JpaDistributionSet> 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((JpaDistributionSetType) type);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public void deleteDistributionSetType(final DistributionSetType ty) {
|
||||
final JpaDistributionSetType type = (JpaDistributionSetType) ty;
|
||||
|
||||
if (distributionSetRepository.countByType(type) > 0) {
|
||||
final JpaDistributionSetType 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 md) {
|
||||
final JpaDistributionSetMetadata metadata = (JpaDistributionSetMetadata) md;
|
||||
|
||||
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((JpaDistributionSet) metadata.getDistributionSet()).setLastModifiedAt(0L);
|
||||
return distributionSetMetadataRepository.save(metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
public List<DistributionSetMetadata> createDistributionSetMetadata(final Collection<DistributionSetMetadata> md) {
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
final Collection<JpaDistributionSetMetadata> metadata = (Collection) md;
|
||||
|
||||
for (final JpaDistributionSetMetadata distributionSetMetadata : metadata) {
|
||||
checkAndThrowAlreadyIfDistributionSetMetadataExists(distributionSetMetadata.getId());
|
||||
}
|
||||
metadata.forEach(m -> entityManager.merge((JpaDistributionSet) m.getDistributionSet()).setLastModifiedAt(0L));
|
||||
|
||||
return new ArrayList<DistributionSetMetadata>(
|
||||
(Collection<? extends DistributionSetMetadata>) distributionSetMetadataRepository.save(metadata));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
public DistributionSetMetadata updateDistributionSetMetadata(final DistributionSetMetadata md) {
|
||||
final JpaDistributionSetMetadata metadata = (JpaDistributionSetMetadata) md;
|
||||
|
||||
// check if exists otherwise throw entity not found exception
|
||||
findOne(metadata.getDistributionSet(), metadata.getKey());
|
||||
// touch it to update the lock revision because we are modifying the
|
||||
// DS indirectly
|
||||
entityManager.merge((JpaDistributionSet) metadata.getDistributionSet()).setLastModifiedAt(0L);
|
||||
return distributionSetMetadataRepository.save(metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
public void deleteDistributionSetMetadata(final DistributionSet distributionSet, final String key) {
|
||||
distributionSetMetadataRepository.delete(new DsMetadataCompositeKey(distributionSet, key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<DistributionSetMetadata> findDistributionSetMetadataByDistributionSetId(final Long distributionSetId,
|
||||
final Pageable pageable) {
|
||||
|
||||
return convertMdPage(distributionSetMetadataRepository
|
||||
.findAll((Specification<JpaDistributionSetMetadata>) (root, query, cb) -> cb.equal(
|
||||
root.get(JpaDistributionSetMetadata_.distributionSet).get(JpaDistributionSet_.id),
|
||||
distributionSetId), pageable),
|
||||
pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<DistributionSetMetadata> findDistributionSetMetadataByDistributionSetId(final Long distributionSetId,
|
||||
final String rsqlParam, final Pageable pageable) {
|
||||
|
||||
final Specification<JpaDistributionSetMetadata> spec = RSQLUtility.parse(rsqlParam,
|
||||
DistributionSetMetadataFields.class);
|
||||
|
||||
return convertMdPage(
|
||||
distributionSetMetadataRepository
|
||||
.findAll((Specification<JpaDistributionSetMetadata>) (root, query, cb) -> cb.and(
|
||||
cb.equal(root.get(JpaDistributionSetMetadata_.distributionSet)
|
||||
.get(JpaDistributionSet_.id), distributionSetId),
|
||||
spec.toPredicate(root, query, cb)), pageable),
|
||||
pageable);
|
||||
}
|
||||
|
||||
private static Page<DistributionSetMetadata> convertMdPage(final Page<JpaDistributionSetMetadata> findAll,
|
||||
final Pageable pageable) {
|
||||
return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSetMetadata findOne(final DistributionSet distributionSet, final String key) {
|
||||
final DistributionSetMetadata findOne = distributionSetMetadataRepository
|
||||
.findOne(new DsMetadataCompositeKey(distributionSet, key));
|
||||
if (findOne == null) {
|
||||
throw new EntityNotFoundException("Metadata with key '" + key + "' does not exist");
|
||||
}
|
||||
return findOne;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSet findDistributionSetByAction(final Action action) {
|
||||
return distributionSetRepository.findByAction((JpaAction) action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDistributionSetInUse(final DistributionSet distributionSet) {
|
||||
return actionRepository.countByDistributionSet((JpaDistributionSet) distributionSet) > 0;
|
||||
}
|
||||
|
||||
private static List<Specification<JpaDistributionSet>> buildDistributionSetSpecifications(
|
||||
final DistributionSetFilter distributionSetFilter) {
|
||||
final List<Specification<JpaDistributionSet>> specList = new ArrayList<>();
|
||||
|
||||
Specification<JpaDistributionSet> 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 JpaDistributionSet 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<JpaDistributionSet> findByCriteriaAPI(final Pageable pageable,
|
||||
final List<Specification<JpaDistributionSet>> 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<JpaDistributionSet> allDs = findDistributionSetListWithDetails(dsIds);
|
||||
|
||||
allDs.forEach(ds -> ds.getTags().add(tag));
|
||||
|
||||
final List<DistributionSet> save = new ArrayList<>(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) {
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
final Collection<JpaDistributionSet> distributionSets = (Collection) tag.getAssignedToDistributionSet();
|
||||
|
||||
return new ArrayList<>(unAssignTag(distributionSets, tag));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public DistributionSet unAssignTag(final Long dsId, final DistributionSetTag distributionSetTag) {
|
||||
final List<JpaDistributionSet> allDs = findDistributionSetListWithDetails(Arrays.asList(dsId));
|
||||
final List<JpaDistributionSet> unAssignTag = unAssignTag(allDs, distributionSetTag);
|
||||
return unAssignTag.isEmpty() ? null : unAssignTag.get(0);
|
||||
}
|
||||
|
||||
private List<JpaDistributionSet> unAssignTag(final Collection<JpaDistributionSet> distributionSets,
|
||||
final DistributionSetTag tag) {
|
||||
distributionSets.forEach(ds -> ds.getTags().remove(tag));
|
||||
return distributionSetRepository.save(distributionSets);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public List<DistributionSetType> createDistributionSetTypes(final Collection<DistributionSetType> types) {
|
||||
return types.stream().map(this::createDistributionSetType).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public void deleteDistributionSet(final DistributionSet set) {
|
||||
deleteDistributionSet(set.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public DistributionSetTagAssignmentResult toggleTagAssignment(final Collection<DistributionSet> sets,
|
||||
final DistributionSetTag tag) {
|
||||
return toggleTagAssignment(sets.stream().map(ds -> ds.getId()).collect(Collectors.toList()), tag.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public DistributionSetType generateDistributionSetType() {
|
||||
return new JpaDistributionSetType();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public DistributionSet generateDistributionSet() {
|
||||
return new JpaDistributionSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public DistributionSetMetadata generateDistributionSetMetadata() {
|
||||
return new JpaDistributionSetMetadata();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public DistributionSetMetadata generateDistributionSetMetadata(final DistributionSet distributionSet,
|
||||
final String key, final String value) {
|
||||
return new JpaDistributionSetMetadata(key, distributionSet, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public DistributionSetType generateDistributionSetType(final String key, final String name,
|
||||
final String description) {
|
||||
return new JpaDistributionSetType(key, name, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public DistributionSet generateDistributionSet(final String name, final String version, final String description,
|
||||
final DistributionSetType type, final Collection<SoftwareModule> moduleList) {
|
||||
return new JpaDistributionSet(name, version, description, type, moduleList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countDistributionSetsByType(final DistributionSetType type) {
|
||||
return distributionSetRepository.countByType((JpaDistributionSetType) type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,436 @@
|
||||
/**
|
||||
* 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.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.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;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link ReportManagement}.
|
||||
*
|
||||
*/
|
||||
@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<JpaTarget> targetRoot = query.from(JpaTarget.class);
|
||||
final Join<JpaTarget, JpaTargetInfo> targetInfo = targetRoot.join(JpaTarget_.targetInfo);
|
||||
final Expression<Long> countColumn = cb.count(targetInfo.get(JpaTargetInfo_.targetId));
|
||||
final CriteriaQuery<Object[]> multiselect = query
|
||||
.multiselect(targetInfo.get(JpaTargetInfo_.updateStatus), countColumn)
|
||||
.groupBy(targetInfo.get(JpaTargetInfo_.updateStatus))
|
||||
.orderBy(cb.desc(targetInfo.get(JpaTargetInfo_.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<JpaDistributionSet> rootTopX = queryTopX.from(JpaDistributionSet.class);
|
||||
final ListJoin<JpaDistributionSet, JpaTarget> joinTopX = rootTopX.join(JpaDistributionSet_.assignedToTargets,
|
||||
JoinType.LEFT);
|
||||
final Expression<Long> countColumn = cbTopX.count(joinTopX);
|
||||
// top x usage query
|
||||
final CriteriaQuery<Object[]> groupBy = queryTopX
|
||||
.multiselect(rootTopX.get(JpaDistributionSet_.name), rootTopX.get(JpaDistributionSet_.version),
|
||||
countColumn)
|
||||
.where(cbTopX.equal(rootTopX.get(JpaDistributionSet_.deleted), false))
|
||||
.groupBy(rootTopX.get(JpaDistributionSet_.name), rootTopX.get(JpaDistributionSet_.version))
|
||||
.orderBy(cbTopX.desc(countColumn), cbTopX.asc(rootTopX.get(JpaDistributionSet_.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<JpaDistributionSet> rootTopX = queryTopX.from(JpaDistributionSet.class);
|
||||
final ListJoin<JpaDistributionSet, JpaTargetInfo> joinTopX = rootTopX
|
||||
.join(JpaDistributionSet_.installedAtTargets, JoinType.LEFT);
|
||||
final Expression<Long> countColumn = cbTopX.count(joinTopX);
|
||||
// top x usage query
|
||||
final CriteriaQuery<Object[]> groupBy = queryTopX
|
||||
.multiselect(rootTopX.get(JpaDistributionSet_.name), rootTopX.get(JpaDistributionSet_.version),
|
||||
countColumn)
|
||||
.where(cbTopX.equal(rootTopX.get(JpaDistributionSet_.deleted), false))
|
||||
.groupBy(rootTopX.get(JpaDistributionSet_.name), rootTopX.get(JpaDistributionSet_.version))
|
||||
.orderBy(cbTopX.desc(countColumn), cbTopX.asc(rootTopX.get(JpaDistributionSet_.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));
|
||||
@SuppressWarnings("unchecked")
|
||||
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<JpaTarget> countSelectRoot = countSelect.from(JpaTarget.class);
|
||||
final Join<JpaTarget, JpaTargetInfo> targetInfoJoin = countSelectRoot.join(JpaTarget_.targetInfo);
|
||||
countSelect.select(cb.count(countSelectRoot));
|
||||
if (start != null && end != null) {
|
||||
countSelect.where(cb.between(targetInfoJoin.get(JpaTargetInfo_.lastTargetQuery), start, end));
|
||||
} else if (from == null && to != null) {
|
||||
countSelect.where(cb.lessThanOrEqualTo(targetInfoJoin.get(JpaTargetInfo_.lastTargetQuery), end));
|
||||
} else {
|
||||
countSelect.where(cb.isNull(targetInfoJoin.get(JpaTargetInfo_.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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
/**
|
||||
* 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.ArrayList;
|
||||
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.RolloutGroupFields;
|
||||
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetFields;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup_;
|
||||
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.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetWithActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
|
||||
import org.eclipse.hawkbit.repository.rsql.RSQLUtility;
|
||||
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.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;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link RolloutGroupManagement}.
|
||||
*/
|
||||
@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 pageable) {
|
||||
return convertPage(rolloutGroupRepository.findByRolloutId(rolloutId, pageable), pageable);
|
||||
}
|
||||
|
||||
private static Page<RolloutGroup> convertPage(final Page<JpaRolloutGroup> findAll, final Pageable pageable) {
|
||||
return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements());
|
||||
}
|
||||
|
||||
private static Page<Target> convertTPage(final Page<JpaTarget> findAll, final Pageable pageable) {
|
||||
return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<RolloutGroup> findRolloutGroupsAll(final Rollout rollout, final String rsqlParam,
|
||||
final Pageable pageable) {
|
||||
|
||||
final Specification<JpaRolloutGroup> specification = RSQLUtility.parse(rsqlParam, RolloutGroupFields.class);
|
||||
|
||||
return convertPage(
|
||||
rolloutGroupRepository
|
||||
.findAll(
|
||||
(root, query, criteriaBuilder) -> criteriaBuilder.and(
|
||||
criteriaBuilder.equal(root.get(JpaRolloutGroup_.rollout), rollout),
|
||||
specification.toPredicate(root, query, criteriaBuilder)),
|
||||
pageable),
|
||||
pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<RolloutGroup> findAllRolloutGroupsWithDetailedStatus(final Long rolloutId, final Pageable pageable) {
|
||||
final Page<JpaRolloutGroup> rolloutGroups = rolloutGroupRepository.findByRolloutId(rolloutId, pageable);
|
||||
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 convertPage(rolloutGroups, pageable);
|
||||
}
|
||||
|
||||
@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 String rsqlParam,
|
||||
final Pageable pageable) {
|
||||
|
||||
final Specification<JpaTarget> rsqlSpecification = RSQLUtility.parse(rsqlParam, TargetFields.class);
|
||||
|
||||
return convertTPage(targetRepository.findAll((root, query, criteriaBuilder) -> {
|
||||
final ListJoin<JpaTarget, RolloutTargetGroup> rolloutTargetJoin = root.join(JpaTarget_.rolloutTargetGroup);
|
||||
return criteriaBuilder.and(rsqlSpecification.toPredicate(root, query, criteriaBuilder),
|
||||
criteriaBuilder.equal(rolloutTargetJoin.get(RolloutTargetGroup_.rolloutGroup), rolloutGroup));
|
||||
}, pageable), pageable);
|
||||
}
|
||||
|
||||
@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((JpaRolloutGroup) 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, JpaTarget> targetJoin = targetRoot.join(RolloutTargetGroup_.target);
|
||||
final ListJoin<RolloutTargetGroup, JpaAction> 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(JpaAction_.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);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public RolloutGroup generateRolloutGroup() {
|
||||
return new JpaRolloutGroup();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,675 @@
|
||||
/**
|
||||
* 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.ArrayList;
|
||||
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.OffsetBasedPageRequest;
|
||||
import org.eclipse.hawkbit.repository.RolloutFields;
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetWithActionType;
|
||||
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup;
|
||||
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.RolloutGroupErrorCondition;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
|
||||
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.repository.rsql.RSQLUtility;
|
||||
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.PageImpl;
|
||||
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.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;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link RolloutManagement}.
|
||||
*/
|
||||
@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 pageable) {
|
||||
return convertPage(rolloutRepository.findAll(pageable), pageable);
|
||||
}
|
||||
|
||||
private static Page<Rollout> convertPage(final Page<JpaRollout> findAll, final Pageable pageable) {
|
||||
return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements());
|
||||
}
|
||||
|
||||
private static Slice<Rollout> convertPage(final Slice<JpaRollout> findAll, final Pageable pageable) {
|
||||
return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Rollout> findAllWithDetailedStatusByPredicate(final String rsqlParam, final Pageable pageable) {
|
||||
|
||||
final Specification<JpaRollout> specification = RSQLUtility.parse(rsqlParam, RolloutFields.class);
|
||||
|
||||
final Page<JpaRollout> findAll = rolloutRepository.findAll(specification, pageable);
|
||||
setRolloutStatusDetails(findAll);
|
||||
return convertPage(findAll, pageable);
|
||||
}
|
||||
|
||||
@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 JpaRollout savedRollout = createRollout((JpaRollout) 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 JpaRollout savedRollout = createRollout((JpaRollout) 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 JpaRollout createRollout(final JpaRollout 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 JpaRollout 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 JpaRollout 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 JpaRolloutGroup group = new JpaRolloutGroup();
|
||||
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 JpaRolloutGroup 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 JpaRollout mergedRollout = entityManager.merge((JpaRollout) rollout);
|
||||
checkIfRolloutCanStarted(rollout, mergedRollout);
|
||||
return doStartRollout(mergedRollout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
public Rollout startRolloutAsync(final Rollout rollout) {
|
||||
final JpaRollout mergedRollout = entityManager.merge((JpaRollout) rollout);
|
||||
checkIfRolloutCanStarted(rollout, mergedRollout);
|
||||
mergedRollout.setStatus(RolloutStatus.STARTING);
|
||||
final JpaRollout 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 JpaRollout rollout) {
|
||||
final DistributionSet distributionSet = rollout.getDistributionSet();
|
||||
final ActionType actionType = rollout.getActionType();
|
||||
final long forceTime = rollout.getForcedTime();
|
||||
final List<JpaRolloutGroup> rolloutGroups = rolloutGroupRepository.findByRolloutOrderByIdAsc(rollout);
|
||||
for (int iGroup = 0; iGroup < rolloutGroups.size(); iGroup++) {
|
||||
final JpaRolloutGroup 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 JpaRollout mergedRollout = entityManager.merge((JpaRollout) 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 JpaRollout mergedRollout = entityManager.merge((JpaRollout) 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<JpaRollout> rolloutsToCheck = rolloutRepository.findByLastCheckAndStatus(lastCheck,
|
||||
RolloutStatus.RUNNING);
|
||||
LOGGER.info("Found {} running rollouts to check", rolloutsToCheck.size());
|
||||
|
||||
for (final JpaRollout rollout : rolloutsToCheck) {
|
||||
LOGGER.debug("Checking rollout {}", rollout);
|
||||
final List<JpaRolloutGroup> 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<JpaRollout> 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<JpaRollout> 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 JpaRollout rollout, final List<JpaRolloutGroup> rolloutGroups) {
|
||||
for (final JpaRolloutGroup 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 JpaRollout rollout) {
|
||||
final List<JpaRolloutGroup> 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 JpaRollout rollout) {
|
||||
final Long groupsActiveLeft = rolloutGroupRepository.countByRolloutAndStatusOrStatus(rollout,
|
||||
RolloutGroupStatus.RUNNING, RolloutGroupStatus.SCHEDULED);
|
||||
return groupsActiveLeft == 0;
|
||||
}
|
||||
|
||||
private boolean isRolloutGroupComplete(final JpaRollout rollout, final JpaRolloutGroup 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<JpaRollout> likeNameOrDescription(final String searchText) {
|
||||
return (rolloutRoot, query, criteriaBuilder) -> {
|
||||
final String searchTextToLower = searchText.toLowerCase();
|
||||
return criteriaBuilder.or(
|
||||
criteriaBuilder.like(criteriaBuilder.lower(rolloutRoot.get(JpaRollout_.name)), searchTextToLower),
|
||||
criteriaBuilder.like(criteriaBuilder.lower(rolloutRoot.get(JpaRollout_.description)),
|
||||
searchTextToLower));
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Rollout> findRolloutByFilters(final Pageable pageable, final String searchText) {
|
||||
final Specification<JpaRollout> specs = likeNameOrDescription(searchText);
|
||||
final Slice<JpaRollout> findAll = criteriaNoCountDao.findAll(specs, pageable, JpaRollout.class);
|
||||
setRolloutStatusDetails(findAll);
|
||||
return convertPage(findAll, pageable);
|
||||
}
|
||||
|
||||
@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((JpaRollout) 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 pageable) {
|
||||
final Page<JpaRollout> rollouts = rolloutRepository.findAll(pageable);
|
||||
setRolloutStatusDetails(rollouts);
|
||||
return convertPage(rollouts, pageable);
|
||||
|
||||
}
|
||||
|
||||
@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());
|
||||
((JpaRollout) 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<JpaRollout> 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());
|
||||
((JpaRollout) 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public Rollout generateRollout() {
|
||||
return new JpaRollout();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,706 @@
|
||||
/**
|
||||
* 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.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.ListJoin;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
||||
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.SoftwareModuleTypeFields;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.SwMetadataCompositeKey;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.SoftwareModuleSpecification;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
|
||||
import org.eclipse.hawkbit.repository.model.AssignedSoftwareModule;
|
||||
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.SoftwareModuleMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.rsql.RSQLUtility;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.AuditorAware;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.SliceImpl;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.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.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link SoftwareManagement}.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Validated
|
||||
@Service
|
||||
public class JpaSoftwareManagement implements SoftwareManagement {
|
||||
|
||||
@Autowired
|
||||
private EntityManager entityManager;
|
||||
|
||||
@Autowired
|
||||
private DistributionSetRepository distributionSetRepository;
|
||||
|
||||
@Autowired
|
||||
private DistributionSetTypeRepository distributionSetTypeRepository;
|
||||
|
||||
@Autowired
|
||||
private SoftwareModuleRepository softwareModuleRepository;
|
||||
|
||||
@Autowired
|
||||
private SoftwareModuleMetadataRepository softwareModuleMetadataRepository;
|
||||
|
||||
@Autowired
|
||||
private SoftwareModuleTypeRepository softwareModuleTypeRepository;
|
||||
|
||||
@Autowired
|
||||
private NoCountPagingRepository criteriaNoCountDao;
|
||||
|
||||
@Autowired
|
||||
private AuditorAware<String> auditorProvider;
|
||||
|
||||
@Autowired
|
||||
private ArtifactManagement artifactManagement;
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public SoftwareModule updateSoftwareModule(final SoftwareModule sm) {
|
||||
checkNotNull(sm.getId());
|
||||
|
||||
final JpaSoftwareModule module = softwareModuleRepository.findOne(sm.getId());
|
||||
|
||||
boolean updated = false;
|
||||
if (null == sm.getDescription() || !sm.getDescription().equals(module.getDescription())) {
|
||||
module.setDescription(sm.getDescription());
|
||||
updated = true;
|
||||
}
|
||||
if (null == sm.getVendor() || !sm.getVendor().equals(module.getVendor())) {
|
||||
module.setVendor(sm.getVendor());
|
||||
updated = true;
|
||||
}
|
||||
|
||||
return updated ? softwareModuleRepository.save(module) : module;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public SoftwareModuleType updateSoftwareModuleType(final SoftwareModuleType sm) {
|
||||
checkNotNull(sm.getId());
|
||||
|
||||
final JpaSoftwareModuleType type = softwareModuleTypeRepository.findOne(sm.getId());
|
||||
|
||||
boolean updated = false;
|
||||
if (sm.getDescription() != null && !sm.getDescription().equals(type.getDescription())) {
|
||||
type.setDescription(sm.getDescription());
|
||||
updated = true;
|
||||
}
|
||||
if (sm.getColour() != null && !sm.getColour().equals(type.getColour())) {
|
||||
type.setColour(sm.getColour());
|
||||
updated = true;
|
||||
}
|
||||
return updated ? softwareModuleTypeRepository.save(type) : type;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public SoftwareModule createSoftwareModule(final SoftwareModule swModule) {
|
||||
if (null != swModule.getId()) {
|
||||
throw new EntityAlreadyExistsException();
|
||||
}
|
||||
return softwareModuleRepository.save((JpaSoftwareModule) swModule);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public List<SoftwareModule> createSoftwareModule(final Collection<SoftwareModule> swModules) {
|
||||
swModules.forEach(swModule -> {
|
||||
if (null != swModule.getId()) {
|
||||
throw new EntityAlreadyExistsException();
|
||||
}
|
||||
});
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
final Collection<JpaSoftwareModule> jpaCast = (Collection) swModules;
|
||||
|
||||
return new ArrayList<>(softwareModuleRepository.save(jpaCast));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<SoftwareModule> findSoftwareModulesByType(final Pageable pageable, final SoftwareModuleType type) {
|
||||
|
||||
final List<Specification<JpaSoftwareModule>> specList = new LinkedList<>();
|
||||
|
||||
Specification<JpaSoftwareModule> spec = SoftwareModuleSpecification.equalType((JpaSoftwareModuleType) type);
|
||||
specList.add(spec);
|
||||
|
||||
spec = SoftwareModuleSpecification.isDeletedFalse();
|
||||
specList.add(spec);
|
||||
|
||||
return convertSmPage(findSwModuleByCriteriaAPI(pageable, specList), pageable);
|
||||
}
|
||||
|
||||
private static Slice<SoftwareModule> convertSmPage(final Slice<JpaSoftwareModule> findAll,
|
||||
final Pageable pageable) {
|
||||
return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, 0);
|
||||
}
|
||||
|
||||
private static Page<SoftwareModule> convertSmPage(final Page<JpaSoftwareModule> findAll, final Pageable pageable) {
|
||||
return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements());
|
||||
}
|
||||
|
||||
private static Page<SoftwareModuleMetadata> convertSmMdPage(final Page<JpaSoftwareModuleMetadata> findAll,
|
||||
final Pageable pageable) {
|
||||
return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countSoftwareModulesByType(final SoftwareModuleType type) {
|
||||
|
||||
final List<Specification<JpaSoftwareModule>> specList = new ArrayList<>();
|
||||
|
||||
Specification<JpaSoftwareModule> spec = SoftwareModuleSpecification.equalType((JpaSoftwareModuleType) type);
|
||||
specList.add(spec);
|
||||
|
||||
spec = SoftwareModuleSpecification.isDeletedFalse();
|
||||
specList.add(spec);
|
||||
|
||||
return countSwModuleByCriteriaAPI(specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoftwareModule findSoftwareModuleById(final Long id) {
|
||||
return artifactManagement.findSoftwareModuleById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoftwareModule findSoftwareModuleByNameAndVersion(final String name, final String version,
|
||||
final SoftwareModuleType type) {
|
||||
|
||||
return softwareModuleRepository.findOneByNameAndVersionAndType(name, version, (JpaSoftwareModuleType) type);
|
||||
}
|
||||
|
||||
private boolean isUnassigned(final JpaSoftwareModule bsmMerged) {
|
||||
return distributionSetRepository.findByModules(bsmMerged).isEmpty();
|
||||
}
|
||||
|
||||
private Slice<JpaSoftwareModule> findSwModuleByCriteriaAPI(final Pageable pageable,
|
||||
final List<Specification<JpaSoftwareModule>> specList) {
|
||||
return criteriaNoCountDao.findAll(SpecificationsBuilder.combineWithAnd(specList), pageable,
|
||||
JpaSoftwareModule.class);
|
||||
}
|
||||
|
||||
private Long countSwModuleByCriteriaAPI(final List<Specification<JpaSoftwareModule>> specList) {
|
||||
return softwareModuleRepository.count(SpecificationsBuilder.combineWithAnd(specList));
|
||||
}
|
||||
|
||||
private void deleteGridFsArtifacts(final JpaSoftwareModule swModule) {
|
||||
for (final LocalArtifact localArtifact : swModule.getLocalArtifacts()) {
|
||||
artifactManagement.deleteLocalArtifact(localArtifact);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public void deleteSoftwareModules(final Collection<Long> ids) {
|
||||
final List<JpaSoftwareModule> swModulesToDelete = softwareModuleRepository.findByIdIn(ids);
|
||||
final Set<Long> assignedModuleIds = new HashSet<>();
|
||||
swModulesToDelete.forEach(swModule -> {
|
||||
|
||||
// delete binary data of artifacts
|
||||
deleteGridFsArtifacts(swModule);
|
||||
|
||||
if (isUnassigned(swModule)) {
|
||||
|
||||
softwareModuleRepository.delete(swModule);
|
||||
|
||||
} else {
|
||||
|
||||
assignedModuleIds.add(swModule.getId());
|
||||
}
|
||||
});
|
||||
|
||||
if (!assignedModuleIds.isEmpty()) {
|
||||
String currentUser = null;
|
||||
if (auditorProvider != null) {
|
||||
currentUser = auditorProvider.getCurrentAuditor();
|
||||
}
|
||||
softwareModuleRepository.deleteSoftwareModule(System.currentTimeMillis(), currentUser,
|
||||
assignedModuleIds.toArray(new Long[0]));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<SoftwareModule> findSoftwareModulesAll(final Pageable pageable) {
|
||||
|
||||
final List<Specification<JpaSoftwareModule>> specList = new ArrayList<>();
|
||||
|
||||
Specification<JpaSoftwareModule> spec = SoftwareModuleSpecification.isDeletedFalse();
|
||||
specList.add(spec);
|
||||
|
||||
spec = (root, query, cb) -> {
|
||||
if (!query.getResultType().isAssignableFrom(Long.class)) {
|
||||
root.fetch(JpaSoftwareModule_.type);
|
||||
}
|
||||
return cb.conjunction();
|
||||
};
|
||||
|
||||
specList.add(spec);
|
||||
|
||||
return convertSmPage(findSwModuleByCriteriaAPI(pageable, specList), pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countSoftwareModulesAll() {
|
||||
|
||||
final List<Specification<JpaSoftwareModule>> specList = new ArrayList<>();
|
||||
|
||||
final Specification<JpaSoftwareModule> spec = SoftwareModuleSpecification.isDeletedFalse();
|
||||
specList.add(spec);
|
||||
|
||||
return countSwModuleByCriteriaAPI(specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoftwareModule findSoftwareModuleWithDetails(final Long id) {
|
||||
return artifactManagement.findSoftwareModuleWithDetails(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<SoftwareModule> findSoftwareModulesByPredicate(final String rsqlParam, final Pageable pageable) {
|
||||
final Specification<JpaSoftwareModule> spec = RSQLUtility.parse(rsqlParam, SoftwareModuleFields.class);
|
||||
|
||||
return convertSmPage(softwareModuleRepository.findAll(spec, pageable), pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<SoftwareModuleType> findSoftwareModuleTypesAll(final String rsqlParam, final Pageable pageable) {
|
||||
|
||||
final Specification<JpaSoftwareModuleType> spec = RSQLUtility.parse(rsqlParam, SoftwareModuleTypeFields.class);
|
||||
|
||||
return convertSmTPage(softwareModuleTypeRepository.findAll(spec, pageable), pageable);
|
||||
}
|
||||
|
||||
private static Page<SoftwareModuleType> convertSmTPage(final Page<JpaSoftwareModuleType> findAll,
|
||||
final Pageable pageable) {
|
||||
return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements());
|
||||
}
|
||||
|
||||
@Override
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||
public List<SoftwareModule> findSoftwareModulesById(final Collection<Long> ids) {
|
||||
return new ArrayList<>(softwareModuleRepository.findByIdIn(ids));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<SoftwareModule> findSoftwareModuleByFilters(final Pageable pageable, final String searchText,
|
||||
final SoftwareModuleType type) {
|
||||
|
||||
final List<Specification<JpaSoftwareModule>> specList = new ArrayList<>();
|
||||
|
||||
Specification<JpaSoftwareModule> spec = SoftwareModuleSpecification.isDeletedFalse();
|
||||
specList.add(spec);
|
||||
|
||||
if (!Strings.isNullOrEmpty(searchText)) {
|
||||
spec = SoftwareModuleSpecification.likeNameOrVersion(searchText);
|
||||
specList.add(spec);
|
||||
}
|
||||
|
||||
if (null != type) {
|
||||
spec = SoftwareModuleSpecification.equalType((JpaSoftwareModuleType) type);
|
||||
specList.add(spec);
|
||||
}
|
||||
|
||||
spec = (root, query, cb) -> {
|
||||
if (!query.getResultType().isAssignableFrom(Long.class)) {
|
||||
root.fetch(JpaSoftwareModule_.type);
|
||||
}
|
||||
return cb.conjunction();
|
||||
};
|
||||
|
||||
specList.add(spec);
|
||||
|
||||
return convertSmPage(findSwModuleByCriteriaAPI(pageable, specList), pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<AssignedSoftwareModule> findSoftwareModuleOrderBySetAssignmentAndModuleNameAscModuleVersionAsc(
|
||||
final Pageable pageable, final Long orderByDistributionId, final String searchText,
|
||||
final SoftwareModuleType ty) {
|
||||
final JpaSoftwareModuleType type = (JpaSoftwareModuleType) ty;
|
||||
|
||||
final List<AssignedSoftwareModule> resultList = new ArrayList<>();
|
||||
final int pageSize = pageable.getPageSize();
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
|
||||
// get the assigned software modules
|
||||
final CriteriaQuery<JpaSoftwareModule> assignedQuery = cb.createQuery(JpaSoftwareModule.class);
|
||||
final Root<JpaSoftwareModule> assignedRoot = assignedQuery.from(JpaSoftwareModule.class);
|
||||
assignedQuery.distinct(true);
|
||||
final ListJoin<JpaSoftwareModule, JpaDistributionSet> assignedDsJoin = assignedRoot
|
||||
.join(JpaSoftwareModule_.assignedTo);
|
||||
// build the specifications and then to predicates necessary by the
|
||||
// given filters
|
||||
final Predicate[] specPredicate = specificationsToPredicate(buildSpecificationList(searchText, type),
|
||||
assignedRoot, assignedQuery, cb,
|
||||
cb.equal(assignedDsJoin.get(JpaDistributionSet_.id), orderByDistributionId));
|
||||
// if we have some predicates then add it to the where clause of the
|
||||
// multi select
|
||||
assignedQuery.where(specPredicate);
|
||||
assignedQuery.orderBy(cb.asc(assignedRoot.get(JpaSoftwareModule_.name)),
|
||||
cb.asc(assignedRoot.get(JpaSoftwareModule_.version)));
|
||||
// don't page the assigned query on database, we need all assigned
|
||||
// software modules to filter
|
||||
// them out in the unassigned query
|
||||
final List<JpaSoftwareModule> assignedSoftwareModules = entityManager.createQuery(assignedQuery)
|
||||
.getResultList();
|
||||
// map result
|
||||
if (pageable.getOffset() < assignedSoftwareModules.size()) {
|
||||
assignedSoftwareModules
|
||||
.subList(pageable.getOffset(), Math.min(assignedSoftwareModules.size(), pageable.getPageSize()))
|
||||
.forEach(sw -> resultList.add(new AssignedSoftwareModule(sw, true)));
|
||||
}
|
||||
|
||||
if (assignedSoftwareModules.size() >= pageSize) {
|
||||
return new SliceImpl<>(resultList);
|
||||
}
|
||||
|
||||
// get the unassigned software modules
|
||||
final CriteriaQuery<JpaSoftwareModule> unassignedQuery = cb.createQuery(JpaSoftwareModule.class);
|
||||
unassignedQuery.distinct(true);
|
||||
final Root<JpaSoftwareModule> unassignedRoot = unassignedQuery.from(JpaSoftwareModule.class);
|
||||
|
||||
Predicate[] unassignedSpec;
|
||||
if (!assignedSoftwareModules.isEmpty()) {
|
||||
unassignedSpec = specificationsToPredicate(buildSpecificationList(searchText, type), unassignedRoot,
|
||||
unassignedQuery, cb, cb.not(unassignedRoot.get(JpaSoftwareModule_.id)
|
||||
.in(assignedSoftwareModules.stream().map(sw -> sw.getId()).collect(Collectors.toList()))));
|
||||
} else {
|
||||
unassignedSpec = specificationsToPredicate(buildSpecificationList(searchText, type), unassignedRoot,
|
||||
unassignedQuery, cb);
|
||||
}
|
||||
|
||||
unassignedQuery.where(unassignedSpec);
|
||||
unassignedQuery.orderBy(cb.asc(unassignedRoot.get(JpaSoftwareModule_.name)),
|
||||
cb.asc(unassignedRoot.get(JpaSoftwareModule_.version)));
|
||||
final List<JpaSoftwareModule> unassignedSoftwareModules = entityManager.createQuery(unassignedQuery)
|
||||
.setFirstResult(Math.max(0, pageable.getOffset() - assignedSoftwareModules.size()))
|
||||
.setMaxResults(pageSize).getResultList();
|
||||
// map result
|
||||
unassignedSoftwareModules.forEach(sw -> resultList.add(new AssignedSoftwareModule(sw, false)));
|
||||
|
||||
return new SliceImpl<>(resultList);
|
||||
}
|
||||
|
||||
private static List<Specification<JpaSoftwareModule>> buildSpecificationList(final String searchText,
|
||||
final JpaSoftwareModuleType type) {
|
||||
final List<Specification<JpaSoftwareModule>> specList = new ArrayList<>();
|
||||
if (!Strings.isNullOrEmpty(searchText)) {
|
||||
specList.add(SoftwareModuleSpecification.likeNameOrVersion(searchText));
|
||||
}
|
||||
if (type != null) {
|
||||
specList.add(SoftwareModuleSpecification.equalType(type));
|
||||
}
|
||||
specList.add(SoftwareModuleSpecification.isDeletedFalse());
|
||||
return specList;
|
||||
}
|
||||
|
||||
private Predicate[] specificationsToPredicate(final List<Specification<JpaSoftwareModule>> specifications,
|
||||
final Root<JpaSoftwareModule> root, final CriteriaQuery<?> query, final CriteriaBuilder cb,
|
||||
final Predicate... additionalPredicates) {
|
||||
final List<Predicate> predicates = new ArrayList<>();
|
||||
specifications.forEach(spec -> predicates.add(spec.toPredicate(root, query, cb)));
|
||||
for (final Predicate predicate : additionalPredicates) {
|
||||
predicates.add(predicate);
|
||||
}
|
||||
return predicates.toArray(new Predicate[predicates.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countSoftwareModuleByFilters(final String searchText, final SoftwareModuleType type) {
|
||||
|
||||
final List<Specification<JpaSoftwareModule>> specList = new ArrayList<>();
|
||||
|
||||
Specification<JpaSoftwareModule> spec = SoftwareModuleSpecification.isDeletedFalse();
|
||||
specList.add(spec);
|
||||
|
||||
if (!Strings.isNullOrEmpty(searchText)) {
|
||||
spec = SoftwareModuleSpecification.likeNameOrVersion(searchText);
|
||||
specList.add(spec);
|
||||
}
|
||||
|
||||
if (null != type) {
|
||||
spec = SoftwareModuleSpecification.equalType((JpaSoftwareModuleType) type);
|
||||
specList.add(spec);
|
||||
}
|
||||
|
||||
return countSwModuleByCriteriaAPI(specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<SoftwareModuleType> findSoftwareModuleTypesAll(final Pageable pageable) {
|
||||
return softwareModuleTypeRepository.findByDeleted(pageable, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countSoftwareModuleTypesAll() {
|
||||
return softwareModuleTypeRepository.countByDeleted(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoftwareModuleType findSoftwareModuleTypeByKey(final String key) {
|
||||
return softwareModuleTypeRepository.findByKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoftwareModuleType findSoftwareModuleTypeById(final Long id) {
|
||||
return softwareModuleTypeRepository.findOne(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoftwareModuleType findSoftwareModuleTypeByName(final String name) {
|
||||
return softwareModuleTypeRepository.findByName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public SoftwareModuleType createSoftwareModuleType(final SoftwareModuleType type) {
|
||||
if (type.getId() != null) {
|
||||
throw new EntityAlreadyExistsException("Given type contains an Id!");
|
||||
}
|
||||
|
||||
return softwareModuleTypeRepository.save((JpaSoftwareModuleType) type);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public void deleteSoftwareModuleType(final SoftwareModuleType ty) {
|
||||
final JpaSoftwareModuleType type = (JpaSoftwareModuleType) ty;
|
||||
|
||||
if (softwareModuleRepository.countByType(type) > 0
|
||||
|| distributionSetTypeRepository.countByElementsSmType(type) > 0) {
|
||||
final JpaSoftwareModuleType toDelete = entityManager.merge(type);
|
||||
toDelete.setDeleted(true);
|
||||
softwareModuleTypeRepository.save(toDelete);
|
||||
} else {
|
||||
softwareModuleTypeRepository.delete(type.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<SoftwareModule> findSoftwareModuleByAssignedTo(final Pageable pageable, final DistributionSet set) {
|
||||
return softwareModuleRepository.findByAssignedTo(pageable, (JpaDistributionSet) set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<SoftwareModule> findSoftwareModuleByAssignedToAndType(final Pageable pageable,
|
||||
final DistributionSet set, final SoftwareModuleType type) {
|
||||
return softwareModuleRepository.findByAssignedToAndType(pageable, (JpaDistributionSet) set,
|
||||
(JpaSoftwareModuleType) type);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
public SoftwareModuleMetadata createSoftwareModuleMetadata(final SoftwareModuleMetadata md) {
|
||||
final JpaSoftwareModuleMetadata metadata = (JpaSoftwareModuleMetadata) md;
|
||||
|
||||
if (softwareModuleMetadataRepository.exists(metadata.getId())) {
|
||||
throwMetadataKeyAlreadyExists(metadata.getId().getKey());
|
||||
}
|
||||
// merge base software module so optLockRevision gets updated and audit
|
||||
// log written because
|
||||
// modifying metadata is modifying the base software module itself for
|
||||
// auditing purposes.
|
||||
entityManager.merge((JpaSoftwareModule) metadata.getSoftwareModule()).setLastModifiedAt(-1L);
|
||||
return softwareModuleMetadataRepository.save(metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
public List<SoftwareModuleMetadata> createSoftwareModuleMetadata(final Collection<SoftwareModuleMetadata> md) {
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
final Collection<JpaSoftwareModuleMetadata> metadata = (Collection) md;
|
||||
|
||||
for (final JpaSoftwareModuleMetadata softwareModuleMetadata : metadata) {
|
||||
checkAndThrowAlreadyExistsIfSoftwareModuleMetadataExists(softwareModuleMetadata.getId());
|
||||
}
|
||||
metadata.forEach(m -> entityManager.merge((JpaSoftwareModule) m.getSoftwareModule()).setLastModifiedAt(-1L));
|
||||
return new ArrayList<>(softwareModuleMetadataRepository.save(metadata));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
public SoftwareModuleMetadata updateSoftwareModuleMetadata(final SoftwareModuleMetadata md) {
|
||||
final JpaSoftwareModuleMetadata metadata = (JpaSoftwareModuleMetadata) md;
|
||||
|
||||
// check if exists otherwise throw entity not found exception
|
||||
findSoftwareModuleMetadata(metadata.getId());
|
||||
// touch it to update the lock revision because we are modifying the
|
||||
// software module
|
||||
// indirectly
|
||||
entityManager.merge((JpaSoftwareModule) metadata.getSoftwareModule()).setLastModifiedAt(-1L);
|
||||
return softwareModuleMetadataRepository.save(metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
public void deleteSoftwareModuleMetadata(final SoftwareModule softwareModule, final String key) {
|
||||
softwareModuleMetadataRepository.delete(new SwMetadataCompositeKey(softwareModule, key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<SoftwareModuleMetadata> findSoftwareModuleMetadataBySoftwareModuleId(final Long swId,
|
||||
final Pageable pageable) {
|
||||
return softwareModuleMetadataRepository.findBySoftwareModuleId(swId, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<SoftwareModuleMetadata> findSoftwareModuleMetadataBySoftwareModuleId(final Long softwareModuleId,
|
||||
final String rsqlParam, final Pageable pageable) {
|
||||
|
||||
final Specification<JpaSoftwareModuleMetadata> spec = RSQLUtility.parse(rsqlParam,
|
||||
SoftwareModuleMetadataFields.class);
|
||||
return convertSmMdPage(
|
||||
softwareModuleMetadataRepository
|
||||
.findAll(
|
||||
(Specification<JpaSoftwareModuleMetadata>) (root, query, cb) -> cb.and(
|
||||
cb.equal(root.get(JpaSoftwareModuleMetadata_.softwareModule)
|
||||
.get(JpaSoftwareModule_.id), softwareModuleId),
|
||||
spec.toPredicate(root, query, cb)),
|
||||
pageable),
|
||||
pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoftwareModuleMetadata findSoftwareModuleMetadata(final SoftwareModule softwareModule, final String key) {
|
||||
return findSoftwareModuleMetadata(new SwMetadataCompositeKey(softwareModule, key));
|
||||
}
|
||||
|
||||
private SoftwareModuleMetadata findSoftwareModuleMetadata(final SwMetadataCompositeKey id) {
|
||||
final SoftwareModuleMetadata findOne = softwareModuleMetadataRepository.findOne(id);
|
||||
if (findOne == null) {
|
||||
throw new EntityNotFoundException("Metadata with key '" + id.getKey() + "' does not exist");
|
||||
}
|
||||
return findOne;
|
||||
}
|
||||
|
||||
private void checkAndThrowAlreadyExistsIfSoftwareModuleMetadataExists(final SwMetadataCompositeKey metadataId) {
|
||||
if (softwareModuleMetadataRepository.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 void deleteSoftwareModule(final SoftwareModule bsm) {
|
||||
deleteSoftwareModules(Sets.newHashSet(bsm.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public List<SoftwareModuleType> createSoftwareModuleType(final Collection<SoftwareModuleType> types) {
|
||||
|
||||
return types.stream().map(this::createSoftwareModuleType).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public SoftwareModuleType generateSoftwareModuleType() {
|
||||
return new JpaSoftwareModuleType();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public SoftwareModule generateSoftwareModule() {
|
||||
return new JpaSoftwareModule();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public SoftwareModule generateSoftwareModule(final SoftwareModuleType type, final String name, final String version,
|
||||
final String description, final String vendor) {
|
||||
|
||||
return new JpaSoftwareModule(type, name, version, description, vendor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public SoftwareModuleMetadata generateSoftwareModuleMetadata() {
|
||||
return new JpaSoftwareModuleMetadata();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public SoftwareModuleMetadata generateSoftwareModuleMetadata(final SoftwareModule softwareModule, final String key,
|
||||
final String value) {
|
||||
return new JpaSoftwareModuleMetadata(key, softwareModule, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public SoftwareModuleType generateSoftwareModuleType(final String key, final String name, final String description,
|
||||
final int maxAssignments) {
|
||||
return new JpaSoftwareModuleType(key, name, description, maxAssignments);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
/**
|
||||
* 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.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import org.eclipse.hawkbit.cache.TenancyCacheManager;
|
||||
import org.eclipse.hawkbit.report.model.SystemUsageReport;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantStatsManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTenantMetaData;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.model.TenantMetaData;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.persistence.config.PersistenceUnitProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.CachePut;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.cache.interceptor.SimpleKeyGenerator;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
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;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link SystemManagement}.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Validated
|
||||
@Service
|
||||
public class JpaSystemManagement implements SystemManagement {
|
||||
@Autowired
|
||||
private EntityManager entityManager;
|
||||
|
||||
@Autowired
|
||||
private TargetRepository targetRepository;
|
||||
|
||||
@Autowired
|
||||
private ActionRepository actionRepository;
|
||||
|
||||
@Autowired
|
||||
private DistributionSetRepository distributionSetRepository;
|
||||
|
||||
@Autowired
|
||||
private SoftwareModuleRepository softwareModuleRepository;
|
||||
|
||||
@Autowired
|
||||
private TenantMetaDataRepository tenantMetaDataRepository;
|
||||
|
||||
@Autowired
|
||||
private DistributionSetTypeRepository distributionSetTypeRepository;
|
||||
|
||||
@Autowired
|
||||
private SoftwareModuleTypeRepository softwareModuleTypeRepository;
|
||||
|
||||
@Autowired
|
||||
private TargetTagRepository targetTagRepository;
|
||||
|
||||
@Autowired
|
||||
private DistributionSetTagRepository distributionSetTagRepository;
|
||||
|
||||
@Autowired
|
||||
private ExternalArtifactRepository externalArtifactRepository;
|
||||
|
||||
@Autowired
|
||||
private LocalArtifactRepository artifactRepository;
|
||||
|
||||
@Autowired
|
||||
private ExternalArtifactProviderRepository externalArtifactProviderRepository;
|
||||
|
||||
@Autowired
|
||||
private TenantConfigurationRepository tenantConfigurationRepository;
|
||||
|
||||
@Autowired
|
||||
private RolloutRepository rolloutRepository;
|
||||
|
||||
@Autowired
|
||||
private RolloutGroupRepository rolloutGroupRepository;
|
||||
|
||||
@Autowired
|
||||
private TenantAware tenantAware;
|
||||
|
||||
@Autowired
|
||||
private TenantStatsManagement systemStatsManagement;
|
||||
|
||||
@Autowired
|
||||
private TenancyCacheManager cacheManager;
|
||||
|
||||
private final ThreadLocal<String> createInitialTenant = new ThreadLocal<>();
|
||||
|
||||
@Override
|
||||
public SystemUsageReport getSystemUsageStatistics() {
|
||||
|
||||
BigDecimal sumOfArtifacts = (BigDecimal) entityManager
|
||||
.createNativeQuery(
|
||||
"select SUM(file_size) from sp_artifact a INNER JOIN sp_base_software_module sm ON a.software_module = sm.id WHERE sm.deleted = 0")
|
||||
.getSingleResult();
|
||||
|
||||
if (sumOfArtifacts == null) {
|
||||
sumOfArtifacts = new BigDecimal(0);
|
||||
}
|
||||
|
||||
// we use native queries to punch through the tenant boundaries. This
|
||||
// has to be used with care!
|
||||
final Long targets = (Long) entityManager.createNativeQuery("SELECT COUNT(id) FROM sp_target")
|
||||
.getSingleResult();
|
||||
|
||||
final Long artifacts = (Long) entityManager
|
||||
.createNativeQuery(
|
||||
"SELECT COUNT(a.id) FROM sp_artifact a INNER JOIN sp_base_software_module sm ON a.software_module = sm.id WHERE sm.deleted = 0")
|
||||
.getSingleResult();
|
||||
|
||||
final Long actions = (Long) entityManager.createNativeQuery("SELECT COUNT(id) FROM sp_action")
|
||||
.getSingleResult();
|
||||
|
||||
final SystemUsageReport result = new SystemUsageReport(targets, artifacts, actions,
|
||||
sumOfArtifacts.setScale(0, BigDecimal.ROUND_HALF_UP).longValue());
|
||||
|
||||
usageStatsPerTenant(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void usageStatsPerTenant(final SystemUsageReport report) {
|
||||
final List<String> tenants = findTenants();
|
||||
|
||||
tenants.forEach(tenant -> tenantAware.runAsTenant(tenant, () -> {
|
||||
report.addTenantData(systemStatsManagement.getStatsOfTenant(tenant));
|
||||
return null;
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
@Bean
|
||||
public KeyGenerator currentTenantKeyGenerator() {
|
||||
return new CurrentTenantKeyGenerator();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = "tenantMetadata", key = "#tenant.toUpperCase()")
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
public TenantMetaData getTenantMetadata(final String tenant) {
|
||||
final TenantMetaData result = tenantMetaDataRepository.findByTenantIgnoreCase(tenant);
|
||||
|
||||
// Create if it does not exist
|
||||
if (result == null) {
|
||||
try {
|
||||
createInitialTenant.set(tenant);
|
||||
cacheManager.getCache("currentTenant").evict(currentTenantKeyGenerator().generate(null, null));
|
||||
return tenantMetaDataRepository.save(new JpaTenantMetaData(createStandardSoftwareDataSetup(), tenant));
|
||||
} finally {
|
||||
createInitialTenant.remove();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> findTenants() {
|
||||
return tenantMetaDataRepository.findAll().stream().map(md -> md.getTenant()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(value = { "tenantMetadata" }, key = "#tenant.toUpperCase()")
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
public void deleteTenant(final String tenant) {
|
||||
cacheManager.evictCaches(tenant);
|
||||
cacheManager.getCache("currentTenant").evict(currentTenantKeyGenerator().generate(null, null));
|
||||
tenantAware.runAsTenant(tenant, () -> {
|
||||
entityManager.setProperty(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, tenant.toUpperCase());
|
||||
tenantMetaDataRepository.deleteByTenantIgnoreCase(tenant);
|
||||
tenantConfigurationRepository.deleteByTenantIgnoreCase(tenant);
|
||||
targetRepository.deleteByTenantIgnoreCase(tenant);
|
||||
actionRepository.deleteByTenantIgnoreCase(tenant);
|
||||
rolloutGroupRepository.deleteByTenantIgnoreCase(tenant);
|
||||
rolloutRepository.deleteByTenantIgnoreCase(tenant);
|
||||
artifactRepository.deleteByTenantIgnoreCase(tenant);
|
||||
externalArtifactRepository.deleteByTenantIgnoreCase(tenant);
|
||||
externalArtifactProviderRepository.deleteByTenantIgnoreCase(tenant);
|
||||
targetTagRepository.deleteByTenantIgnoreCase(tenant);
|
||||
distributionSetTagRepository.deleteByTenantIgnoreCase(tenant);
|
||||
distributionSetRepository.deleteByTenantIgnoreCase(tenant);
|
||||
distributionSetTypeRepository.deleteByTenantIgnoreCase(tenant);
|
||||
softwareModuleRepository.deleteByTenantIgnoreCase(tenant);
|
||||
softwareModuleTypeRepository.deleteByTenantIgnoreCase(tenant);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = "tenantMetadata", keyGenerator = "tenantKeyGenerator")
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
public TenantMetaData getTenantMetadata() {
|
||||
if (tenantAware.getCurrentTenant() == null) {
|
||||
throw new IllegalStateException("Tenant not set");
|
||||
}
|
||||
|
||||
return getTenantMetadata(tenantAware.getCurrentTenant());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = "currentTenant", keyGenerator = "currentTenantKeyGenerator")
|
||||
// set transaction to not supported, due we call this in
|
||||
// BaseEntity#prePersist methods
|
||||
// and it seems that JPA committing the transaction when executing this
|
||||
// transactional method,
|
||||
// which then leads that the BaseEntity#prePersist is called again to
|
||||
// persist the un-persisted
|
||||
// entity and we landing again in the #currentTenant() method
|
||||
// suspend the transaction here to do a read-request against the medata
|
||||
// table, when the current
|
||||
// tenant is not cached anyway already.
|
||||
@Transactional(propagation = Propagation.NOT_SUPPORTED, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public String currentTenant() {
|
||||
final String initialTenantCreation = createInitialTenant.get();
|
||||
if (initialTenantCreation == null) {
|
||||
final TenantMetaData findByTenant = tenantMetaDataRepository
|
||||
.findByTenantIgnoreCase(tenantAware.getCurrentTenant());
|
||||
return findByTenant != null ? findByTenant.getTenant() : null;
|
||||
}
|
||||
return initialTenantCreation;
|
||||
}
|
||||
|
||||
@Override
|
||||
@CachePut(value = "tenantMetadata", key = "#metaData.tenant.toUpperCase()")
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
public TenantMetaData updateTenantMetadata(final TenantMetaData metaData) {
|
||||
if (!tenantMetaDataRepository.exists(metaData.getId())) {
|
||||
throw new EntityNotFoundException("Metadata does not exist: " + metaData.getId());
|
||||
}
|
||||
|
||||
return tenantMetaDataRepository.save((JpaTenantMetaData) metaData);
|
||||
}
|
||||
|
||||
private DistributionSetType createStandardSoftwareDataSetup() {
|
||||
final SoftwareModuleType eclApp = softwareModuleTypeRepository.save(new JpaSoftwareModuleType("application",
|
||||
"ECL Application", "Edge Controller Linux base application type", 1));
|
||||
final SoftwareModuleType eclOs = softwareModuleTypeRepository.save(
|
||||
new JpaSoftwareModuleType("os", "ECL OS", "Edge Controller Linux operation system image type", 1));
|
||||
final SoftwareModuleType eclJvm = softwareModuleTypeRepository.save(
|
||||
new JpaSoftwareModuleType("runtime", "ECL JVM", "Edge Controller Linux java virtual machine type.", 1));
|
||||
|
||||
distributionSetTypeRepository.save((JpaDistributionSetType) new JpaDistributionSetType("ecl_os", "OS only",
|
||||
"Standard Edge Controller Linux distribution set type.").addMandatoryModuleType(eclOs));
|
||||
|
||||
distributionSetTypeRepository.save((JpaDistributionSetType) new JpaDistributionSetType("ecl_os_app",
|
||||
"OS with optional app", "Standard Edge Controller Linux distribution set type. OS only.")
|
||||
.addMandatoryModuleType(eclOs).addOptionalModuleType(eclApp));
|
||||
|
||||
return distributionSetTypeRepository.save(
|
||||
(JpaDistributionSetType) new JpaDistributionSetType("ecl_os_app_jvm", "OS with optional app and jvm",
|
||||
"Standard Edge Controller Linux distribution set type. OS with optional application.")
|
||||
.addMandatoryModuleType(eclOs).addOptionalModuleType(eclApp)
|
||||
.addOptionalModuleType(eclJvm));
|
||||
}
|
||||
|
||||
/**
|
||||
* A implementation of the {@link KeyGenerator} to generate a key based on
|
||||
* either the {@code createInitialTenant} thread local and the
|
||||
* {@link TenantAware}, but in case we are in a tenant creation with its
|
||||
* default types we need to use the tenant the current tenant which is
|
||||
* currently created and not the one currently in the {@link TenantAware}.
|
||||
*
|
||||
*/
|
||||
public class CurrentTenantKeyGenerator implements KeyGenerator {
|
||||
@Override
|
||||
// Exception squid:S923 - override
|
||||
@SuppressWarnings({ "squid:S923" })
|
||||
public Object generate(final Object target, final Method method, final Object... params) {
|
||||
final String initialTenantCreation = createInitialTenant.get();
|
||||
if (initialTenantCreation == null) {
|
||||
return SimpleKeyGenerator.generateKey(tenantAware.getCurrentTenant().toUpperCase(),
|
||||
tenantAware.getCurrentTenant().toUpperCase());
|
||||
}
|
||||
return SimpleKeyGenerator.generateKey(initialTenantCreation.toUpperCase(),
|
||||
initialTenantCreation.toUpperCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
/**
|
||||
* 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.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
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.eventbus.event.TargetTagCreatedBulkEvent;
|
||||
import org.eclipse.hawkbit.eventbus.event.TargetTagDeletedEvent;
|
||||
import org.eclipse.hawkbit.eventbus.event.TargetTagUpdateEvent;
|
||||
import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor;
|
||||
import org.eclipse.hawkbit.repository.TagFields;
|
||||
import org.eclipse.hawkbit.repository.TagManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.eclipse.hawkbit.repository.rsql.RSQLUtility;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
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.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
* JP>A implementation of {@link TagManagement}.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Validated
|
||||
@Service
|
||||
public class JpaTagManagement implements TagManagement {
|
||||
|
||||
@Autowired
|
||||
private TargetTagRepository targetTagRepository;
|
||||
|
||||
@Autowired
|
||||
private TargetRepository targetRepository;
|
||||
|
||||
@Autowired
|
||||
private DistributionSetTagRepository distributionSetTagRepository;
|
||||
|
||||
@Autowired
|
||||
private DistributionSetRepository distributionSetRepository;
|
||||
|
||||
@Autowired
|
||||
private EventBus eventBus;
|
||||
|
||||
@Autowired
|
||||
private TenantAware tenantAware;
|
||||
|
||||
@Autowired
|
||||
private AfterTransactionCommitExecutor afterCommit;
|
||||
|
||||
@Override
|
||||
public TargetTag findTargetTag(final String name) {
|
||||
return targetTagRepository.findByNameEquals(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public TargetTag createTargetTag(final TargetTag targetTag) {
|
||||
if (null != targetTag.getId()) {
|
||||
throw new EntityAlreadyExistsException();
|
||||
}
|
||||
|
||||
if (findTargetTag(targetTag.getName()) != null) {
|
||||
throw new EntityAlreadyExistsException();
|
||||
}
|
||||
|
||||
final TargetTag save = targetTagRepository.save((JpaTargetTag) targetTag);
|
||||
|
||||
afterCommit
|
||||
.afterCommit(() -> eventBus.post(new TargetTagCreatedBulkEvent(tenantAware.getCurrentTenant(), save)));
|
||||
|
||||
return save;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public List<TargetTag> createTargetTags(final Collection<TargetTag> tt) {
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
final Collection<JpaTargetTag> targetTags = (Collection) tt;
|
||||
|
||||
targetTags.forEach(tag -> {
|
||||
if (tag.getId() != null) {
|
||||
throw new EntityAlreadyExistsException();
|
||||
}
|
||||
});
|
||||
|
||||
final List<TargetTag> save = new ArrayList<>(targetTagRepository.save(targetTags));
|
||||
afterCommit
|
||||
.afterCommit(() -> eventBus.post(new TargetTagCreatedBulkEvent(tenantAware.getCurrentTenant(), save)));
|
||||
return save;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public void deleteTargetTag(final String targetTagName) {
|
||||
final JpaTargetTag tag = targetTagRepository.findByNameEquals(targetTagName);
|
||||
|
||||
final List<JpaTarget> changed = new LinkedList<>();
|
||||
for (final JpaTarget target : targetRepository.findByTag(tag)) {
|
||||
target.getTags().remove(tag);
|
||||
changed.add(target);
|
||||
}
|
||||
|
||||
// save association delete
|
||||
targetRepository.save(changed);
|
||||
|
||||
// finally delete the tag itself
|
||||
targetTagRepository.deleteByName(targetTagName);
|
||||
|
||||
afterCommit.afterCommit(() -> eventBus.post(new TargetTagDeletedEvent(tag)));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TargetTag> findAllTargetTags() {
|
||||
return new ArrayList<>(targetTagRepository.findAll());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<TargetTag> findAllTargetTags(final String rsqlParam, final Pageable pageable) {
|
||||
|
||||
final Specification<JpaTargetTag> spec = RSQLUtility.parse(rsqlParam, TagFields.class);
|
||||
return convertTPage(targetTagRepository.findAll(spec, pageable), pageable);
|
||||
}
|
||||
|
||||
private static Page<TargetTag> convertTPage(final Page<JpaTargetTag> findAll, final Pageable pageable) {
|
||||
return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements());
|
||||
}
|
||||
|
||||
private static Page<DistributionSetTag> convertDsPage(final Page<JpaDistributionSetTag> findAll,
|
||||
final Pageable pageable) {
|
||||
return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countTargetTags() {
|
||||
return targetTagRepository.count();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public TargetTag updateTargetTag(final TargetTag targetTag) {
|
||||
checkNotNull(targetTag.getName());
|
||||
checkNotNull(targetTag.getId());
|
||||
final TargetTag save = targetTagRepository.save((JpaTargetTag) targetTag);
|
||||
afterCommit.afterCommit(() -> eventBus.post(new TargetTagUpdateEvent(save)));
|
||||
return save;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSetTag findDistributionSetTag(final String name) {
|
||||
return distributionSetTagRepository.findByNameEquals(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public DistributionSetTag createDistributionSetTag(final DistributionSetTag distributionSetTag) {
|
||||
if (null != distributionSetTag.getId()) {
|
||||
throw new EntityAlreadyExistsException();
|
||||
}
|
||||
|
||||
if (distributionSetTagRepository.findByNameEquals(distributionSetTag.getName()) != null) {
|
||||
throw new EntityAlreadyExistsException();
|
||||
}
|
||||
|
||||
final DistributionSetTag save = distributionSetTagRepository.save((JpaDistributionSetTag) distributionSetTag);
|
||||
|
||||
afterCommit.afterCommit(
|
||||
() -> eventBus.post(new DistributionSetTagCreatedBulkEvent(tenantAware.getCurrentTenant(), save)));
|
||||
return save;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public List<DistributionSetTag> createDistributionSetTags(final Collection<DistributionSetTag> dst) {
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
final Collection<JpaDistributionSetTag> distributionSetTags = (Collection) dst;
|
||||
|
||||
for (final DistributionSetTag dsTag : distributionSetTags) {
|
||||
if (dsTag.getId() != null) {
|
||||
throw new EntityAlreadyExistsException();
|
||||
}
|
||||
}
|
||||
final List<DistributionSetTag> save = new ArrayList<>(distributionSetTagRepository.save(distributionSetTags));
|
||||
afterCommit.afterCommit(
|
||||
() -> eventBus.post(new DistributionSetTagCreatedBulkEvent(tenantAware.getCurrentTenant(), save)));
|
||||
|
||||
return save;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public void deleteDistributionSetTag(final String tagName) {
|
||||
final JpaDistributionSetTag tag = distributionSetTagRepository.findByNameEquals(tagName);
|
||||
|
||||
final List<JpaDistributionSet> changed = new LinkedList<>();
|
||||
for (final JpaDistributionSet set : distributionSetRepository.findByTag(tag)) {
|
||||
set.getTags().remove(tag);
|
||||
changed.add(set);
|
||||
}
|
||||
|
||||
// save association delete
|
||||
distributionSetRepository.save(changed);
|
||||
|
||||
distributionSetTagRepository.deleteByName(tagName);
|
||||
|
||||
afterCommit.afterCommit(() -> eventBus.post(new DistributionSetTagDeletedEvent(tag)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public DistributionSetTag updateDistributionSetTag(final DistributionSetTag distributionSetTag) {
|
||||
checkNotNull(distributionSetTag.getName());
|
||||
checkNotNull(distributionSetTag.getId());
|
||||
final DistributionSetTag save = distributionSetTagRepository.save((JpaDistributionSetTag) distributionSetTag);
|
||||
afterCommit.afterCommit(() -> eventBus.post(new DistributionSetTagUpdateEvent(save)));
|
||||
|
||||
return save;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DistributionSetTag> findAllDistributionSetTags() {
|
||||
return new ArrayList<>(distributionSetTagRepository.findAll());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetTag findTargetTagById(final Long id) {
|
||||
return targetTagRepository.findOne(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSetTag findDistributionSetTagById(final Long id) {
|
||||
return distributionSetTagRepository.findOne(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<TargetTag> findAllTargetTags(final Pageable pageable) {
|
||||
return convertTPage(targetTagRepository.findAll(pageable), pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<DistributionSetTag> findAllDistributionSetTags(final Pageable pageable) {
|
||||
return convertDsPage(distributionSetTagRepository.findAll(pageable), pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<DistributionSetTag> findAllDistributionSetTags(final String rsqlParam, final Pageable pageable) {
|
||||
final Specification<JpaDistributionSetTag> spec = RSQLUtility.parse(rsqlParam, TagFields.class);
|
||||
|
||||
return convertDsPage(distributionSetTagRepository.findAll(spec, pageable), pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public TargetTag generateTargetTag() {
|
||||
return new JpaTargetTag();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public DistributionSetTag generateDistributionSetTag() {
|
||||
return new JpaDistributionSetTag();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public TargetTag generateTargetTag(final String name, final String description, final String colour) {
|
||||
return new JpaTargetTag(name, description, colour);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public DistributionSetTag generateDistributionSetTag(final String name, final String description,
|
||||
final String colour) {
|
||||
return new JpaDistributionSetTag(name, description, colour);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public TargetTag generateTargetTag(final String name) {
|
||||
return new JpaTargetTag(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public DistributionSetTag generateDistributionSetTag(final String name) {
|
||||
return new JpaDistributionSetTag(name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* 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.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.TargetFilterQuerySpecification;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
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.domain.Specifications;
|
||||
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.util.Assert;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link TargetFilterQueryManagement}.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Validated
|
||||
@Service
|
||||
public class JpaTargetFilterQueryManagement implements TargetFilterQueryManagement {
|
||||
|
||||
@Autowired
|
||||
private TargetFilterQueryRepository targetFilterQueryRepository;
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public TargetFilterQuery createTargetFilterQuery(final TargetFilterQuery customTargetFilter) {
|
||||
|
||||
if (targetFilterQueryRepository.findByName(customTargetFilter.getName()) != null) {
|
||||
throw new EntityAlreadyExistsException(customTargetFilter.getName());
|
||||
}
|
||||
return targetFilterQueryRepository.save((JpaTargetFilterQuery) customTargetFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public void deleteTargetFilterQuery(final Long targetFilterQueryId) {
|
||||
targetFilterQueryRepository.delete(targetFilterQueryId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<TargetFilterQuery> findAllTargetFilterQuery(final Pageable pageable) {
|
||||
return convertPage(targetFilterQueryRepository.findAll(pageable), pageable);
|
||||
}
|
||||
|
||||
private static Page<TargetFilterQuery> convertPage(final Page<JpaTargetFilterQuery> findAll,
|
||||
final Pageable pageable) {
|
||||
return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<TargetFilterQuery> findTargetFilterQueryByFilters(final Pageable pageable, final String name) {
|
||||
final List<Specification<JpaTargetFilterQuery>> specList = new ArrayList<>();
|
||||
if (!Strings.isNullOrEmpty(name)) {
|
||||
specList.add(TargetFilterQuerySpecification.likeName(name));
|
||||
}
|
||||
return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable);
|
||||
}
|
||||
|
||||
private Page<JpaTargetFilterQuery> findTargetFilterQueryByCriteriaAPI(final Pageable pageable,
|
||||
final List<Specification<JpaTargetFilterQuery>> specList) {
|
||||
if (specList == null || specList.isEmpty()) {
|
||||
return targetFilterQueryRepository.findAll(pageable);
|
||||
}
|
||||
|
||||
final Specifications<JpaTargetFilterQuery> specs = SpecificationsBuilder.combineWithAnd(specList);
|
||||
return targetFilterQueryRepository.findAll(specs, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetFilterQuery findTargetFilterQueryByName(final String targetFilterQueryName) {
|
||||
return targetFilterQueryRepository.findByName(targetFilterQueryName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetFilterQuery findTargetFilterQueryById(final Long targetFilterQueryId) {
|
||||
return targetFilterQueryRepository.findOne(targetFilterQueryId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public TargetFilterQuery updateTargetFilterQuery(final TargetFilterQuery targetFilterQuery) {
|
||||
Assert.notNull(targetFilterQuery.getId());
|
||||
return targetFilterQueryRepository.save((JpaTargetFilterQuery) targetFilterQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetFilterQuery generateTargetFilterQuery() {
|
||||
return new JpaTargetFilterQuery();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,681 @@
|
||||
/**
|
||||
* 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.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Expression;
|
||||
import javax.persistence.criteria.Join;
|
||||
import javax.persistence.criteria.JoinType;
|
||||
import javax.persistence.criteria.Order;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.eclipse.hawkbit.Constants;
|
||||
import org.eclipse.hawkbit.eventbus.event.TargetTagAssigmentResultEvent;
|
||||
import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor;
|
||||
import org.eclipse.hawkbit.repository.TargetFields;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.eclipse.hawkbit.repository.model.TargetIdName;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.rsql.RSQLUtility;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.SliceImpl;
|
||||
import org.springframework.data.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.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link TargetManagement}.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Validated
|
||||
@Service
|
||||
public class JpaTargetManagement implements TargetManagement {
|
||||
|
||||
@Autowired
|
||||
private EntityManager entityManager;
|
||||
|
||||
@Autowired
|
||||
private TargetRepository targetRepository;
|
||||
|
||||
@Autowired
|
||||
private TargetTagRepository targetTagRepository;
|
||||
|
||||
@Autowired
|
||||
private TargetInfoRepository targetInfoRepository;
|
||||
|
||||
@Autowired
|
||||
private NoCountPagingRepository criteriaNoCountDao;
|
||||
|
||||
@Autowired
|
||||
private EventBus eventBus;
|
||||
|
||||
@Autowired
|
||||
private AfterTransactionCommitExecutor afterCommit;
|
||||
|
||||
@Override
|
||||
public Target findTargetByControllerID(final String controllerId) {
|
||||
return targetRepository.findByControllerId(controllerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Target findTargetByControllerIDWithDetails(final String controllerId) {
|
||||
final Target result = targetRepository.findByControllerId(controllerId);
|
||||
// load lazy relations
|
||||
if (result != null) {
|
||||
result.getTargetInfo().getControllerAttributes().size();
|
||||
if (result.getTargetInfo() != null && result.getTargetInfo().getInstalledDistributionSet() != null) {
|
||||
result.getTargetInfo().getInstalledDistributionSet().getName();
|
||||
result.getTargetInfo().getInstalledDistributionSet().getModules().size();
|
||||
}
|
||||
if (result.getAssignedDistributionSet() != null) {
|
||||
result.getAssignedDistributionSet().getName();
|
||||
result.getAssignedDistributionSet().getModules().size();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Target> findTargetByControllerID(final Collection<String> controllerIDs) {
|
||||
return new ArrayList<>(targetRepository
|
||||
.findAll(TargetSpecifications.byControllerIdWithStatusAndAssignedInJoin(controllerIDs)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countTargetsAll() {
|
||||
return targetRepository.count();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Target> findTargetsAll(final Pageable pageable) {
|
||||
// workarround - no join fetch allowed that is why we need specification
|
||||
// instead of query for
|
||||
// count() of Pageable
|
||||
final Specification<JpaTarget> spec = (root, query, cb) -> {
|
||||
if (!query.getResultType().isAssignableFrom(Long.class)) {
|
||||
root.fetch(JpaTarget_.targetInfo);
|
||||
}
|
||||
return cb.conjunction();
|
||||
};
|
||||
return convertPage(criteriaNoCountDao.findAll(spec, pageable, JpaTarget.class), pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Target> findTargetsAll(final TargetFilterQuery targetFilterQuery, final Pageable pageable) {
|
||||
return findTargetsBySpec(RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class), pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Target> findTargetsAll(final String targetFilterQuery, final Pageable pageable) {
|
||||
return findTargetsBySpec(RSQLUtility.parse(targetFilterQuery, TargetFields.class), pageable);
|
||||
}
|
||||
|
||||
private Page<Target> findTargetsBySpec(final Specification<JpaTarget> spec, final Pageable pageable) {
|
||||
return convertPage(targetRepository.findAll(spec, pageable), pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Target> findTargetsByControllerIDsWithTags(final List<String> controllerIDs) {
|
||||
final List<List<String>> partition = Lists.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT);
|
||||
return partition.stream()
|
||||
.map(ids -> targetRepository.findAll(TargetSpecifications.byControllerIdWithStatusAndTagsInJoin(ids)))
|
||||
.flatMap(t -> t.stream()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public Target updateTarget(final Target target) {
|
||||
Assert.notNull(target.getId());
|
||||
|
||||
final JpaTarget toUpdate = (JpaTarget) target;
|
||||
toUpdate.setNew(false);
|
||||
return targetRepository.save(toUpdate);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public List<Target> updateTargets(final Collection<Target> targets) {
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
final Collection<JpaTarget> toUpdate = (Collection) targets;
|
||||
|
||||
toUpdate.forEach(target -> target.setNew(false));
|
||||
|
||||
return new ArrayList<>(targetRepository.save(toUpdate));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public void deleteTargets(final Long... targetIDs) {
|
||||
// we need to select the target IDs first to check the if the targetIDs
|
||||
// belonging to the
|
||||
// tenant! Delete statement are not automatically enhanced with the
|
||||
// @FilterDef of the
|
||||
// hibernate session.
|
||||
final List<Long> targetsForCurrentTenant = targetRepository.findAll(Lists.newArrayList(targetIDs)).stream()
|
||||
.map(Target::getId).collect(Collectors.toList());
|
||||
if (!targetsForCurrentTenant.isEmpty()) {
|
||||
targetInfoRepository.deleteByTargetIdIn(targetsForCurrentTenant);
|
||||
targetRepository.deleteByIdIn(targetsForCurrentTenant);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Target> findTargetByAssignedDistributionSet(final Long distributionSetID, final Pageable pageReq) {
|
||||
return targetRepository.findByAssignedDistributionSetId(pageReq, distributionSetID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Target> findTargetByAssignedDistributionSet(final Long distributionSetID, final String rsqlParam,
|
||||
final Pageable pageReq) {
|
||||
|
||||
final Specification<JpaTarget> spec = RSQLUtility.parse(rsqlParam, TargetFields.class);
|
||||
|
||||
return convertPage(
|
||||
targetRepository
|
||||
.findAll((Specification<JpaTarget>) (root, query,
|
||||
cb) -> cb.and(TargetSpecifications.hasAssignedDistributionSet(distributionSetID)
|
||||
.toPredicate(root, query, cb), spec.toPredicate(root, query, cb)),
|
||||
pageReq),
|
||||
pageReq);
|
||||
}
|
||||
|
||||
private static Page<Target> convertPage(final Page<JpaTarget> findAll, final Pageable pageable) {
|
||||
return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements());
|
||||
}
|
||||
|
||||
private static Slice<Target> convertPage(final Slice<JpaTarget> findAll, final Pageable pageable) {
|
||||
return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Target> findTargetByInstalledDistributionSet(final Long distributionSetID, final Pageable pageReq) {
|
||||
return targetRepository.findByTargetInfoInstalledDistributionSetId(pageReq, distributionSetID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Target> findTargetByInstalledDistributionSet(final Long distributionSetId, final String rsqlParam,
|
||||
final Pageable pageable) {
|
||||
|
||||
final Specification<JpaTarget> spec = RSQLUtility.parse(rsqlParam, TargetFields.class);
|
||||
|
||||
return convertPage(
|
||||
targetRepository
|
||||
.findAll((Specification<JpaTarget>) (root, query,
|
||||
cb) -> cb.and(TargetSpecifications.hasInstalledDistributionSet(distributionSetId)
|
||||
.toPredicate(root, query, cb), spec.toPredicate(root, query, cb)),
|
||||
pageable),
|
||||
pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Target> findTargetByUpdateStatus(final Pageable pageable, final TargetUpdateStatus status) {
|
||||
return targetRepository.findByTargetInfoUpdateStatus(pageable, status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Target> findTargetByFilters(final Pageable pageable, final Collection<TargetUpdateStatus> status,
|
||||
final String searchText, final Long installedOrAssignedDistributionSetId,
|
||||
final Boolean selectTargetWithNoTag, final String... tagNames) {
|
||||
final List<Specification<JpaTarget>> specList = buildSpecificationList(status, searchText,
|
||||
installedOrAssignedDistributionSetId, selectTargetWithNoTag, true, tagNames);
|
||||
return findByCriteriaAPI(pageable, specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countTargetByFilters(final Collection<TargetUpdateStatus> status, final String searchText,
|
||||
final Long installedOrAssignedDistributionSetId, final Boolean selectTargetWithNoTag,
|
||||
final String... tagNames) {
|
||||
final List<Specification<JpaTarget>> specList = buildSpecificationList(status, searchText,
|
||||
installedOrAssignedDistributionSetId, selectTargetWithNoTag, true, tagNames);
|
||||
return countByCriteriaAPI(specList);
|
||||
}
|
||||
|
||||
private static List<Specification<JpaTarget>> buildSpecificationList(final Collection<TargetUpdateStatus> status,
|
||||
final String searchText, final Long installedOrAssignedDistributionSetId,
|
||||
final Boolean selectTargetWithNoTag, final boolean fetch, final String... tagNames) {
|
||||
final List<Specification<JpaTarget>> specList = new ArrayList<>();
|
||||
if (status != null && !status.isEmpty()) {
|
||||
specList.add(TargetSpecifications.hasTargetUpdateStatus(status, fetch));
|
||||
}
|
||||
if (installedOrAssignedDistributionSetId != null) {
|
||||
specList.add(
|
||||
TargetSpecifications.hasInstalledOrAssignedDistributionSet(installedOrAssignedDistributionSetId));
|
||||
}
|
||||
if (!Strings.isNullOrEmpty(searchText)) {
|
||||
specList.add(TargetSpecifications.likeNameOrDescriptionOrIp(searchText));
|
||||
}
|
||||
if (selectTargetWithNoTag != null && (selectTargetWithNoTag || (tagNames != null && tagNames.length > 0))) {
|
||||
specList.add(TargetSpecifications.hasTags(tagNames, selectTargetWithNoTag));
|
||||
}
|
||||
return specList;
|
||||
}
|
||||
|
||||
private Slice<Target> findByCriteriaAPI(final Pageable pageable, final List<Specification<JpaTarget>> specList) {
|
||||
if (specList == null || specList.isEmpty()) {
|
||||
return convertPage(criteriaNoCountDao.findAll(pageable, JpaTarget.class), pageable);
|
||||
}
|
||||
return convertPage(
|
||||
criteriaNoCountDao.findAll(SpecificationsBuilder.combineWithAnd(specList), pageable, JpaTarget.class),
|
||||
pageable);
|
||||
}
|
||||
|
||||
private Long countByCriteriaAPI(final List<Specification<JpaTarget>> specList) {
|
||||
if (specList == null || specList.isEmpty()) {
|
||||
return targetRepository.count();
|
||||
}
|
||||
|
||||
return targetRepository.count(SpecificationsBuilder.combineWithAnd(specList));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public TargetTagAssignmentResult toggleTagAssignment(final Collection<Target> targets, final TargetTag tag) {
|
||||
return toggleTagAssignment(
|
||||
targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList()), tag.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public TargetTagAssignmentResult toggleTagAssignment(final Collection<String> targetIds, final String tagName) {
|
||||
final TargetTag tag = targetTagRepository.findByNameEquals(tagName);
|
||||
final List<Target> alreadyAssignedTargets = targetRepository.findByTagNameAndControllerIdIn(tagName, targetIds);
|
||||
final List<JpaTarget> allTargets = targetRepository
|
||||
.findAll(TargetSpecifications.byControllerIdWithStatusAndTagsInJoin(targetIds));
|
||||
|
||||
// all are already assigned -> unassign
|
||||
if (alreadyAssignedTargets.size() == allTargets.size()) {
|
||||
alreadyAssignedTargets.forEach(target -> target.getTags().remove(tag));
|
||||
final TargetTagAssignmentResult result = new TargetTagAssignmentResult(0, 0, alreadyAssignedTargets.size(),
|
||||
Collections.emptyList(), alreadyAssignedTargets, tag);
|
||||
|
||||
afterCommit.afterCommit(() -> eventBus.post(new TargetTagAssigmentResultEvent(result)));
|
||||
return result;
|
||||
}
|
||||
|
||||
allTargets.removeAll(alreadyAssignedTargets);
|
||||
// some or none are assigned -> assign
|
||||
allTargets.forEach(target -> target.getTags().add(tag));
|
||||
final TargetTagAssignmentResult result = new TargetTagAssignmentResult(alreadyAssignedTargets.size(),
|
||||
allTargets.size(), 0, new ArrayList<>(targetRepository.save(allTargets)), Collections.emptyList(), tag);
|
||||
|
||||
afterCommit.afterCommit(() -> eventBus.post(new TargetTagAssigmentResultEvent(result)));
|
||||
|
||||
// no reason to persist the tag
|
||||
entityManager.detach(tag);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public List<Target> assignTag(final Collection<String> targetIds, final TargetTag tag) {
|
||||
final List<JpaTarget> allTargets = targetRepository
|
||||
.findAll(TargetSpecifications.byControllerIdWithStatusAndTagsInJoin(targetIds));
|
||||
|
||||
allTargets.forEach(target -> target.getTags().add(tag));
|
||||
final List<Target> save = new ArrayList<>(targetRepository.save(allTargets));
|
||||
|
||||
afterCommit.afterCommit(() -> {
|
||||
final TargetTagAssignmentResult assigmentResult = new TargetTagAssignmentResult(0, save.size(), 0, save,
|
||||
Collections.emptyList(), tag);
|
||||
eventBus.post(new TargetTagAssigmentResultEvent(assigmentResult));
|
||||
});
|
||||
|
||||
return save;
|
||||
}
|
||||
|
||||
private List<Target> unAssignTag(final Collection<Target> targets, final TargetTag tag) {
|
||||
final Collection<JpaTarget> toUnassign = (Collection) targets;
|
||||
|
||||
toUnassign.forEach(target -> target.getTags().remove(tag));
|
||||
|
||||
final List<Target> save = new ArrayList<>(targetRepository.save(toUnassign));
|
||||
afterCommit.afterCommit(() -> {
|
||||
final TargetTagAssignmentResult assigmentResult = new TargetTagAssignmentResult(0, 0, save.size(),
|
||||
Collections.emptyList(), save, tag);
|
||||
eventBus.post(new TargetTagAssigmentResultEvent(assigmentResult));
|
||||
});
|
||||
return save;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public List<Target> unAssignAllTargetsByTag(final TargetTag tag) {
|
||||
return unAssignTag(tag.getAssignedToTargets(), tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public Target unAssignTag(final String controllerID, final TargetTag targetTag) {
|
||||
// TODO : optimize this, findone?
|
||||
final List<Target> allTargets = new ArrayList<>(targetRepository
|
||||
.findAll(TargetSpecifications.byControllerIdWithStatusAndTagsInJoin(Arrays.asList(controllerID))));
|
||||
final List<Target> unAssignTag = unAssignTag(allTargets, targetTag);
|
||||
return unAssignTag.isEmpty() ? null : unAssignTag.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Target> findTargetsAllOrderByLinkedDistributionSet(final Pageable pageable,
|
||||
final Long orderByDistributionId, final Long filterByDistributionId,
|
||||
final Collection<TargetUpdateStatus> filterByStatus, final String filterBySearchText,
|
||||
final Boolean selectTargetWithNoTag, final String... filterByTagNames) {
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
final CriteriaQuery<JpaTarget> query = cb.createQuery(JpaTarget.class);
|
||||
final Root<JpaTarget> targetRoot = query.from(JpaTarget.class);
|
||||
|
||||
// necessary joins for the select
|
||||
final Join<JpaTarget, JpaTargetInfo> targetInfo = (Join<JpaTarget, JpaTargetInfo>) targetRoot
|
||||
.fetch(JpaTarget_.targetInfo, JoinType.LEFT);
|
||||
|
||||
// select case expression to retrieve the case value as a column to be
|
||||
// able to order based on
|
||||
// this column, installed first,...
|
||||
final Expression<Object> selectCase = cb.selectCase()
|
||||
.when(cb.equal(targetInfo.get(JpaTargetInfo_.installedDistributionSet).get(JpaDistributionSet_.id),
|
||||
orderByDistributionId), 1)
|
||||
.when(cb.equal(targetRoot.get(JpaTarget_.assignedDistributionSet).get(JpaDistributionSet_.id),
|
||||
orderByDistributionId), 2)
|
||||
.otherwise(100);
|
||||
// multiselect statement order by the select case and controllerId
|
||||
query.distinct(true);
|
||||
// build the specifications and then to predicates necessary by the
|
||||
// given filters
|
||||
final Predicate[] specificationsForMultiSelect = specificationsToPredicate(
|
||||
buildSpecificationList(filterByStatus, filterBySearchText, filterByDistributionId,
|
||||
selectTargetWithNoTag, true, filterByTagNames),
|
||||
targetRoot, query, cb);
|
||||
|
||||
// if we have some predicates then add it to the where clause of the
|
||||
// multiselect
|
||||
if (specificationsForMultiSelect.length > 0) {
|
||||
query.where(specificationsForMultiSelect);
|
||||
}
|
||||
// add the order to the multi select first based on the selectCase
|
||||
query.orderBy(cb.asc(selectCase), cb.desc(targetRoot.get(JpaTarget_.id)));
|
||||
// the result is a Object[] due the fact that the selectCase is an extra
|
||||
// column, so it cannot
|
||||
// be mapped directly to a Target entity because the selectCase is not a
|
||||
// attribute of the
|
||||
// Target entity, the the Object array contains the Target on the first
|
||||
// index (case of the
|
||||
// multiselect order) of the array and
|
||||
// the 2nd contains the selectCase int value.
|
||||
final int pageSize = pageable.getPageSize();
|
||||
final List<JpaTarget> resultList = entityManager.createQuery(query).setFirstResult(pageable.getOffset())
|
||||
.setMaxResults(pageSize + 1).getResultList();
|
||||
final boolean hasNext = resultList.size() > pageSize;
|
||||
return new SliceImpl<>(new ArrayList<>(resultList), pageable, hasNext);
|
||||
}
|
||||
|
||||
private static Predicate[] specificationsToPredicate(final List<Specification<JpaTarget>> specifications,
|
||||
final Root<JpaTarget> root, final CriteriaQuery<?> query, final CriteriaBuilder cb) {
|
||||
final Predicate[] predicates = new Predicate[specifications.size()];
|
||||
for (int index = 0; index < predicates.length; index++) {
|
||||
predicates[index] = specifications.get(index).toPredicate(root, query, cb);
|
||||
}
|
||||
return predicates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countTargetByAssignedDistributionSet(final Long distId) {
|
||||
return targetRepository.countByAssignedDistributionSetId(distId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countTargetByInstalledDistributionSet(final Long distId) {
|
||||
return targetRepository.countByTargetInfoInstalledDistributionSetId(distId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TargetIdName> findAllTargetIds() {
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
final CriteriaQuery<TargetIdName> query = cb.createQuery(TargetIdName.class);
|
||||
final Root<JpaTarget> targetRoot = query.from(JpaTarget.class);
|
||||
return entityManager.createQuery(query.multiselect(targetRoot.get(JpaTarget_.id),
|
||||
targetRoot.get(JpaTarget_.controllerId), targetRoot.get(JpaTarget_.name))).getResultList();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TargetIdName> findAllTargetIdsByFilters(final Pageable pageRequest,
|
||||
final Collection<TargetUpdateStatus> filterByStatus, final String filterBySearchText,
|
||||
final Long installedOrAssignedDistributionSetId, final Boolean selectTargetWithNoTag,
|
||||
final String... filterByTagNames) {
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
|
||||
final Root<JpaTarget> targetRoot = query.from(JpaTarget.class);
|
||||
List<Object[]> resultList;
|
||||
|
||||
String sortProperty = JpaTarget_.id.getName();
|
||||
if (pageRequest.getSort() != null && pageRequest.getSort().iterator().hasNext()) {
|
||||
sortProperty = pageRequest.getSort().iterator().next().getProperty();
|
||||
}
|
||||
|
||||
final CriteriaQuery<Object[]> multiselect = query.multiselect(targetRoot.get(JpaTarget_.id),
|
||||
targetRoot.get(JpaTarget_.controllerId), targetRoot.get(JpaTarget_.name), targetRoot.get(sortProperty));
|
||||
|
||||
final Predicate[] specificationsForMultiSelect = specificationsToPredicate(
|
||||
buildSpecificationList(filterByStatus, filterBySearchText, installedOrAssignedDistributionSetId,
|
||||
selectTargetWithNoTag, false, filterByTagNames),
|
||||
targetRoot, multiselect, cb);
|
||||
|
||||
// if we have some predicates then add it to the where clause of the
|
||||
// multiselect
|
||||
if (specificationsForMultiSelect.length > 0) {
|
||||
multiselect.where(specificationsForMultiSelect);
|
||||
}
|
||||
|
||||
resultList = getTargetIdNameResultSet(pageRequest, cb, targetRoot, multiselect);
|
||||
return resultList.parallelStream().map(o -> new TargetIdName((long) o[0], o[1].toString(), o[2].toString()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TargetIdName> findAllTargetIdsByTargetFilterQuery(final Pageable pageRequest,
|
||||
final TargetFilterQuery targetFilterQuery) {
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
|
||||
final Root<JpaTarget> targetRoot = query.from(JpaTarget.class);
|
||||
|
||||
String sortProperty = JpaTarget_.id.getName();
|
||||
if (pageRequest.getSort() != null && pageRequest.getSort().iterator().hasNext()) {
|
||||
sortProperty = pageRequest.getSort().iterator().next().getProperty();
|
||||
}
|
||||
|
||||
final CriteriaQuery<Object[]> multiselect = query.multiselect(targetRoot.get(JpaTarget_.id),
|
||||
targetRoot.get(JpaTarget_.controllerId), targetRoot.get(JpaTarget_.name), targetRoot.get(sortProperty));
|
||||
|
||||
final Specification<JpaTarget> spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class);
|
||||
final List<Specification<JpaTarget>> specList = new ArrayList<>();
|
||||
specList.add(spec);
|
||||
|
||||
final Predicate[] specificationsForMultiSelect = specificationsToPredicate(specList, targetRoot, multiselect,
|
||||
cb);
|
||||
|
||||
// if we have some predicates then add it to the where clause of the
|
||||
// multiselect
|
||||
if (specificationsForMultiSelect.length > 0) {
|
||||
multiselect.where(specificationsForMultiSelect);
|
||||
}
|
||||
final List<Object[]> resultList = getTargetIdNameResultSet(pageRequest, cb, targetRoot, multiselect);
|
||||
return resultList.parallelStream().map(o -> new TargetIdName((long) o[0], o[1].toString(), o[2].toString()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
void destroy() {
|
||||
eventBus.unregister(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@CacheEvict(value = { "targetsCreatedOverPeriod" }, allEntries = true)
|
||||
public Target createTarget(final Target t, final TargetUpdateStatus status, final Long lastTargetQuery,
|
||||
final URI address) {
|
||||
final JpaTarget target = (JpaTarget) t;
|
||||
|
||||
if (targetRepository.findByControllerId(target.getControllerId()) != null) {
|
||||
throw new EntityAlreadyExistsException(target.getControllerId());
|
||||
}
|
||||
|
||||
target.setNew(true);
|
||||
final JpaTarget savedTarget = targetRepository.save(target);
|
||||
final JpaTargetInfo targetInfo = (JpaTargetInfo) savedTarget.getTargetInfo();
|
||||
targetInfo.setUpdateStatus(status);
|
||||
if (lastTargetQuery != null) {
|
||||
targetInfo.setLastTargetQuery(lastTargetQuery);
|
||||
}
|
||||
if (address != null) {
|
||||
targetInfo.setAddress(address.toString());
|
||||
}
|
||||
targetInfo.setNew(true);
|
||||
final Target targetToReturn = targetInfoRepository.save(targetInfo).getTarget();
|
||||
targetInfo.setNew(false);
|
||||
return targetToReturn;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@CacheEvict(value = { "targetsCreatedOverPeriod" }, allEntries = true)
|
||||
public Target createTarget(final Target target) {
|
||||
return createTarget(target, TargetUpdateStatus.UNKNOWN, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public List<Target> createTargets(final Collection<Target> targets) {
|
||||
if (!targets.isEmpty() && targetRepository.countByControllerIdIn(
|
||||
targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList())) > 0) {
|
||||
throw new EntityAlreadyExistsException();
|
||||
}
|
||||
final List<Target> savedTargets = new ArrayList<>();
|
||||
for (final Target t : targets) {
|
||||
final Target myTarget = createTarget(t);
|
||||
savedTargets.add(myTarget);
|
||||
}
|
||||
return savedTargets;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public List<Target> createTargets(final Collection<Target> targets, final TargetUpdateStatus status,
|
||||
final Long lastTargetQuery, final URI address) {
|
||||
if (targetRepository.countByControllerIdIn(
|
||||
targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList())) > 0) {
|
||||
throw new EntityAlreadyExistsException();
|
||||
}
|
||||
final List<Target> savedTargets = new ArrayList<>();
|
||||
for (final Target t : targets) {
|
||||
final Target myTarget = createTarget(t, status, lastTargetQuery, address);
|
||||
savedTargets.add(myTarget);
|
||||
}
|
||||
return savedTargets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Target> findTargetsByTag(final String tagName) {
|
||||
final JpaTargetTag tag = targetTagRepository.findByNameEquals(tagName);
|
||||
return new ArrayList<>(targetRepository.findByTag(tag));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countTargetByTargetFilterQuery(final TargetFilterQuery targetFilterQuery) {
|
||||
final Specification<JpaTarget> specs = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class);
|
||||
return targetRepository.count(specs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countTargetByTargetFilterQuery(final String targetFilterQuery) {
|
||||
final Specification<JpaTarget> specs = RSQLUtility.parse(targetFilterQuery, TargetFields.class);
|
||||
return targetRepository.count(specs);
|
||||
}
|
||||
|
||||
private List<Object[]> getTargetIdNameResultSet(final Pageable pageRequest, final CriteriaBuilder cb,
|
||||
final Root<JpaTarget> targetRoot, final CriteriaQuery<Object[]> multiselect) {
|
||||
List<Object[]> resultList;
|
||||
if (pageRequest.getSort() != null) {
|
||||
final List<Order> orders = new ArrayList<>();
|
||||
final Sort sort = pageRequest.getSort();
|
||||
for (final Sort.Order sortOrder : sort) {
|
||||
if (sortOrder.getDirection() == Direction.ASC) {
|
||||
orders.add(cb.asc(targetRoot.get(sortOrder.getProperty())));
|
||||
} else {
|
||||
orders.add(cb.desc(targetRoot.get(sortOrder.getProperty())));
|
||||
}
|
||||
}
|
||||
multiselect.orderBy(orders);
|
||||
resultList = entityManager.createQuery(multiselect).setFirstResult(pageRequest.getOffset())
|
||||
.setMaxResults(pageRequest.getPageSize()).getResultList();
|
||||
} else {
|
||||
resultList = entityManager.createQuery(multiselect).getResultList();
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public Target generateTarget(final String controllerId) {
|
||||
return new JpaTarget(controllerId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* 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.jpa.model.JpaTenantConfiguration;
|
||||
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().global(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().global(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);
|
||||
|
||||
JpaTenantConfiguration tenantConfiguration = tenantConfigurationRepository
|
||||
.findByKey(configurationKey.getKeyName());
|
||||
|
||||
if (tenantConfiguration == null) {
|
||||
tenantConfiguration = new JpaTenantConfiguration(configurationKey.getKeyName(), value.toString());
|
||||
} else {
|
||||
tenantConfiguration.setValue(value.toString());
|
||||
}
|
||||
|
||||
final JpaTenantConfiguration updatedTenantConfiguration = tenantConfigurationRepository
|
||||
.save(tenantConfiguration);
|
||||
|
||||
final Class<T> clazzT = (Class<T>) value.getClass();
|
||||
|
||||
return TenantConfigurationValue.<T> builder().global(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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* 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.Optional;
|
||||
|
||||
import org.eclipse.hawkbit.report.model.TenantUsage;
|
||||
import org.eclipse.hawkbit.repository.TenantStatsManagement;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Management service for statistics of a single tenant.
|
||||
*
|
||||
*/
|
||||
@Validated
|
||||
@Service
|
||||
public class JpaTenantStatsManagement implements TenantStatsManagement {
|
||||
|
||||
@Autowired
|
||||
private TargetRepository targetRepository;
|
||||
|
||||
@Autowired
|
||||
private LocalArtifactRepository artifactRepository;
|
||||
|
||||
@Autowired
|
||||
private ActionRepository actionRepository;
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public TenantUsage getStatsOfTenant(final String tenant) {
|
||||
final TenantUsage result = new TenantUsage(tenant);
|
||||
|
||||
result.setTargets(targetRepository.count());
|
||||
|
||||
final Long artifacts = artifactRepository.countBySoftwareModuleDeleted(false);
|
||||
result.setArtifacts(artifacts);
|
||||
|
||||
final Optional<Long> artifactsSize = artifactRepository.getSumOfUndeletedArtifactSize();
|
||||
if (artifactsSize.isPresent()) {
|
||||
result.setOverallArtifactVolumeInBytes(artifactsSize.get());
|
||||
}
|
||||
|
||||
result.setActions(actionRepository.count());
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* 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.Optional;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaLocalArtifact;
|
||||
import org.eclipse.hawkbit.repository.model.LocalArtifact;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link LocalArtifact} repository.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface LocalArtifactRepository extends BaseEntityRepository<JpaLocalArtifact, Long> {
|
||||
|
||||
/**
|
||||
* Counts artifacts size where the related software module is not
|
||||
* deleted/archived.
|
||||
*
|
||||
* @return sum of artifacts size in bytes
|
||||
*/
|
||||
@Query("SELECT SUM(la.size) FROM JpaLocalArtifact la WHERE la.softwareModule.deleted = 0")
|
||||
Optional<Long> getSumOfUndeletedArtifactSize();
|
||||
|
||||
/**
|
||||
* Counts artifacts where the related software module is deleted/archived.
|
||||
*
|
||||
* @param deleted
|
||||
* to true for counting the deleted artifacts
|
||||
*
|
||||
* @return number of artifacts
|
||||
*/
|
||||
Long countBySoftwareModuleDeleted(boolean deleted);
|
||||
|
||||
/**
|
||||
* Searches for a {@link LocalArtifact} based on given gridFsFileName.
|
||||
*
|
||||
* @param gridFsFileName
|
||||
* to search
|
||||
* @return list of {@link LocalArtifact}s.
|
||||
*/
|
||||
List<LocalArtifact> findByGridFsFileName(String gridFsFileName);
|
||||
|
||||
/**
|
||||
* Searches for a {@link LocalArtifact} based on given gridFsFileName.
|
||||
*
|
||||
* @param gridFsFileName
|
||||
* to search
|
||||
* @return {@link LocalArtifact} the first in the result list
|
||||
*/
|
||||
JpaLocalArtifact findFirstByGridFsFileName(String gridFsFileName);
|
||||
|
||||
/**
|
||||
* Searches for a {@link LocalArtifact} based user provided filename at
|
||||
* upload.
|
||||
*
|
||||
* @param filename
|
||||
* to search
|
||||
* @return list of {@link LocalArtifact}.
|
||||
*/
|
||||
List<LocalArtifact> findByFilename(String filename);
|
||||
|
||||
/**
|
||||
* Searches for local artifact for a base software module.
|
||||
*
|
||||
* @param pageReq
|
||||
* Pageable
|
||||
* @param swId
|
||||
* software module id
|
||||
*
|
||||
* @return Page<LocalArtifact>
|
||||
*/
|
||||
Page<LocalArtifact> findBySoftwareModuleId(Pageable pageReq, final Long swId);
|
||||
|
||||
/**
|
||||
* Searches for a {@link LocalArtifact} based user provided filename at
|
||||
* upload and selected software module id.
|
||||
*
|
||||
* @param filename
|
||||
* to search
|
||||
* @param softwareModuleId
|
||||
* selected software module id
|
||||
* @return list of {@link LocalArtifact}.
|
||||
*/
|
||||
List<LocalArtifact> findByFilenameAndSoftwareModuleId(final String filename, final Long softwareModuleId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* 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.util.List;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.TypedQuery;
|
||||
|
||||
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.domain.Slice;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* Workaround as spring data does not provide a {@link Slice} based
|
||||
* {@link JpaRepository#findAll()}.
|
||||
*
|
||||
*/
|
||||
@Repository
|
||||
public class NoCountPagingRepository {
|
||||
|
||||
@Autowired
|
||||
protected EntityManager em;
|
||||
|
||||
/**
|
||||
* Searches without the need for an extra count query.
|
||||
*
|
||||
* @param spec
|
||||
* to search for
|
||||
* @param pageable
|
||||
* information
|
||||
* @param domainClass
|
||||
* of the {@link Entity}
|
||||
*
|
||||
* @return {@link Slice} of data
|
||||
*
|
||||
* @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findAll(org.springframework
|
||||
* .data.jpa.domain.Specification,
|
||||
* org.springframework.data.domain.Pageable)
|
||||
*/
|
||||
public <T, I extends Serializable> Slice<T> findAll(final Specification<T> spec, final Pageable pageable,
|
||||
final Class<T> domainClass) {
|
||||
final SimpleJpaNoCountRepository<T, I> noCountDao = new SimpleJpaNoCountRepository<>(domainClass, em);
|
||||
return noCountDao.findAll(spec, pageable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches without the need for an extra count query.
|
||||
*
|
||||
* @param pageable
|
||||
* information
|
||||
* @param domainClass
|
||||
* of the {@link Entity}
|
||||
*
|
||||
* @return {@link Slice} of data
|
||||
*
|
||||
* @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findAll(org.springframework
|
||||
* .data.jpa.domain.Specification,
|
||||
* org.springframework.data.domain.Pageable)
|
||||
*/
|
||||
public <T, I extends Serializable> Slice<T> findAll(final Pageable pageable, final Class<T> domainClass) {
|
||||
final SimpleJpaNoCountRepository<T, I> noCountDao = new SimpleJpaNoCountRepository<>(domainClass, em);
|
||||
return noCountDao.findAll(pageable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Repository implementation with disabled count query.
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param <T>
|
||||
* entity type
|
||||
* @param <I>
|
||||
* key or ID type
|
||||
*/
|
||||
public static class SimpleJpaNoCountRepository<T, I extends Serializable> extends SimpleJpaRepository<T, I> {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param domainClass
|
||||
* of the {@link Entity}
|
||||
* @param em
|
||||
* {@link EntityManager} instance for the queries
|
||||
*/
|
||||
public SimpleJpaNoCountRepository(final Class<T> domainClass, final EntityManager em) {
|
||||
super(domainClass, em);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Page<T> readPage(final TypedQuery<T> query, final Pageable pageable, final Specification<T> spec) {
|
||||
query.setFirstResult(pageable.getOffset());
|
||||
query.setMaxResults(pageable.getPageSize());
|
||||
|
||||
final List<T> content = query.getResultList();
|
||||
|
||||
return new PageImpl<>(content, pageable, content.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* 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 org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* The repository interface for the {@link RolloutGroup} model.
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface RolloutGroupRepository
|
||||
extends BaseEntityRepository<JpaRolloutGroup, Long>, JpaSpecificationExecutor<JpaRolloutGroup> {
|
||||
|
||||
/**
|
||||
* Retrieves all {@link RolloutGroup} referring a specific rollout in the
|
||||
* order of creating them. ID ASC.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout the rolloutgroups belong to
|
||||
* @return the rollout groups belonging to a rollout ordered by ID ASC.
|
||||
*/
|
||||
List<JpaRolloutGroup> findByRolloutOrderByIdAsc(final JpaRollout rollout);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link RolloutGroup} referring a specific rollout in a
|
||||
* specific {@link RolloutGroupStatus}.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout the rolloutgroup belong to
|
||||
* @param status
|
||||
* the status of the rollout groups
|
||||
* @return the rollout groups belonging to a rollout in a specific status
|
||||
*/
|
||||
List<JpaRolloutGroup> findByRolloutAndStatus(final Rollout rollout, final RolloutGroupStatus status);
|
||||
|
||||
/**
|
||||
* Counts all {@link RolloutGroup} referring a specific rollout.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout the rolloutgroup belong to
|
||||
* @return the count of the rollout groups for a specific rollout
|
||||
*/
|
||||
Long countByRollout(final JpaRollout rollout);
|
||||
|
||||
/**
|
||||
* Counts all {@link RolloutGroup} referring a specific rollout in a
|
||||
* specific {@link RolloutGroupStatus}.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout the rolloutgroup belong to
|
||||
* @param rolloutGroupStatus
|
||||
* the status of the rollout groups
|
||||
* @return the count of rollout groups belonging to a rollout in a specific
|
||||
* status
|
||||
*/
|
||||
Long countByRolloutAndStatus(JpaRollout rollout, RolloutGroupStatus rolloutGroupStatus);
|
||||
|
||||
/**
|
||||
* Counts all {@link RolloutGroup} referring a specific rollout in specific
|
||||
* {@link RolloutGroupStatus}s. An in-clause statement does not work with
|
||||
* the spring-data, so this is specific usecase regarding to the
|
||||
* rollout-management to find out rolloutgroups which are in specific
|
||||
* states.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout the rolloutgroup belong to
|
||||
* @param rolloutGroupStatus1
|
||||
* the status of the rollout groups
|
||||
* @param rolloutGroupStatus2
|
||||
* the status of the rollout groups
|
||||
* @return the count of rollout groups belonging to a rollout in specific
|
||||
* status
|
||||
*/
|
||||
@Query("SELECT COUNT(r.id) FROM JpaRolloutGroup r WHERE r.rollout = :rollout and (r.status = :status1 or r.status = :status2)")
|
||||
Long countByRolloutAndStatusOrStatus(@Param("rollout") JpaRollout rollout,
|
||||
@Param("status1") RolloutGroupStatus rolloutGroupStatus1,
|
||||
@Param("status2") RolloutGroupStatus rolloutGroupStatus2);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link RolloutGroup} for a specific parent in a specific
|
||||
* status. Retrieves the child rolloutgroup for a specific status.
|
||||
*
|
||||
* @param rolloutGroup
|
||||
* the parent rolloutgroup
|
||||
* @param status
|
||||
* the status of the rolloutgroups
|
||||
* @return The child {@link RolloutGroup}s in a specific status
|
||||
*/
|
||||
List<JpaRolloutGroup> findByParentAndStatus(JpaRolloutGroup rolloutGroup, RolloutGroupStatus status);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link RolloutGroup} for a specific rollout and status not
|
||||
* having ordered by ID DESC, latest top.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout the rolloutgroup belong to
|
||||
* @param notStatus
|
||||
* the status which the rolloutgroup should not have
|
||||
* @return rolloutgroup referring to a rollout and not having a specific
|
||||
* status ordered by ID DESC.
|
||||
*/
|
||||
List<JpaRolloutGroup> findByRolloutAndStatusNotOrderByIdDesc(JpaRollout rollout, RolloutGroupStatus notStatus);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link RolloutGroup} for a specific rollout.
|
||||
*
|
||||
* @param rolloutId
|
||||
* the ID of the rollout to find the rollout groups
|
||||
* @param page
|
||||
* the page request to sort, limit the result
|
||||
* @return a page of found {@link RolloutGroup} or {@code empty}.
|
||||
*/
|
||||
Page<JpaRolloutGroup> findByRolloutId(final Long rolloutId, Pageable page);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* 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 org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* The repository interface for the {@link Rollout} model.
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface RolloutRepository
|
||||
extends BaseEntityRepository<JpaRollout, Long>, JpaSpecificationExecutor<JpaRollout> {
|
||||
|
||||
/**
|
||||
* Updates the {@code lastCheck} field of the {@link Rollout} for rollouts
|
||||
* in a specific status and only if the {@code lastCheck} is overdue.
|
||||
*
|
||||
* @param lastCheck
|
||||
* the time in milliseconds to set to the lastCheck column
|
||||
* @param delay
|
||||
* the delay between last checks
|
||||
* @param status
|
||||
* the status which the rollout should have to update the last
|
||||
* check field
|
||||
* @return the count of the updated rows. Zero if no row has been updated
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Query("UPDATE JpaRollout r SET r.lastCheck = :lastCheck WHERE r.lastCheck < (:lastCheck - :delay) AND r.status=:status")
|
||||
int updateLastCheck(@Param("lastCheck") final long lastCheck, @Param("delay") final long delay,
|
||||
@Param("status") final RolloutStatus status);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link Rollout} for a specific {@code lastCheck} time and
|
||||
* for a specific status.
|
||||
*
|
||||
* @param lastCheck
|
||||
* the lastCheck time to find the specific rollout.
|
||||
* @param status
|
||||
* the status of the rollout to find
|
||||
* @return the list of {@link Rollout} for specific lastCheck time and
|
||||
* status
|
||||
*/
|
||||
List<JpaRollout> findByLastCheckAndStatus(long lastCheck, RolloutStatus status);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link Rollout} for a specific {@code name}
|
||||
*
|
||||
* @param name
|
||||
* the rollout name
|
||||
* @return {@link Rollout} for specific name
|
||||
*/
|
||||
Page<JpaRollout> findByName(final Pageable pageable, String name);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link Rollout} for a specific {@code name}
|
||||
*
|
||||
* @param name
|
||||
* the rollout name
|
||||
* @return {@link Rollout} for specific name
|
||||
*/
|
||||
JpaRollout findByName(String name);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link Rollout} for a specific status.
|
||||
*
|
||||
* @param status
|
||||
* the status of the rollouts to retrieve
|
||||
* @return a list of {@link Rollout} having the given status
|
||||
*/
|
||||
List<JpaRollout> findByStatus(final RolloutStatus status);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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.jpa.model.RolloutTargetGroup;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroupId;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Spring data repository for {@link RolloutTargetGroup}.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface RolloutTargetGroupRepository
|
||||
extends CrudRepository<RolloutTargetGroup, RolloutTargetGroupId>, JpaSpecificationExecutor<RolloutTargetGroup> {
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 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 org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.SwMetadataCompositeKey;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link SoftwareModuleMetadata} repository.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface SoftwareModuleMetadataRepository
|
||||
extends PagingAndSortingRepository<JpaSoftwareModuleMetadata, SwMetadataCompositeKey>,
|
||||
JpaSpecificationExecutor<JpaSoftwareModuleMetadata> {
|
||||
|
||||
/**
|
||||
* Saves all given entities.
|
||||
*
|
||||
* @param entities
|
||||
* @return the saved entities
|
||||
* @throws IllegalArgumentException
|
||||
* in case the given entity is (@literal null}.
|
||||
*/
|
||||
@Override
|
||||
<S extends JpaSoftwareModuleMetadata> List<S> save(Iterable<S> entities);
|
||||
|
||||
/**
|
||||
* finds all software module meta data of the given software module id.
|
||||
*
|
||||
* @param swId
|
||||
* the ID of the software module to retrieve the meta data
|
||||
* @param pageable
|
||||
* the page request to page the result set
|
||||
* @return the paged result of all meta data of an given software module id
|
||||
*/
|
||||
Page<SoftwareModuleMetadata> findBySoftwareModuleId(final Long swId, Pageable pageable);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* 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 org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.EntityGraph;
|
||||
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link SoftwareModule} repository.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface SoftwareModuleRepository
|
||||
extends BaseEntityRepository<JpaSoftwareModule, Long>, JpaSpecificationExecutor<JpaSoftwareModule> {
|
||||
|
||||
/**
|
||||
* Counts all {@link SoftwareModule}s based on the given {@link Type}.
|
||||
*
|
||||
* @param type
|
||||
* to count for
|
||||
* @return number of {@link SoftwareModule}s
|
||||
*/
|
||||
Long countByType(JpaSoftwareModuleType type);
|
||||
|
||||
/**
|
||||
* Retrieves {@link SoftwareModule} by filtering on name AND version AND
|
||||
* type (which is unique per tenant.
|
||||
*
|
||||
* @param name
|
||||
* to be filtered on
|
||||
* @param version
|
||||
* to be filtered on
|
||||
* @param type
|
||||
* to be filtered on
|
||||
* @return the found {@link SoftwareModule} with the given name AND version
|
||||
* AND type
|
||||
*/
|
||||
JpaSoftwareModule findOneByNameAndVersionAndType(String name, String version, JpaSoftwareModuleType type);
|
||||
|
||||
/**
|
||||
* deletes the {@link SoftwareModule}s with the given IDs.
|
||||
*
|
||||
* @param modifiedAt
|
||||
* current timestamp
|
||||
* @param modifiedBy
|
||||
* user name of current auditor
|
||||
* @param ids
|
||||
* to be deleted
|
||||
*
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Query("UPDATE JpaSoftwareModule b SET b.deleted = 1, b.lastModifiedAt = :lastModifiedAt, b.lastModifiedBy = :lastModifiedBy WHERE b.id IN :ids")
|
||||
void deleteSoftwareModule(@Param("lastModifiedAt") Long modifiedAt, @Param("lastModifiedBy") String modifiedBy,
|
||||
@Param("ids") final Long... ids);
|
||||
|
||||
/**
|
||||
* @param pageable
|
||||
* the page request to page the result set
|
||||
* @param set
|
||||
* to search for
|
||||
* @return all {@link SoftwareModule}s that are assigned to given
|
||||
* {@link DistributionSet}.
|
||||
*/
|
||||
Page<SoftwareModule> findByAssignedTo(Pageable pageable, JpaDistributionSet set);
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param set
|
||||
* to search for
|
||||
* @return all {@link SoftwareModule}s that are assigned to given
|
||||
* {@link DistributionSet}
|
||||
*/
|
||||
@EntityGraph(value = "SoftwareModule.artifacts", type = EntityGraphType.LOAD)
|
||||
List<JpaSoftwareModule> findByAssignedTo(JpaDistributionSet set);
|
||||
|
||||
/**
|
||||
* @param pageable
|
||||
* the page request to page the result set
|
||||
* @param set
|
||||
* to search for
|
||||
* @param type
|
||||
* to filter
|
||||
* @return all {@link SoftwareModule}s that are assigned to given
|
||||
* {@link DistributionSet} filtered by {@link SoftwareModuleType}.
|
||||
*/
|
||||
Page<SoftwareModule> findByAssignedToAndType(Pageable pageable, JpaDistributionSet set, JpaSoftwareModuleType type);
|
||||
|
||||
/**
|
||||
* retrieves all software modules with a given {@link SoftwareModuleType}
|
||||
* and {@link SoftwareModule#getId()}.
|
||||
*
|
||||
* @param ids
|
||||
* to search for
|
||||
* @param type
|
||||
* to search for
|
||||
* @return {@link List} of found {@link SoftwareModule}s
|
||||
*/
|
||||
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
|
||||
@Query("SELECT sm FROM JpaSoftwareModule sm WHERE sm.id IN ?1 and sm.type = ?2")
|
||||
List<SoftwareModule> findByIdInAndType(Iterable<Long> ids, JpaSoftwareModuleType type);
|
||||
|
||||
@Override
|
||||
<S extends JpaSoftwareModule> List<S> save(Iterable<S> entities);
|
||||
|
||||
/**
|
||||
* retrieves all software modules with a given
|
||||
* {@link SoftwareModule#getId()}.
|
||||
*
|
||||
* @param ids
|
||||
* to search for
|
||||
* @return {@link List} of found {@link SoftwareModule}s
|
||||
*/
|
||||
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
|
||||
@Query("SELECT sm FROM JpaSoftwareModule sm WHERE sm.id IN ?1")
|
||||
List<JpaSoftwareModule> findByIdIn(Iterable<Long> ids);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 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.jpa.model.JpaSoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Repository for {@link SoftwareModuleType}.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface SoftwareModuleTypeRepository
|
||||
extends BaseEntityRepository<JpaSoftwareModuleType, Long>, JpaSpecificationExecutor<JpaSoftwareModuleType> {
|
||||
|
||||
/**
|
||||
* @param pageable
|
||||
* @param isDeleted
|
||||
* to <code>true</code> if only marked as deleted have to be
|
||||
* count or all undeleted.
|
||||
* @return found {@link SoftwareModuleType}s.
|
||||
*/
|
||||
Page<SoftwareModuleType> findByDeleted(Pageable pageable, boolean isDeleted);
|
||||
|
||||
/**
|
||||
* @param isDeleted
|
||||
* to <code>true</code> if only marked as deleted have to be
|
||||
* count or all undeleted.
|
||||
* @return number of {@link SoftwareModuleType}s in the repository.
|
||||
*/
|
||||
Long countByDeleted(boolean isDeleted);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param key
|
||||
* to search for
|
||||
* @return all {@link SoftwareModuleType}s in the repository with given
|
||||
* {@link SoftwareModuleType#getKey()}
|
||||
*/
|
||||
JpaSoftwareModuleType findByKey(String key);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name
|
||||
* to search for
|
||||
* @return all {@link SoftwareModuleType}s in the repository with given
|
||||
* {@link SoftwareModuleType#getName()}
|
||||
*/
|
||||
JpaSoftwareModuleType findByName(String name);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* 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.jpa.model.JpaTargetFilterQuery;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Spring data repositories for {@link TargetFilterQuery}s.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface TargetFilterQueryRepository
|
||||
extends BaseEntityRepository<JpaTargetFilterQuery, Long>, JpaSpecificationExecutor<JpaTargetFilterQuery> {
|
||||
|
||||
/**
|
||||
* Find customer target filter by name
|
||||
*
|
||||
* @param name
|
||||
* @return custom target filter
|
||||
*/
|
||||
TargetFilterQuery findByName(final String name);
|
||||
|
||||
/**
|
||||
* Find list of all custom target filters.
|
||||
*/
|
||||
@Override
|
||||
Page<JpaTargetFilterQuery> findAll();
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional
|
||||
<S extends JpaTargetFilterQuery> S save(S entity);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
|
||||
import org.eclipse.hawkbit.repository.model.TargetInfo;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Usually a JPA spring data repository to handle {@link TargetInfo} entity.
|
||||
* However, do to an eclipselink bug with spring boot now a regular interface
|
||||
* that is implemented by {@link EclipseLinkTargetInfoRepository}.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface TargetInfoRepository {
|
||||
|
||||
/**
|
||||
* Sets new TargetUpdateStatus of given target if is not already on that
|
||||
* value.
|
||||
*
|
||||
* @param status
|
||||
* to set
|
||||
* @param targets
|
||||
* to set it for
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Query("update JpaTargetInfo ti set ti.updateStatus = :status where ti.targetId in :targets and ti.updateStatus != :status")
|
||||
void setTargetUpdateStatus(@Param("status") TargetUpdateStatus status, @Param("targets") List<Long> targets);
|
||||
|
||||
/**
|
||||
* Save entity and evict cache with it.
|
||||
*
|
||||
* @param entity
|
||||
* to persists
|
||||
*
|
||||
* @return persisted or updated {@link Entity}
|
||||
*/
|
||||
@CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true)
|
||||
<S extends JpaTargetInfo> S save(S entity);
|
||||
|
||||
/**
|
||||
* Deletes info entries by ID.
|
||||
*
|
||||
* @param targetIDs
|
||||
* to delete
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true)
|
||||
void deleteByTargetIdIn(final Collection<Long> targetIDs);
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
/**
|
||||
* 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.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Tag;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.model.TargetWithActionStatus;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.EntityGraph;
|
||||
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link Target} repository.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface TargetRepository extends BaseEntityRepository<JpaTarget, Long>, JpaSpecificationExecutor<JpaTarget> {
|
||||
|
||||
/**
|
||||
* Loads {@link Target} including details {@link EntityGraph} by given ID.
|
||||
*
|
||||
* @param controllerID
|
||||
* to search for
|
||||
* @return found {@link Target} or <code>null</code> if not found.
|
||||
*/
|
||||
@EntityGraph(value = "Target.detail", type = EntityGraphType.LOAD)
|
||||
JpaTarget findByControllerId(String controllerID);
|
||||
|
||||
/**
|
||||
* Finds targets by given list of {@link Target#getControllerId()}s.
|
||||
*
|
||||
* @param controllerIDs
|
||||
* to serach for
|
||||
* @return list of found {@link Target}s
|
||||
*/
|
||||
List<Target> findByControllerIdIn(String... controllerIDs);
|
||||
|
||||
/**
|
||||
* Deletes the {@link Target}s with the given target IDs.
|
||||
*
|
||||
* @param targetIDs
|
||||
* to be deleted
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
|
||||
@Query("DELETE FROM JpaTarget t WHERE t.id IN ?1")
|
||||
void deleteByIdIn(final Collection<Long> targetIDs);
|
||||
|
||||
/**
|
||||
* Finds {@link Target}s by assigned {@link Tag}.
|
||||
*
|
||||
* @param tag
|
||||
* to be found
|
||||
* @return list of found targets
|
||||
*/
|
||||
@Query(value = "SELECT DISTINCT t FROM JpaTarget t JOIN t.tags tt WHERE tt = :tag")
|
||||
List<JpaTarget> findByTag(@Param("tag") final JpaTargetTag tag);
|
||||
|
||||
/**
|
||||
* Finds all {@link Target}s based on given {@link Target#getControllerId()}
|
||||
* list and assigned {@link Tag#getName()}.
|
||||
*
|
||||
* @param tag
|
||||
* to search for
|
||||
* @param controllerIds
|
||||
* to search for
|
||||
* @return {@link List} of found {@link Target}s.
|
||||
*/
|
||||
@Query(value = "SELECT DISTINCT t from JpaTarget t JOIN t.tags tt WHERE tt.name = :tagname AND t.controllerId IN :targets")
|
||||
List<Target> findByTagNameAndControllerIdIn(@Param("tagname") final String tag,
|
||||
@Param("targets") final Collection<String> controllerIds);
|
||||
|
||||
/**
|
||||
* Used by UI to filter based on selected status.
|
||||
*
|
||||
* @param pageable
|
||||
* for page configuration
|
||||
* @param status
|
||||
* to filter for
|
||||
*
|
||||
* @return found targets
|
||||
*/
|
||||
Page<Target> findByTargetInfoUpdateStatus(final Pageable pageable, final TargetUpdateStatus status);
|
||||
|
||||
/**
|
||||
* Finds all targets that have defined {@link DistributionSet} installed.
|
||||
*
|
||||
* @param pageable
|
||||
* for page configuration
|
||||
* @param set
|
||||
* is the {@link DistributionSet} to filter for.
|
||||
*
|
||||
* @return found targets
|
||||
*/
|
||||
Page<Target> findByTargetInfoInstalledDistributionSet(final Pageable pageable, final JpaDistributionSet set);
|
||||
|
||||
/**
|
||||
* retrieves the {@link Target}s which has the {@link DistributionSet}
|
||||
* installed with the given ID.
|
||||
*
|
||||
* @param pageable
|
||||
* parameter
|
||||
* @param setID
|
||||
* the ID of the {@link DistributionSet}
|
||||
* @return the found {@link Target}s
|
||||
*/
|
||||
Page<Target> findByTargetInfoInstalledDistributionSetId(final Pageable pageable, final Long setID);
|
||||
|
||||
/**
|
||||
* Finds all targets that have defined {@link DistributionSet} assigned.
|
||||
*
|
||||
* @param pageable
|
||||
* for page configuration
|
||||
* @param set
|
||||
* is the {@link DistributionSet} to filter for.
|
||||
*
|
||||
* @return found targets
|
||||
*/
|
||||
Page<Target> findByAssignedDistributionSet(final Pageable pageable, final JpaDistributionSet set);
|
||||
|
||||
/**
|
||||
* Saves all given {@link Target}s.
|
||||
*
|
||||
* @param entities
|
||||
* @return the saved entities
|
||||
* @throws IllegalArgumentException
|
||||
* in case the given entity is (@literal null}.
|
||||
*
|
||||
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable)
|
||||
*/
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true)
|
||||
<S extends JpaTarget> List<S> save(Iterable<S> entities);
|
||||
|
||||
/**
|
||||
* Saves a given entity. Use the returned instance for further operations as
|
||||
* the save operation might have changed the entity instance completely.
|
||||
*
|
||||
* @param entity
|
||||
* the target to save
|
||||
* @return the saved entity
|
||||
*/
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true)
|
||||
<S extends JpaTarget> S save(S entity);
|
||||
|
||||
/**
|
||||
* Finds all targets that have defined {@link DistributionSet} assigned.
|
||||
*
|
||||
* @param pageable
|
||||
* for page configuration
|
||||
* @param setID
|
||||
* is the ID of the {@link DistributionSet} to filter for.
|
||||
*
|
||||
* @return page of found targets
|
||||
*/
|
||||
Page<Target> findByAssignedDistributionSetId(final Pageable pageable, final Long setID);
|
||||
|
||||
/**
|
||||
* Counts number of targets with given
|
||||
* {@link Target#getAssignedDistributionSet()}.
|
||||
*
|
||||
* @param distId
|
||||
* to search for
|
||||
*
|
||||
* @return number of found {@link Target}s.
|
||||
*/
|
||||
Long countByAssignedDistributionSetId(final Long distId);
|
||||
|
||||
/**
|
||||
* @param ids
|
||||
* of count in DB
|
||||
* @return number of found {@link Target}s with given
|
||||
* {@link Target#getControllerId()}s
|
||||
*/
|
||||
@Query("SELECT COUNT(t) FROM JpaTarget t WHERE t.controllerId IN ?1")
|
||||
Long countByControllerIdIn(final Collection<String> ids);
|
||||
|
||||
/**
|
||||
* Counts number of targets with given
|
||||
* {@link TargetStatus#getInstalledDistributionSet()}.
|
||||
*
|
||||
* @param distId
|
||||
* to search for
|
||||
* @return number of found {@link Target}s.
|
||||
*/
|
||||
Long countByTargetInfoInstalledDistributionSetId(final Long distId);
|
||||
|
||||
/**
|
||||
* Finds all targets that have defined {@link DistributionSet} assigned or
|
||||
* installed.
|
||||
*
|
||||
* @param pageable
|
||||
* for page configuration
|
||||
* @param assigned
|
||||
* {@link DistributionSet} filter for; please note: must not be
|
||||
* null
|
||||
* @param installed
|
||||
* {@link DistributionSet} filter for; please note: must not be
|
||||
* null
|
||||
*
|
||||
* @return found targets
|
||||
*/
|
||||
Page<Target> findByAssignedDistributionSetOrTargetInfoInstalledDistributionSet(final Pageable pageable,
|
||||
final JpaDistributionSet assigned, final JpaDistributionSet installed);
|
||||
|
||||
/**
|
||||
* Finds all targets that have defined {@link DistributionSet} assigned or
|
||||
* installed.
|
||||
*
|
||||
* @param pageable
|
||||
* for page configuration
|
||||
* @param assigned
|
||||
* {@link DistributionSet} filter for; please note: must not be
|
||||
* null
|
||||
* @param installed
|
||||
* {@link DistributionSet} filter for; please note: must not be
|
||||
* null
|
||||
* @return found targets
|
||||
*/
|
||||
Page<Target> findByAssignedDistributionSetIdOrTargetInfoInstalledDistributionSetId(final Pageable pageable,
|
||||
final Long assigned, final Long installed);
|
||||
|
||||
/**
|
||||
* Finds all {@link Target}s in the repository.
|
||||
*
|
||||
* @return {@link List} of {@link Target}s
|
||||
*
|
||||
* @see org.springframework.data.repository.CrudRepository#findAll()
|
||||
*/
|
||||
@Override
|
||||
List<JpaTarget> findAll();
|
||||
|
||||
@Override
|
||||
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
|
||||
@Query("SELECT t FROM JpaTarget t WHERE t.id IN ?1")
|
||||
List<JpaTarget> findAll(Iterable<Long> ids);
|
||||
|
||||
/**
|
||||
* Sets {@link Target#getAssignedDistributionSet()}.
|
||||
*
|
||||
* @param set
|
||||
* to use
|
||||
* @param modifiedAt
|
||||
* current time
|
||||
* @param modifiedBy
|
||||
* current auditor
|
||||
* @param targets
|
||||
* to update
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Query("UPDATE JpaTarget t SET t.assignedDistributionSet = :set, t.lastModifiedAt = :lastModifiedAt, t.lastModifiedBy = :lastModifiedBy WHERE t.id IN :targets")
|
||||
void setAssignedDistributionSet(@Param("set") JpaDistributionSet set, @Param("lastModifiedAt") Long modifiedAt,
|
||||
@Param("lastModifiedBy") String modifiedBy, @Param("targets") Collection<Long> targets);
|
||||
|
||||
List<Target> findByRolloutTargetGroupRolloutGroup(final JpaRolloutGroup rolloutGroup);
|
||||
|
||||
/**
|
||||
*
|
||||
* Finds all targets of a rollout group.
|
||||
*
|
||||
* @param rolloutGroupId
|
||||
* the ID of the rollout group
|
||||
* @param page
|
||||
* the page request parameter
|
||||
* @return a page of all targets related to a rollout group
|
||||
*/
|
||||
Page<Target> findByRolloutTargetGroupRolloutGroupId(final Long rolloutGroupId, Pageable page);
|
||||
|
||||
/**
|
||||
* Finds all targets related to a target rollout group stored for a specific
|
||||
* rollout.
|
||||
*
|
||||
* @param rolloutGroup
|
||||
* the rollout group the targets should belong to
|
||||
* @param page
|
||||
* the page request parameter
|
||||
* @return a page of all targets related to a rollout group
|
||||
*/
|
||||
Page<Target> findByActionsRolloutGroup(JpaRolloutGroup rolloutGroup, Pageable page);
|
||||
|
||||
/**
|
||||
* Find all targets with action status for a specific group.
|
||||
*
|
||||
* @param pageable
|
||||
* the page request parameter
|
||||
* @param rolloutGroupId
|
||||
* the ID of the rollout group
|
||||
* @return targets with action status
|
||||
*/
|
||||
@Query("select DISTINCT NEW org.eclipse.hawkbit.repository.model.TargetWithActionStatus(a.target,a.status) from JpaAction a inner join fetch a.target t where a.rolloutGroup.id = :rolloutGroupId")
|
||||
Page<TargetWithActionStatus> findTargetsWithActionStatusByRolloutGroupId(final Pageable pageable,
|
||||
@Param("rolloutGroupId") Long rolloutGroupId);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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 org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link TargetTag} repository.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface TargetTagRepository
|
||||
extends BaseEntityRepository<JpaTargetTag, Long>, JpaSpecificationExecutor<JpaTargetTag> {
|
||||
|
||||
/**
|
||||
* deletes the {@link TargetTag}s with the given tag names.
|
||||
*
|
||||
* @param tagNames
|
||||
* to be deleted
|
||||
* @return 1 if tag was deleted
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
Long deleteByName(final String tagName);
|
||||
|
||||
/**
|
||||
* find {@link TargetTag} by its name.
|
||||
*
|
||||
* @param tagName
|
||||
* to filter on
|
||||
* @return the {@link TargetTag} if found, otherwise null
|
||||
*/
|
||||
JpaTargetTag findByNameEquals(final String tagName);
|
||||
|
||||
/**
|
||||
* Returns all instances of the type.
|
||||
*
|
||||
* @return all entities
|
||||
*/
|
||||
@Override
|
||||
List<JpaTargetTag> findAll();
|
||||
|
||||
@Override
|
||||
<S extends JpaTargetTag> List<S> save(Iterable<S> entities);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* 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 org.eclipse.hawkbit.repository.jpa.model.JpaTenantConfiguration;
|
||||
import org.eclipse.hawkbit.repository.model.TenantConfiguration;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* The spring-data repository for the entity {@link TenantConfiguration}.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface TenantConfigurationRepository extends BaseEntityRepository<JpaTenantConfiguration, Long> {
|
||||
|
||||
/**
|
||||
* Finds a specific {@link TenantConfiguration} by the configuration key.
|
||||
*
|
||||
* @param configurationKey
|
||||
* the configuration key to find the configuration for
|
||||
* @return the found tenant configuration object otherwise {@code null}
|
||||
*/
|
||||
JpaTenantConfiguration findByKey(String configurationKey);
|
||||
|
||||
@Override
|
||||
List<JpaTenantConfiguration> findAll();
|
||||
|
||||
/**
|
||||
* Deletes a tenant configuration by tenant and key.
|
||||
*
|
||||
* @param tenant
|
||||
* the tenant for this configuration
|
||||
* @param keyName
|
||||
* the name of the key to be deleted
|
||||
*/
|
||||
void deleteByKey(String keyName);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 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.lang.reflect.Method;
|
||||
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* {@link KeyGenerator} for tenant related caches.
|
||||
*
|
||||
*/
|
||||
@Service
|
||||
public class TenantKeyGenerator implements KeyGenerator {
|
||||
|
||||
@Autowired
|
||||
private TenantAware tenantAware;
|
||||
|
||||
@Override
|
||||
public Object generate(final Object target, final Method method, final Object... params) {
|
||||
return tenantAware.getCurrentTenant().toUpperCase();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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 org.eclipse.hawkbit.repository.jpa.model.JpaTenantMetaData;
|
||||
import org.eclipse.hawkbit.repository.model.TenantMetaData;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* repository for operations on {@link TenantMetaData} entity.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface TenantMetaDataRepository extends PagingAndSortingRepository<JpaTenantMetaData, Long> {
|
||||
|
||||
/**
|
||||
* Search {@link TenantMetaData} by tenant name.
|
||||
*
|
||||
* @param tenant
|
||||
* to search for
|
||||
* @return found {@link TenantMetaData} or <code>null</code>
|
||||
*/
|
||||
TenantMetaData findByTenantIgnoreCase(String tenant);
|
||||
|
||||
/**
|
||||
* Counts the tenant by the tenant field which is either a count of one or a
|
||||
* count of zero, this is mostly to check if the tenant exists.
|
||||
*
|
||||
* @param tenant
|
||||
* the name of the tenant to check if it is exists
|
||||
* @return the count of the tenant by name which is either one or zero
|
||||
*/
|
||||
Long countByTenantIgnoreCase(String tenant);
|
||||
|
||||
@Override
|
||||
List<JpaTenantMetaData> findAll();
|
||||
|
||||
/**
|
||||
* @param tenant
|
||||
*/
|
||||
void deleteByTenantIgnoreCase(String tenant);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
|
||||
/**
|
||||
* Tenant specific locally stored artifact representation that is used by
|
||||
* {@link SoftwareModule}.
|
||||
*/
|
||||
@MappedSuperclass
|
||||
public abstract class AbstractJpaArtifact extends AbstractJpaTenantAwareBaseEntity implements Artifact {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "sha1_hash", length = 40, nullable = true)
|
||||
private String sha1Hash;
|
||||
|
||||
@Column(name = "md5_hash", length = 32, nullable = true)
|
||||
private String md5Hash;
|
||||
|
||||
@Column(name = "file_size")
|
||||
private Long size;
|
||||
|
||||
@Override
|
||||
public abstract SoftwareModule getSoftwareModule();
|
||||
|
||||
@Override
|
||||
public String getMd5Hash() {
|
||||
return md5Hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSha1Hash() {
|
||||
return sha1Hash;
|
||||
}
|
||||
|
||||
public void setMd5Hash(final String md5Hash) {
|
||||
this.md5Hash = md5Hash;
|
||||
}
|
||||
|
||||
public void setSha1Hash(final String sha1Hash) {
|
||||
this.sha1Hash = sha1Hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(final Long size) {
|
||||
this.size = size;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.EntityListeners;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.Version;
|
||||
|
||||
import org.eclipse.hawkbit.eventbus.CacheFieldEntityListener;
|
||||
import org.eclipse.hawkbit.eventbus.EntityPropertyChangeListener;
|
||||
import org.eclipse.hawkbit.repository.model.BaseEntity;
|
||||
import org.springframework.data.annotation.CreatedBy;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.LastModifiedBy;
|
||||
import org.springframework.data.annotation.LastModifiedDate;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
|
||||
/**
|
||||
* Holder of the base attributes common to all entities.
|
||||
*
|
||||
*/
|
||||
@MappedSuperclass
|
||||
@Access(AccessType.FIELD)
|
||||
@EntityListeners({ AuditingEntityListener.class, CacheFieldEntityListener.class, EntityPropertyChangeListener.class })
|
||||
public abstract class AbstractJpaBaseEntity implements BaseEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id")
|
||||
private Long id;
|
||||
|
||||
private String createdBy;
|
||||
private String lastModifiedBy;
|
||||
private Long createdAt;
|
||||
private Long lastModifiedAt;
|
||||
|
||||
@Version
|
||||
@Column(name = "optlock_revision")
|
||||
private long optLockRevision;
|
||||
|
||||
/**
|
||||
* Default constructor needed for JPA entities.
|
||||
*/
|
||||
public AbstractJpaBaseEntity() {
|
||||
// Default constructor needed for JPA entities.
|
||||
}
|
||||
|
||||
@Override
|
||||
@Access(AccessType.PROPERTY)
|
||||
@Column(name = "created_at", insertable = true, updatable = false)
|
||||
public Long getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Access(AccessType.PROPERTY)
|
||||
@Column(name = "created_by", insertable = true, updatable = false, length = 40)
|
||||
public String getCreatedBy() {
|
||||
return createdBy;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Access(AccessType.PROPERTY)
|
||||
@Column(name = "last_modified_at", insertable = false, updatable = true)
|
||||
public Long getLastModifiedAt() {
|
||||
return lastModifiedAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Access(AccessType.PROPERTY)
|
||||
@Column(name = "last_modified_by", insertable = false, updatable = true, length = 40)
|
||||
public String getLastModifiedBy() {
|
||||
return lastModifiedBy;
|
||||
}
|
||||
|
||||
@CreatedBy
|
||||
public void setCreatedBy(final String createdBy) {
|
||||
this.createdBy = createdBy;
|
||||
}
|
||||
|
||||
@LastModifiedBy
|
||||
public void setLastModifiedBy(final String lastModifiedBy) {
|
||||
this.lastModifiedBy = lastModifiedBy;
|
||||
}
|
||||
|
||||
@CreatedDate
|
||||
public void setCreatedAt(final Long createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
@LastModifiedDate
|
||||
public void setLastModifiedAt(final Long lastModifiedAt) {
|
||||
this.lastModifiedAt = lastModifiedAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getOptLockRevision() {
|
||||
return optLockRevision;
|
||||
}
|
||||
|
||||
public void setOptLockRevision(final long optLockRevision) {
|
||||
this.optLockRevision = optLockRevision;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BaseEntity [id=" + id + "]";
|
||||
}
|
||||
|
||||
public void setId(final Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defined equals/hashcode strategy for the repository in general is that an
|
||||
* entity is equal if it has the same {@link #getId()} and
|
||||
* {@link #getOptLockRevision()} and class.
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
// Exception squid:S864 - generated code
|
||||
@SuppressWarnings({ "squid:S864" })
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + (id == null ? 0 : id.hashCode());
|
||||
result = prime * result + (int) (optLockRevision ^ optLockRevision >>> 32);
|
||||
result = prime * result + this.getClass().getName().hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defined equals/hashcode strategy for the repository in general is that an
|
||||
* entity is equal if it has the same {@link #getId()} and
|
||||
* {@link #getOptLockRevision()} and class.
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object obj) { // NOSONAR - as this is generated
|
||||
// code
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(this.getClass().isInstance(obj))) {
|
||||
return false;
|
||||
}
|
||||
final AbstractJpaBaseEntity other = (AbstractJpaBaseEntity) obj;
|
||||
if (id == null) {
|
||||
if (other.id != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!id.equals(other.id)) {
|
||||
return false;
|
||||
}
|
||||
if (optLockRevision != other.optLockRevision) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.MetaData;
|
||||
|
||||
/**
|
||||
* Meta data for entities.
|
||||
*
|
||||
*/
|
||||
@MappedSuperclass
|
||||
public abstract class AbstractJpaMetaData implements MetaData {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@Column(name = "meta_key", length = 128)
|
||||
private String key;
|
||||
|
||||
@Column(name = "meta_value", length = 4000)
|
||||
@Basic
|
||||
private String value;
|
||||
|
||||
public AbstractJpaMetaData(final String key, final String value) {
|
||||
super();
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public AbstractJpaMetaData() {
|
||||
// Default constructor needed for JPA entities
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKey(final String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(final String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((key == null) ? 0 : key.hashCode());
|
||||
result = prime * result + ((value == null) ? 0 : value.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(this.getClass().isInstance(obj))) {
|
||||
return false;
|
||||
}
|
||||
final AbstractJpaMetaData other = (AbstractJpaMetaData) obj;
|
||||
if (key == null) {
|
||||
if (other.key != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!key.equals(other.key)) {
|
||||
return false;
|
||||
}
|
||||
if (value == null) {
|
||||
if (other.value != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!value.equals(other.value)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
|
||||
/**
|
||||
* {@link TenantAwareBaseEntity} extension for all entities that are named in
|
||||
* addition to their technical ID.
|
||||
*/
|
||||
@MappedSuperclass
|
||||
public abstract class AbstractJpaNamedEntity extends AbstractJpaTenantAwareBaseEntity implements NamedEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "name", nullable = false, length = 64)
|
||||
private String name;
|
||||
|
||||
@Column(name = "description", nullable = true, length = 512)
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public AbstractJpaNamedEntity() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameterized constructor.
|
||||
*
|
||||
* @param name
|
||||
* of the {@link NamedEntity}
|
||||
* @param description
|
||||
* of the {@link NamedEntity}
|
||||
*/
|
||||
public AbstractJpaNamedEntity(final String name, final String description) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDescription(final String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
import org.eclipse.hawkbit.repository.model.NamedVersionedEntity;
|
||||
|
||||
/**
|
||||
* Extension for {@link NamedEntity} that are versioned.
|
||||
*
|
||||
*/
|
||||
@MappedSuperclass
|
||||
public abstract class AbstractJpaNamedVersionedEntity extends AbstractJpaNamedEntity implements NamedVersionedEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "version", nullable = false, length = 64)
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* parameterized constructor.
|
||||
*
|
||||
* @param name
|
||||
* of the entity
|
||||
* @param version
|
||||
* of the entity
|
||||
* @param description
|
||||
*/
|
||||
public AbstractJpaNamedVersionedEntity(final String name, final String version, final String description) {
|
||||
super(name, description);
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
AbstractJpaNamedVersionedEntity() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVersion(final String version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.Tag;
|
||||
|
||||
/**
|
||||
* A Tag can be used as describing and organizational meta information for any
|
||||
* kind of entity.
|
||||
*
|
||||
*/
|
||||
@MappedSuperclass
|
||||
public abstract class AbstractJpaTag extends AbstractJpaNamedEntity implements Tag {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "colour", nullable = true, length = 16)
|
||||
private String colour;
|
||||
|
||||
protected AbstractJpaTag() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Public constructor.
|
||||
*
|
||||
* @param name
|
||||
* of the {@link Tag}
|
||||
* @param description
|
||||
* of the {@link Tag}
|
||||
* @param colour
|
||||
* of tag in UI
|
||||
*/
|
||||
public AbstractJpaTag(final String name, final String description, final String colour) {
|
||||
super(name, description);
|
||||
this.colour = colour;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColour() {
|
||||
return colour;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColour(final String colour) {
|
||||
this.colour = colour;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Tag [getOptLockRevision()=" + getOptLockRevision() + ", getId()=" + getId() + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.PrePersist;
|
||||
|
||||
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.eclipse.hawkbit.repository.model.helper.SystemManagementHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.TenantAwareHolder;
|
||||
import org.eclipse.persistence.annotations.Multitenant;
|
||||
import org.eclipse.persistence.annotations.MultitenantType;
|
||||
import org.eclipse.persistence.annotations.TenantDiscriminatorColumn;
|
||||
|
||||
/**
|
||||
* Holder of the base attributes common to all tenant aware entities.
|
||||
*
|
||||
*/
|
||||
@MappedSuperclass
|
||||
@TenantDiscriminatorColumn(name = "tenant", length = 40)
|
||||
@Multitenant(MultitenantType.SINGLE_TABLE)
|
||||
public abstract class AbstractJpaTenantAwareBaseEntity extends AbstractJpaBaseEntity implements TenantAwareBaseEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "tenant", nullable = false, insertable = false, updatable = false, length = 40)
|
||||
private String tenant;
|
||||
|
||||
/**
|
||||
* Default constructor needed for JPA entities.
|
||||
*/
|
||||
public AbstractJpaTenantAwareBaseEntity() {
|
||||
// Default constructor needed for JPA entities.
|
||||
}
|
||||
|
||||
/**
|
||||
* PrePersist listener method for all {@link TenantAwareBaseEntity}
|
||||
* entities.
|
||||
*/
|
||||
@PrePersist
|
||||
public void prePersist() {
|
||||
// before persisting the entity check the current ID of the tenant by
|
||||
// using the TenantAware
|
||||
// service
|
||||
final String currentTenant = SystemManagementHolder.getInstance().currentTenant();
|
||||
if (currentTenant == null) {
|
||||
throw new TenantNotExistException("Tenant "
|
||||
+ TenantAwareHolder.getInstance().getTenantAware().getCurrentTenant()
|
||||
+ " does not exists, cannot create entity " + this.getClass() + " with id " + super.getId());
|
||||
}
|
||||
setTenant(currentTenant.toUpperCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTenant() {
|
||||
return tenant;
|
||||
}
|
||||
|
||||
public void setTenant(final String tenant) {
|
||||
this.tenant = tenant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BaseEntity [id=" + super.getId() + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Tenant aware entities extend the equals/hashcode strategy with the tenant
|
||||
* name. That would allow for instance in a multi-schema based data
|
||||
* separation setup to have the same primary key for different entities of
|
||||
* different tenants.
|
||||
*
|
||||
* @see org.eclipse.hawkbit.repository.model.BaseEntity#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + (tenant == null ? 0 : tenant.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tenant aware entities extend the equals/hashcode strategy with the tenant
|
||||
* name. That would allow for instance in a multi-schema based data
|
||||
* separation setup to have the same primary key for different entities of
|
||||
* different tenants.
|
||||
*
|
||||
* @see org.eclipse.hawkbit.repository.model.BaseEntity#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
final AbstractJpaTenantAwareBaseEntity other = (AbstractJpaTenantAwareBaseEntity) obj;
|
||||
if (tenant == null) {
|
||||
if (other.tenant != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!tenant.equals(other.tenant)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ConstraintMode;
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MapsId;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
|
||||
/**
|
||||
* Relation element between a {@link DistributionSetType} and its
|
||||
* {@link SoftwareModuleType} elements.
|
||||
*
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sp_ds_type_element")
|
||||
public class DistributionSetTypeElement implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@EmbeddedId
|
||||
private DistributionSetTypeElementCompositeKey key;
|
||||
|
||||
@Column(name = "mandatory")
|
||||
private boolean mandatory;
|
||||
|
||||
@MapsId("dsType")
|
||||
@ManyToOne(optional = false, fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "distribution_set_type", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_type_element_dstype"))
|
||||
private JpaDistributionSetType dsType;
|
||||
|
||||
@MapsId("smType")
|
||||
@ManyToOne(optional = false, fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "software_module_type", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_type_element_smtype"))
|
||||
private JpaSoftwareModuleType smType;
|
||||
|
||||
public DistributionSetTypeElement() {
|
||||
// Default constructor for JPA
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard constructor.
|
||||
*
|
||||
* @param dsType
|
||||
* of the element
|
||||
* @param smType
|
||||
* of the element
|
||||
* @param mandatory
|
||||
* to <code>true</code> if the {@link SoftwareModuleType} if
|
||||
* mandatory element in the {@link DistributionSet}.
|
||||
*/
|
||||
public DistributionSetTypeElement(final JpaDistributionSetType dsType, final JpaSoftwareModuleType smType,
|
||||
final boolean mandatory) {
|
||||
super();
|
||||
key = new DistributionSetTypeElementCompositeKey(dsType, smType);
|
||||
this.dsType = dsType;
|
||||
this.smType = smType;
|
||||
this.mandatory = mandatory;
|
||||
}
|
||||
|
||||
public boolean isMandatory() {
|
||||
return mandatory;
|
||||
}
|
||||
|
||||
public DistributionSetType getDsType() {
|
||||
return dsType;
|
||||
}
|
||||
|
||||
public SoftwareModuleType getSmType() {
|
||||
return smType;
|
||||
}
|
||||
|
||||
public DistributionSetTypeElementCompositeKey getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DistributionSetTypeElement [mandatory=" + mandatory + ", dsType=" + dsType + ", smType=" + smType + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
|
||||
/**
|
||||
* Composite key for {@link DistributionSetTypeElement}.
|
||||
*/
|
||||
@Embeddable
|
||||
public class DistributionSetTypeElementCompositeKey implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "distribution_set_type", nullable = false)
|
||||
private Long dsType;
|
||||
|
||||
@Column(name = "software_module_type", nullable = false)
|
||||
private Long smType;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
DistributionSetTypeElementCompositeKey() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param dsType
|
||||
* in the key
|
||||
* @param smType
|
||||
* in the key
|
||||
*/
|
||||
DistributionSetTypeElementCompositeKey(final JpaDistributionSetType dsType, final JpaSoftwareModuleType smType) {
|
||||
super();
|
||||
this.dsType = dsType.getId();
|
||||
this.smType = smType.getId();
|
||||
}
|
||||
|
||||
public Long getDsType() {
|
||||
return dsType;
|
||||
}
|
||||
|
||||
public void setDsType(final Long dsType) {
|
||||
this.dsType = dsType;
|
||||
}
|
||||
|
||||
public Long getSmType() {
|
||||
return smType;
|
||||
}
|
||||
|
||||
public void setSmType(final Long smType) {
|
||||
this.smType = smType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
|
||||
/**
|
||||
* The DistributionSet Metadata composite key which contains the meta data key
|
||||
* and the ID of the DistributionSet itself.
|
||||
*
|
||||
*/
|
||||
public final class DsMetadataCompositeKey implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String key;
|
||||
|
||||
private Long distributionSet;
|
||||
|
||||
public DsMetadataCompositeKey() {
|
||||
// Default constructor for JPA.
|
||||
}
|
||||
|
||||
/**
|
||||
* @param distributionSet
|
||||
* the distribution set for this meta data
|
||||
* @param key
|
||||
* the key of the meta data
|
||||
*/
|
||||
public DsMetadataCompositeKey(final DistributionSet distributionSet, final String key) {
|
||||
this.distributionSet = distributionSet.getId();
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(final String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public Long getDistributionSet() {
|
||||
return distributionSet;
|
||||
}
|
||||
|
||||
public void setDistributionSet(final Long distributionSet) {
|
||||
this.distributionSet = distributionSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + (distributionSet == null ? 0 : distributionSet.hashCode());
|
||||
result = prime * result + (key == null ? 0 : key.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) { // NOSONAR - as this is generated
|
||||
// code
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final DsMetadataCompositeKey other = (DsMetadataCompositeKey) obj;
|
||||
if (distributionSet == null) {
|
||||
if (other.distributionSet != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!distributionSet.equals(other.distributionSet)) {
|
||||
return false;
|
||||
}
|
||||
if (key == null) {
|
||||
if (other.key != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!key.equals(other.key)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ConstraintMode;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.NamedAttributeNode;
|
||||
import javax.persistence.NamedEntityGraph;
|
||||
import javax.persistence.NamedEntityGraphs;
|
||||
import javax.persistence.NamedSubgraph;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
import org.eclipse.hawkbit.cache.CacheField;
|
||||
import org.eclipse.hawkbit.cache.CacheKeys;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
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.Target;
|
||||
import org.eclipse.persistence.annotations.CascadeOnDelete;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link Action}.
|
||||
*/
|
||||
@Table(name = "sp_action", indexes = { @Index(name = "sp_idx_action_01", columnList = "tenant,distribution_set"),
|
||||
@Index(name = "sp_idx_action_02", columnList = "tenant,target,active"),
|
||||
@Index(name = "sp_idx_action_prim", columnList = "tenant,id") })
|
||||
@NamedEntityGraphs({ @NamedEntityGraph(name = "Action.ds", attributeNodes = { @NamedAttributeNode("distributionSet") }),
|
||||
@NamedEntityGraph(name = "Action.all", attributeNodes = { @NamedAttributeNode("distributionSet"),
|
||||
@NamedAttributeNode(value = "target", subgraph = "target.ds") }, subgraphs = @NamedSubgraph(name = "target.ds", attributeNodes = @NamedAttributeNode("assignedDistributionSet"))) })
|
||||
@Entity
|
||||
public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Action {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "distribution_set", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_ds"))
|
||||
private JpaDistributionSet distributionSet;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "target", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_target"))
|
||||
private JpaTarget target;
|
||||
|
||||
@Column(name = "active")
|
||||
private boolean active;
|
||||
|
||||
@Column(name = "action_type", nullable = false)
|
||||
@Enumerated(EnumType.STRING)
|
||||
private ActionType actionType;
|
||||
|
||||
@Column(name = "forced_time")
|
||||
private long forcedTime;
|
||||
|
||||
@Column(name = "status")
|
||||
private Status status;
|
||||
|
||||
@CascadeOnDelete
|
||||
@OneToMany(mappedBy = "action", targetEntity = JpaActionStatus.class, fetch = FetchType.LAZY, cascade = {
|
||||
CascadeType.REMOVE })
|
||||
private List<ActionStatus> actionStatus;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "rolloutgroup", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_rolloutgroup"))
|
||||
private JpaRolloutGroup rolloutGroup;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "rollout", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_rollout"))
|
||||
private JpaRollout rollout;
|
||||
|
||||
/**
|
||||
* Note: filled only in {@link Status#DOWNLOAD}.
|
||||
*/
|
||||
@Transient
|
||||
@CacheField(key = CacheKeys.DOWNLOAD_PROGRESS_PERCENT)
|
||||
private int downloadProgressPercent;
|
||||
|
||||
@Override
|
||||
public DistributionSet getDistributionSet() {
|
||||
return distributionSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDistributionSet(final DistributionSet distributionSet) {
|
||||
this.distributionSet = (JpaDistributionSet) distributionSet;
|
||||
}
|
||||
|
||||
public void setActive(final boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatus(final Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDownloadProgressPercent() {
|
||||
return downloadProgressPercent;
|
||||
}
|
||||
|
||||
public void setDownloadProgressPercent(final int downloadProgressPercent) {
|
||||
this.downloadProgressPercent = downloadProgressPercent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
public void setActionType(final ActionType actionType) {
|
||||
this.actionType = actionType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionType getActionType() {
|
||||
return actionType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ActionStatus> getActionStatus() {
|
||||
return actionStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTarget(final Target target) {
|
||||
this.target = (JpaTarget) target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Target getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getForcedTime() {
|
||||
return forcedTime;
|
||||
}
|
||||
|
||||
public void setForcedTime(final long forcedTime) {
|
||||
this.forcedTime = forcedTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RolloutGroup getRolloutGroup() {
|
||||
return rolloutGroup;
|
||||
}
|
||||
|
||||
public void setRolloutGroup(final RolloutGroup rolloutGroup) {
|
||||
this.rolloutGroup = (JpaRolloutGroup) rolloutGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rollout getRollout() {
|
||||
return rollout;
|
||||
}
|
||||
|
||||
public void setRollout(final Rollout rollout) {
|
||||
this.rollout = (JpaRollout) rollout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Action [distributionSet=" + distributionSet + ", getId()=" + getId() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.CollectionTable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ConstraintMode;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.NamedAttributeNode;
|
||||
import javax.persistence.NamedEntityGraph;
|
||||
import javax.persistence.Table;
|
||||
|
||||
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.persistence.annotations.CascadeOnDelete;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
|
||||
/**
|
||||
* Entity to store the status for a specific action.
|
||||
*/
|
||||
@Table(name = "sp_action_status", indexes = { @Index(name = "sp_idx_action_status_01", columnList = "tenant,action"),
|
||||
@Index(name = "sp_idx_action_status_02", columnList = "tenant,action,status"),
|
||||
@Index(name = "sp_idx_action_status_prim", columnList = "tenant,id") })
|
||||
@NamedEntityGraph(name = "ActionStatus.withMessages", attributeNodes = { @NamedAttributeNode("messages") })
|
||||
@Entity
|
||||
public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements ActionStatus {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "target_occurred_at")
|
||||
private Long occurredAt;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(name = "action", nullable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_act_stat_action"))
|
||||
private JpaAction action;
|
||||
|
||||
@Column(name = "status")
|
||||
private Status status;
|
||||
|
||||
@CascadeOnDelete
|
||||
@ElementCollection(fetch = FetchType.LAZY, targetClass = String.class)
|
||||
@CollectionTable(name = "sp_action_status_messages", joinColumns = @JoinColumn(name = "action_status_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_stat_msg_act_stat")), indexes = {
|
||||
@Index(name = "sp_idx_action_status_msgs_01", columnList = "action_status_id") })
|
||||
@Column(name = "detail_message", length = 512)
|
||||
private final List<String> messages = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Creates a new {@link ActionStatus} object.
|
||||
*
|
||||
* @param action
|
||||
* the action for this action status
|
||||
* @param status
|
||||
* the status for this action status
|
||||
* @param occurredAt
|
||||
* the occurred timestamp
|
||||
*/
|
||||
public JpaActionStatus(final Action action, final Status status, final Long occurredAt) {
|
||||
this.action = (JpaAction) action;
|
||||
this.status = status;
|
||||
this.occurredAt = occurredAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ActionStatus} object.
|
||||
*
|
||||
* @param action
|
||||
* the action for this action status
|
||||
* @param status
|
||||
* the status for this action status
|
||||
* @param occurredAt
|
||||
* the occurred timestamp
|
||||
* @param messages
|
||||
* the messages which should be added to this action status
|
||||
*/
|
||||
public JpaActionStatus(final JpaAction action, final Status status, final Long occurredAt, final String message) {
|
||||
this.action = action;
|
||||
this.status = status;
|
||||
this.occurredAt = occurredAt;
|
||||
addMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* JPA default constructor.
|
||||
*/
|
||||
public JpaActionStatus() {
|
||||
// JPA default constructor.
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getOccurredAt() {
|
||||
return occurredAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOccurredAt(final Long occurredAt) {
|
||||
this.occurredAt = occurredAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void addMessage(final String message) {
|
||||
if (message != null) {
|
||||
Splitter.fixedLength(512).split(message).forEach(messages::add);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getMessages() {
|
||||
return messages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAction(final Action action) {
|
||||
this.action = (JpaAction) action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatus(final Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.ActionWithStatusCount;
|
||||
|
||||
/**
|
||||
* Custom JPA Model for querying {@link Action} include the count of the
|
||||
* action's {@link ActionStatus}.
|
||||
*
|
||||
*/
|
||||
public class JpaActionWithStatusCount implements ActionWithStatusCount {
|
||||
private final Long actionStatusCount;
|
||||
private final Long dsId;
|
||||
private final String dsName;
|
||||
private final String dsVersion;
|
||||
private final JpaAction action;
|
||||
private final String rolloutName;
|
||||
|
||||
/**
|
||||
* JPA constructor, the parameter are the result set columns of the custom
|
||||
* query.
|
||||
*
|
||||
* @param actionId
|
||||
* the ID of the action
|
||||
* @param actionType
|
||||
* the action type
|
||||
* @param active
|
||||
* the active attribute of the action
|
||||
* @param forcedTime
|
||||
* the forced time attribute of the action
|
||||
* @param status
|
||||
* the status attribute of the action
|
||||
* @param actionCreatedAt
|
||||
* the createdAt timestamp of the action
|
||||
* @param actionLastModifiedAt
|
||||
* the last modified timestamp of the action
|
||||
* @param dsId
|
||||
* the ID of the distributionset
|
||||
* @param dsName
|
||||
* the name of the distributionset
|
||||
* @param dsVersion
|
||||
* the version of the distributionset
|
||||
* @param actionStatusCount
|
||||
* the count of the action status for this action
|
||||
* @param rolloutName
|
||||
* the rollout name
|
||||
*/
|
||||
// Exception squid:S00107 - needed this way for JPA to fill the view
|
||||
@SuppressWarnings("squid:S00107")
|
||||
public JpaActionWithStatusCount(final Long actionId, final ActionType actionType, final boolean active,
|
||||
final long forcedTime, final Status status, final Long actionCreatedAt, final Long actionLastModifiedAt,
|
||||
final Long dsId, final String dsName, final String dsVersion, final Long actionStatusCount,
|
||||
final String rolloutName) {
|
||||
this.dsId = dsId;
|
||||
this.dsName = dsName;
|
||||
this.dsVersion = dsVersion;
|
||||
this.actionStatusCount = actionStatusCount;
|
||||
this.rolloutName = rolloutName;
|
||||
|
||||
action = new JpaAction();
|
||||
action.setActionType(actionType);
|
||||
action.setActive(active);
|
||||
action.setForcedTime(forcedTime);
|
||||
action.setStatus(status);
|
||||
action.setId(actionId);
|
||||
action.setActionType(actionType);
|
||||
action.setCreatedAt(actionCreatedAt);
|
||||
action.setLastModifiedAt(actionLastModifiedAt);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getDsId() {
|
||||
return dsId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDsName() {
|
||||
return dsName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDsVersion() {
|
||||
return dsVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getActionStatusCount() {
|
||||
return actionStatusCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRolloutName() {
|
||||
return rolloutName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ConstraintMode;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.NamedAttributeNode;
|
||||
import javax.persistence.NamedEntityGraph;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
import org.eclipse.hawkbit.repository.exception.DistributionSetTypeUndefinedException;
|
||||
import org.eclipse.hawkbit.repository.exception.UnsupportedSoftwareModuleForThisDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetIdName;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
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.persistence.annotations.CascadeOnDelete;
|
||||
|
||||
/**
|
||||
* Jpa implementation of {@link DistributionSet}.
|
||||
*
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sp_distribution_set", uniqueConstraints = {
|
||||
@UniqueConstraint(columnNames = { "name", "version", "tenant" }, name = "uk_distrib_set") }, indexes = {
|
||||
@Index(name = "sp_idx_distribution_set_01", columnList = "tenant,deleted,name,complete"),
|
||||
@Index(name = "sp_idx_distribution_set_02", columnList = "tenant,required_migration_step"),
|
||||
@Index(name = "sp_idx_distribution_set_prim", columnList = "tenant,id") })
|
||||
@NamedEntityGraph(name = "DistributionSet.detail", attributeNodes = { @NamedAttributeNode("modules"),
|
||||
@NamedAttributeNode("tags"), @NamedAttributeNode("type") })
|
||||
public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implements DistributionSet {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "required_migration_step")
|
||||
private boolean requiredMigrationStep;
|
||||
|
||||
@ManyToMany(targetEntity = JpaSoftwareModule.class, fetch = FetchType.LAZY)
|
||||
@JoinTable(name = "sp_ds_module", joinColumns = {
|
||||
@JoinColumn(name = "ds_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_module_ds")) }, inverseJoinColumns = {
|
||||
@JoinColumn(name = "module_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_module_module")) })
|
||||
private final Set<SoftwareModule> modules = new HashSet<>();
|
||||
|
||||
@ManyToMany(targetEntity = JpaDistributionSetTag.class)
|
||||
@JoinTable(name = "sp_ds_dstag", joinColumns = {
|
||||
@JoinColumn(name = "ds", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstag_ds")) }, inverseJoinColumns = {
|
||||
@JoinColumn(name = "TAG", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstag_tag")) })
|
||||
private Set<DistributionSetTag> tags = new HashSet<>();
|
||||
|
||||
@Column(name = "deleted")
|
||||
private boolean deleted;
|
||||
|
||||
@OneToMany(mappedBy = "assignedDistributionSet", targetEntity = JpaTarget.class, fetch = FetchType.LAZY)
|
||||
private List<Target> assignedToTargets;
|
||||
|
||||
@OneToMany(mappedBy = "installedDistributionSet", targetEntity = JpaTargetInfo.class, fetch = FetchType.LAZY)
|
||||
private List<TargetInfo> installedAtTargets;
|
||||
|
||||
@OneToMany(mappedBy = "distributionSet", targetEntity = JpaAction.class, fetch = FetchType.LAZY)
|
||||
private List<Action> actions;
|
||||
|
||||
@CascadeOnDelete
|
||||
@OneToMany(fetch = FetchType.LAZY, targetEntity = JpaDistributionSetMetadata.class, cascade = {
|
||||
CascadeType.REMOVE })
|
||||
@JoinColumn(name = "ds_id", insertable = false, updatable = false)
|
||||
private final List<DistributionSetMetadata> metadata = new ArrayList<>();
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, targetEntity = JpaDistributionSetType.class)
|
||||
@JoinColumn(name = "ds_id", nullable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstype_ds"))
|
||||
private DistributionSetType type;
|
||||
|
||||
@Column(name = "complete")
|
||||
private boolean complete;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public JpaDistributionSet() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameterized constructor.
|
||||
*
|
||||
* @param name
|
||||
* of the {@link DistributionSet}
|
||||
* @param version
|
||||
* of the {@link DistributionSet}
|
||||
* @param description
|
||||
* of the {@link DistributionSet}
|
||||
* @param type
|
||||
* of the {@link DistributionSet}
|
||||
* @param moduleList
|
||||
* {@link SoftwareModule}s of the {@link DistributionSet}
|
||||
*/
|
||||
public JpaDistributionSet(final String name, final String version, final String description,
|
||||
final DistributionSetType type, final Collection<SoftwareModule> moduleList) {
|
||||
super(name, version, description);
|
||||
|
||||
this.type = type;
|
||||
if (moduleList != null) {
|
||||
moduleList.forEach(this::addModule);
|
||||
}
|
||||
if (this.type != null) {
|
||||
complete = this.type.checkComplete(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DistributionSetTag> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DistributionSetMetadata> getMetadata() {
|
||||
return Collections.unmodifiableList(metadata);
|
||||
}
|
||||
|
||||
public List<Action> getActions() {
|
||||
return actions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRequiredMigrationStep() {
|
||||
return requiredMigrationStep;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSet setDeleted(final boolean deleted) {
|
||||
this.deleted = deleted;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSet setRequiredMigrationStep(final boolean isRequiredMigrationStep) {
|
||||
requiredMigrationStep = isRequiredMigrationStep;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DistributionSet setTags(final Set<DistributionSetTag> tags) {
|
||||
this.tags = tags;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Target> getAssignedTargets() {
|
||||
return assignedToTargets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TargetInfo> getInstalledTargets() {
|
||||
return installedAtTargets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DistributionSet [getName()=" + getName() + ", getOptLockRevision()=" + getOptLockRevision()
|
||||
+ ", getId()=" + getId() + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SoftwareModule> getModules() {
|
||||
return Collections.unmodifiableSet(modules);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSetIdName getDistributionSetIdName() {
|
||||
return new DistributionSetIdName(getId(), getName(), getVersion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addModule(final SoftwareModule softwareModule) {
|
||||
|
||||
// we cannot allow that modules are added without a type defined
|
||||
if (type == null) {
|
||||
throw new DistributionSetTypeUndefinedException();
|
||||
}
|
||||
|
||||
// check if it is allowed to such a module to this DS type
|
||||
if (!type.containsModuleType(softwareModule.getType())) {
|
||||
throw new UnsupportedSoftwareModuleForThisDistributionSetException();
|
||||
}
|
||||
|
||||
final Optional<SoftwareModule> found = modules.stream()
|
||||
.filter(module -> module.getId().equals(softwareModule.getId())).findFirst();
|
||||
|
||||
if (found.isPresent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final long allready = modules.stream()
|
||||
.filter(module -> module.getType().getKey().equals(softwareModule.getType().getKey())).count();
|
||||
|
||||
if (allready >= softwareModule.getType().getMaxAssignments()) {
|
||||
final Optional<SoftwareModule> sameKey = modules.stream()
|
||||
.filter(module -> module.getType().getKey().equals(softwareModule.getType().getKey())).findFirst();
|
||||
modules.remove(sameKey.get());
|
||||
}
|
||||
|
||||
if (modules.add(softwareModule)) {
|
||||
complete = type.checkComplete(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeModule(final SoftwareModule softwareModule) {
|
||||
final Optional<SoftwareModule> found = modules.stream()
|
||||
.filter(module -> module.getId().equals(softwareModule.getId())).findFirst();
|
||||
|
||||
if (found.isPresent()) {
|
||||
modules.remove(found.get());
|
||||
complete = type.checkComplete(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoftwareModule findFirstModuleByType(final SoftwareModuleType type) {
|
||||
final Optional<SoftwareModule> result = modules.stream().filter(module -> module.getType().equals(type))
|
||||
.findFirst();
|
||||
|
||||
if (result.isPresent()) {
|
||||
return result.get();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSetType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setType(final DistributionSetType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isComplete() {
|
||||
return complete;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import javax.persistence.ConstraintMode;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
|
||||
|
||||
/**
|
||||
* Meta data for {@link DistributionSet}.
|
||||
*
|
||||
*/
|
||||
@IdClass(DsMetadataCompositeKey.class)
|
||||
@Entity
|
||||
@Table(name = "sp_ds_metadata")
|
||||
public class JpaDistributionSetMetadata extends AbstractJpaMetaData implements DistributionSetMetadata {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "ds_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_metadata_ds"))
|
||||
private JpaDistributionSet distributionSet;
|
||||
|
||||
public JpaDistributionSetMetadata() {
|
||||
// default public constructor for JPA
|
||||
}
|
||||
|
||||
public JpaDistributionSetMetadata(final String key, final DistributionSet distributionSet, final String value) {
|
||||
super(key, value);
|
||||
this.distributionSet = (JpaDistributionSet) distributionSet;
|
||||
}
|
||||
|
||||
public DsMetadataCompositeKey getId() {
|
||||
return new DsMetadataCompositeKey(distributionSet, getKey());
|
||||
}
|
||||
|
||||
public void setDistributionSet(final DistributionSet distributionSet) {
|
||||
this.distributionSet = (JpaDistributionSet) distributionSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSet getDistributionSet() {
|
||||
return distributionSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + ((distributionSet == null) ? 0 : distributionSet.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
final JpaDistributionSetMetadata other = (JpaDistributionSetMetadata) obj;
|
||||
if (distributionSet == null) {
|
||||
if (other.distributionSet != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!distributionSet.equals(other.distributionSet)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
|
||||
|
||||
/**
|
||||
* A {@link DistributionSetTag} is used to describe DistributionSet attributes
|
||||
* and use them also for filtering the DistributionSet list.
|
||||
*
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sp_distributionset_tag", indexes = {
|
||||
@Index(name = "sp_idx_distribution_set_tag_prim", columnList = "tenant,id") }, uniqueConstraints = @UniqueConstraint(columnNames = {
|
||||
"name", "tenant" }, name = "uk_ds_tag"))
|
||||
public class JpaDistributionSetTag extends AbstractJpaTag implements DistributionSetTag {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ManyToMany(mappedBy = "tags", targetEntity = JpaDistributionSet.class, fetch = FetchType.LAZY)
|
||||
private List<DistributionSet> assignedToDistributionSet;
|
||||
|
||||
/**
|
||||
* Public constructor.
|
||||
*
|
||||
* @param name
|
||||
* of the {@link DistributionSetTag}
|
||||
**/
|
||||
public JpaDistributionSetTag(final String name) {
|
||||
super(name, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public constructor.
|
||||
*
|
||||
* @param name
|
||||
* of the {@link DistributionSetTag}
|
||||
* @param description
|
||||
* of the {@link DistributionSetTag}
|
||||
* @param colour
|
||||
* of tag in UI
|
||||
*/
|
||||
public JpaDistributionSetTag(final String name, final String description, final String colour) {
|
||||
super(name, description, colour);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor for JPA.
|
||||
*/
|
||||
public JpaDistributionSetTag() {
|
||||
// Default constructor for JPA.
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DistributionSet> getAssignedToDistributionSet() {
|
||||
return assignedToDistributionSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + this.getClass().getName().hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) { // NOSONAR - as this is generated
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof DistributionSetTag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
|
||||
/**
|
||||
* A distribution set type defines which software module types can or have to be
|
||||
* {@link DistributionSet}.
|
||||
*
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sp_distribution_set_type", indexes = {
|
||||
@Index(name = "sp_idx_distribution_set_type_01", columnList = "tenant,deleted"),
|
||||
@Index(name = "sp_idx_distribution_set_type_prim", columnList = "tenant,id") }, uniqueConstraints = {
|
||||
@UniqueConstraint(columnNames = { "name", "tenant" }, name = "uk_dst_name"),
|
||||
@UniqueConstraint(columnNames = { "type_key", "tenant" }, name = "uk_dst_key") })
|
||||
public class JpaDistributionSetType extends AbstractJpaNamedEntity implements DistributionSetType {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@OneToMany(targetEntity = DistributionSetTypeElement.class, cascade = {
|
||||
CascadeType.ALL }, fetch = FetchType.EAGER, orphanRemoval = true)
|
||||
@JoinColumn(name = "distribution_set_type", insertable = false, updatable = false)
|
||||
private final Set<DistributionSetTypeElement> elements = new HashSet<>();
|
||||
|
||||
@Column(name = "type_key", nullable = false, length = 64)
|
||||
private String key;
|
||||
|
||||
@Column(name = "colour", nullable = true, length = 16)
|
||||
private String colour;
|
||||
|
||||
@Column(name = "deleted")
|
||||
private boolean deleted;
|
||||
|
||||
public JpaDistributionSetType() {
|
||||
// default public constructor for JPA
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard constructor.
|
||||
*
|
||||
* @param key
|
||||
* of the type (unique)
|
||||
* @param name
|
||||
* of the type (unique)
|
||||
* @param description
|
||||
* of the type
|
||||
*/
|
||||
public JpaDistributionSetType(final String key, final String name, final String description) {
|
||||
this(key, name, description, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param key
|
||||
* of the type
|
||||
* @param name
|
||||
* of the type
|
||||
* @param description
|
||||
* of the type
|
||||
* @param color
|
||||
* of the type. It will be null by default
|
||||
*/
|
||||
public JpaDistributionSetType(final String key, final String name, final String description, final String color) {
|
||||
super(name, description);
|
||||
this.key = key;
|
||||
colour = color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
public void setDeleted(final boolean deleted) {
|
||||
this.deleted = deleted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SoftwareModuleType> getMandatoryModuleTypes() {
|
||||
return elements.stream().filter(element -> element.isMandatory()).map(element -> element.getSmType())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SoftwareModuleType> getOptionalModuleTypes() {
|
||||
return elements.stream().filter(element -> !element.isMandatory()).map(element -> element.getSmType())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsModuleType(final SoftwareModuleType softwareModuleType) {
|
||||
for (final DistributionSetTypeElement distributionSetTypeElement : elements) {
|
||||
if (distributionSetTypeElement.getSmType().equals(softwareModuleType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsMandatoryModuleType(final SoftwareModuleType softwareModuleType) {
|
||||
return elements.stream().filter(element -> element.isMandatory())
|
||||
.filter(element -> element.getSmType().equals(softwareModuleType)).findFirst().isPresent();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsMandatoryModuleType(final Long softwareModuleTypeId) {
|
||||
return elements.stream().filter(element -> element.isMandatory())
|
||||
.filter(element -> element.getSmType().getId().equals(softwareModuleTypeId)).findFirst().isPresent();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsOptionalModuleType(final SoftwareModuleType softwareModuleType) {
|
||||
return elements.stream().filter(element -> !element.isMandatory())
|
||||
.filter(element -> element.getSmType().equals(softwareModuleType)).findFirst().isPresent();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsOptionalModuleType(final Long softwareModuleTypeId) {
|
||||
return elements.stream().filter(element -> !element.isMandatory())
|
||||
.filter(element -> element.getSmType().getId().equals(softwareModuleTypeId)).findFirst().isPresent();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areModuleEntriesIdentical(final DistributionSetType dsType) {
|
||||
return new HashSet<DistributionSetTypeElement>(((JpaDistributionSetType) dsType).elements).equals(elements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSetType addOptionalModuleType(final SoftwareModuleType smType) {
|
||||
elements.add(new DistributionSetTypeElement(this, (JpaSoftwareModuleType) smType, false));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSetType addMandatoryModuleType(final SoftwareModuleType smType) {
|
||||
elements.add(new DistributionSetTypeElement(this, (JpaSoftwareModuleType) smType, true));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSetType removeModuleType(final Long smTypeId) {
|
||||
// we search by id (standard equals compares also revison)
|
||||
final Optional<DistributionSetTypeElement> found = elements.stream()
|
||||
.filter(element -> element.getSmType().getId().equals(smTypeId)).findFirst();
|
||||
|
||||
if (found.isPresent()) {
|
||||
elements.remove(found.get());
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKey(final String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkComplete(final DistributionSet distributionSet) {
|
||||
return distributionSet.getModules().stream().map(module -> module.getType()).collect(Collectors.toList())
|
||||
.containsAll(getMandatoryModuleTypes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColour() {
|
||||
return colour;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColour(final String colour) {
|
||||
this.colour = colour;
|
||||
}
|
||||
|
||||
public Set<DistributionSetTypeElement> getElements() {
|
||||
return elements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DistributionSetType [key=" + key + ", isDeleted()=" + isDeleted() + ", getId()=" + getId() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ConstraintMode;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.ExternalArtifact;
|
||||
import org.eclipse.hawkbit.repository.model.ExternalArtifactProvider;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
|
||||
/**
|
||||
* External artifact representation with all the necessary information to
|
||||
* generate an artifact {@link URL} at runtime.
|
||||
*
|
||||
*/
|
||||
@Table(name = "sp_external_artifact", indexes = {
|
||||
@Index(name = "sp_idx_external_artifact_prim", columnList = "id,tenant") })
|
||||
@Entity
|
||||
public class JpaExternalArtifact extends AbstractJpaArtifact implements ExternalArtifact {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "provider", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_art_to_ext_provider"))
|
||||
private JpaExternalArtifactProvider externalArtifactProvider;
|
||||
|
||||
@Column(name = "url_suffix", length = 512)
|
||||
private String urlSuffix;
|
||||
|
||||
// CascadeType.PERSIST as we register ourself at the BSM
|
||||
@ManyToOne(optional = false, cascade = { CascadeType.PERSIST })
|
||||
@JoinColumn(name = "software_module", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_external_assigned_sm"))
|
||||
private JpaSoftwareModule softwareModule;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public JpaExternalArtifact() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs {@link ExternalArtifact}.
|
||||
*
|
||||
* @param externalArtifactProvider
|
||||
* of the artifact
|
||||
* @param urlSuffix
|
||||
* of the artifact
|
||||
* @param softwareModule
|
||||
* of the artifact
|
||||
*/
|
||||
public JpaExternalArtifact(@NotNull final ExternalArtifactProvider externalArtifactProvider, final String urlSuffix,
|
||||
final SoftwareModule softwareModule) {
|
||||
setSoftwareModule(softwareModule);
|
||||
this.externalArtifactProvider = (JpaExternalArtifactProvider) externalArtifactProvider;
|
||||
|
||||
if (urlSuffix != null) {
|
||||
this.urlSuffix = urlSuffix;
|
||||
} else {
|
||||
this.urlSuffix = externalArtifactProvider.getDefaultSuffix();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the softwareModule
|
||||
*/
|
||||
@Override
|
||||
public SoftwareModule getSoftwareModule() {
|
||||
return softwareModule;
|
||||
}
|
||||
|
||||
public final void setSoftwareModule(final SoftwareModule softwareModule) {
|
||||
this.softwareModule = (JpaSoftwareModule) softwareModule;
|
||||
this.softwareModule.addArtifact(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExternalArtifactProvider getExternalArtifactProvider() {
|
||||
return externalArtifactProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() {
|
||||
return new StringBuilder().append(externalArtifactProvider.getBasePath()).append(urlSuffix).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrlSuffix() {
|
||||
return urlSuffix;
|
||||
}
|
||||
|
||||
public void setExternalArtifactProvider(final JpaExternalArtifactProvider externalArtifactProvider) {
|
||||
this.externalArtifactProvider = externalArtifactProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param urlSuffix
|
||||
* the urlSuffix to set
|
||||
*/
|
||||
@Override
|
||||
public void setUrlSuffix(final String urlSuffix) {
|
||||
this.urlSuffix = urlSuffix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() { // NOSONAR - as this is generated
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + this.getClass().getName().hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) { // NOSONAR - as this is generated
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof JpaExternalArtifact)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.ExternalArtifact;
|
||||
import org.eclipse.hawkbit.repository.model.ExternalArtifactProvider;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link ExternalArtifactProvider}.
|
||||
*
|
||||
*/
|
||||
@Table(name = "sp_external_provider", indexes = {
|
||||
@Index(name = "sp_idx_external_provider_prim", columnList = "tenant,id") })
|
||||
@Entity
|
||||
public class JpaExternalArtifactProvider extends AbstractJpaNamedEntity implements ExternalArtifactProvider {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "base_url", length = 512, nullable = false)
|
||||
private String basePath;
|
||||
|
||||
@Column(name = "default_url_suffix", length = 512, nullable = true)
|
||||
private String defaultSuffix;
|
||||
|
||||
/**
|
||||
* Constructs {@link ExternalArtifactProvider} based on given properties.
|
||||
*
|
||||
* @param name
|
||||
* of the provided
|
||||
* @param description
|
||||
* which is optional
|
||||
* @param baseURL
|
||||
* of all {@link ExternalArtifact}s of the provider
|
||||
* @param defaultUrlSuffix
|
||||
* that is used if {@link ExternalArtifact#getUrlSuffix()} is
|
||||
* empty.
|
||||
*/
|
||||
public JpaExternalArtifactProvider(final String name, final String description, final String baseURL,
|
||||
final String defaultUrlSuffix) {
|
||||
super(name, description);
|
||||
basePath = baseURL;
|
||||
defaultSuffix = defaultUrlSuffix;
|
||||
}
|
||||
|
||||
JpaExternalArtifactProvider() {
|
||||
super();
|
||||
defaultSuffix = "";
|
||||
basePath = "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBasePath() {
|
||||
return basePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultSuffix() {
|
||||
return defaultSuffix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBasePath(final String basePath) {
|
||||
this.basePath = basePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultSuffix(final String defaultSuffix) {
|
||||
this.defaultSuffix = defaultSuffix;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ConstraintMode;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.LocalArtifact;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
|
||||
import com.mongodb.gridfs.GridFS;
|
||||
import com.mongodb.gridfs.GridFSFile;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link LocalArtifact}.
|
||||
*
|
||||
*/
|
||||
@Table(name = "sp_artifact", indexes = { @Index(name = "sp_idx_artifact_01", columnList = "tenant,software_module"),
|
||||
@Index(name = "sp_idx_artifact_prim", columnList = "tenant,id") })
|
||||
@Entity
|
||||
public class JpaLocalArtifact extends AbstractJpaArtifact implements LocalArtifact {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "gridfs_file_name", length = 40)
|
||||
private String gridFsFileName;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "provided_file_name", length = 256)
|
||||
private String filename;
|
||||
|
||||
@ManyToOne(optional = false, cascade = { CascadeType.PERSIST })
|
||||
@JoinColumn(name = "software_module", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_assigned_sm"))
|
||||
private JpaSoftwareModule softwareModule;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public JpaLocalArtifact() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs artifact.
|
||||
*
|
||||
* @param gridFsFileName
|
||||
* that is the link to the {@link GridFS} entity.
|
||||
* @param filename
|
||||
* that is used by {@link GridFSFile} store.
|
||||
* @param softwareModule
|
||||
* of this artifact
|
||||
*/
|
||||
public JpaLocalArtifact(@NotNull final String gridFsFileName, @NotNull final String filename,
|
||||
final SoftwareModule softwareModule) {
|
||||
setSoftwareModule(softwareModule);
|
||||
this.gridFsFileName = gridFsFileName;
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() { // NOSONAR - as this is generated
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + this.getClass().getName().hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) { // NOSONAR - as this is generated
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof LocalArtifact)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoftwareModule getSoftwareModule() {
|
||||
return softwareModule;
|
||||
}
|
||||
|
||||
public final void setSoftwareModule(final SoftwareModule softwareModule) {
|
||||
this.softwareModule = (JpaSoftwareModule) softwareModule;
|
||||
this.softwareModule.addArtifact(this);
|
||||
}
|
||||
|
||||
public String getGridFsFileName() {
|
||||
return gridFsFileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ConstraintMode;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Transient;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
import org.eclipse.hawkbit.cache.CacheField;
|
||||
import org.eclipse.hawkbit.cache.CacheKeys;
|
||||
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.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
|
||||
|
||||
/**
|
||||
* JPA implementation of a {@link Rollout}.
|
||||
*
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sp_rollout", indexes = {
|
||||
@Index(name = "sp_idx_rollout_01", columnList = "tenant,name") }, uniqueConstraints = @UniqueConstraint(columnNames = {
|
||||
"name", "tenant" }, name = "uk_rollout"))
|
||||
public class JpaRollout extends AbstractJpaNamedEntity implements Rollout {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@OneToMany(targetEntity = JpaRolloutGroup.class)
|
||||
@JoinColumn(name = "rollout", insertable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rollout_rolloutgroup"))
|
||||
private List<RolloutGroup> rolloutGroups;
|
||||
|
||||
@Column(name = "target_filter", length = 1024, nullable = false)
|
||||
private String targetFilterQuery;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "distribution_set", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rolltout_ds"))
|
||||
private JpaDistributionSet distributionSet;
|
||||
|
||||
@Column(name = "status")
|
||||
private RolloutStatus status = RolloutStatus.CREATING;
|
||||
|
||||
@Column(name = "last_check")
|
||||
private long lastCheck;
|
||||
|
||||
@Column(name = "action_type", nullable = false)
|
||||
@Enumerated(EnumType.STRING)
|
||||
private ActionType actionType = ActionType.FORCED;
|
||||
|
||||
@Column(name = "forced_time")
|
||||
private long forcedTime;
|
||||
|
||||
@Column(name = "total_targets")
|
||||
private long totalTargets;
|
||||
|
||||
@Transient
|
||||
@CacheField(key = CacheKeys.ROLLOUT_GROUP_TOTAL)
|
||||
private int rolloutGroupsTotal;
|
||||
|
||||
@Transient
|
||||
@CacheField(key = CacheKeys.ROLLOUT_GROUP_CREATED)
|
||||
private int rolloutGroupsCreated;
|
||||
|
||||
@Transient
|
||||
private transient TotalTargetCountStatus totalTargetCountStatus;
|
||||
|
||||
@Override
|
||||
public DistributionSet getDistributionSet() {
|
||||
return distributionSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDistributionSet(final DistributionSet distributionSet) {
|
||||
this.distributionSet = (JpaDistributionSet) distributionSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RolloutGroup> getRolloutGroups() {
|
||||
return rolloutGroups;
|
||||
}
|
||||
|
||||
public void setRolloutGroups(final List<RolloutGroup> rolloutGroups) {
|
||||
this.rolloutGroups = rolloutGroups;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTargetFilterQuery() {
|
||||
return targetFilterQuery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTargetFilterQuery(final String targetFilterQuery) {
|
||||
this.targetFilterQuery = targetFilterQuery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RolloutStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(final RolloutStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public long getLastCheck() {
|
||||
return lastCheck;
|
||||
}
|
||||
|
||||
public void setLastCheck(final long lastCheck) {
|
||||
this.lastCheck = lastCheck;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionType getActionType() {
|
||||
return actionType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActionType(final ActionType actionType) {
|
||||
this.actionType = actionType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getForcedTime() {
|
||||
return forcedTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setForcedTime(final long forcedTime) {
|
||||
this.forcedTime = forcedTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalTargets() {
|
||||
return totalTargets;
|
||||
}
|
||||
|
||||
public void setTotalTargets(final long totalTargets) {
|
||||
this.totalTargets = totalTargets;
|
||||
}
|
||||
|
||||
public int getRolloutGroupsTotal() {
|
||||
return rolloutGroupsTotal;
|
||||
}
|
||||
|
||||
public void setRolloutGroupsTotal(final int rolloutGroupsTotal) {
|
||||
this.rolloutGroupsTotal = rolloutGroupsTotal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRolloutGroupsCreated() {
|
||||
return rolloutGroupsCreated;
|
||||
}
|
||||
|
||||
public void setRolloutGroupsCreated(final int rolloutGroupsCreated) {
|
||||
this.rolloutGroupsCreated = rolloutGroupsCreated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TotalTargetCountStatus getTotalTargetCountStatus() {
|
||||
if (totalTargetCountStatus == null) {
|
||||
totalTargetCountStatus = new TotalTargetCountStatus(totalTargets);
|
||||
}
|
||||
return totalTargetCountStatus;
|
||||
}
|
||||
|
||||
public void setTotalTargetCountStatus(final TotalTargetCountStatus totalTargetCountStatus) {
|
||||
this.totalTargetCountStatus = totalTargetCountStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Rollout [rolloutGroups=" + rolloutGroups + ", targetFilterQuery=" + targetFilterQuery
|
||||
+ ", distributionSet=" + distributionSet + ", status=" + status + ", lastCheck=" + lastCheck
|
||||
+ ", getName()=" + getName() + ", getId()=" + getId() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ConstraintMode;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Transient;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
|
||||
|
||||
/**
|
||||
* JPA entity definition of persisting a group of an rollout.
|
||||
*
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sp_rolloutgroup", indexes = {
|
||||
@Index(name = "sp_idx_rolloutgroup_01", columnList = "tenant,name") }, uniqueConstraints = @UniqueConstraint(columnNames = {
|
||||
"name", "rollout", "tenant" }, name = "uk_rolloutgroup"))
|
||||
public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGroup {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "rollout", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rolloutgroup_rollout"))
|
||||
private JpaRollout rollout;
|
||||
|
||||
@Column(name = "status")
|
||||
private RolloutGroupStatus status = RolloutGroupStatus.READY;
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST }, targetEntity = RolloutTargetGroup.class)
|
||||
@JoinColumn(name = "rolloutGroup_Id", insertable = false, updatable = false)
|
||||
private final List<RolloutTargetGroup> rolloutTargetGroup = new ArrayList<>();
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
private JpaRolloutGroup parent;
|
||||
|
||||
@Column(name = "success_condition", nullable = false)
|
||||
private RolloutGroupSuccessCondition successCondition = RolloutGroupSuccessCondition.THRESHOLD;
|
||||
|
||||
@Column(name = "success_condition_exp", length = 512, nullable = false)
|
||||
private String successConditionExp;
|
||||
|
||||
@Column(name = "success_action", nullable = false)
|
||||
private RolloutGroupSuccessAction successAction = RolloutGroupSuccessAction.NEXTGROUP;
|
||||
|
||||
@Column(name = "success_action_exp", length = 512, nullable = false)
|
||||
private String successActionExp;
|
||||
|
||||
@Column(name = "error_condition")
|
||||
private RolloutGroupErrorCondition errorCondition;
|
||||
|
||||
@Column(name = "error_condition_exp", length = 512)
|
||||
private String errorConditionExp;
|
||||
|
||||
@Column(name = "error_action")
|
||||
private RolloutGroupErrorAction errorAction;
|
||||
|
||||
@Column(name = "error_action_exp", length = 512)
|
||||
private String errorActionExp;
|
||||
|
||||
@Column(name = "total_targets")
|
||||
private long totalTargets;
|
||||
|
||||
@Transient
|
||||
private transient TotalTargetCountStatus totalTargetCountStatus;
|
||||
|
||||
@Override
|
||||
public Rollout getRollout() {
|
||||
return rollout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRollout(final Rollout rollout) {
|
||||
this.rollout = (JpaRollout) rollout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RolloutGroupStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatus(final RolloutGroupStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public List<RolloutTargetGroup> getRolloutTargetGroup() {
|
||||
return rolloutTargetGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RolloutGroup getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(final RolloutGroup parent) {
|
||||
this.parent = (JpaRolloutGroup) parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RolloutGroupSuccessCondition getSuccessCondition() {
|
||||
return successCondition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSuccessCondition(final RolloutGroupSuccessCondition finishCondition) {
|
||||
successCondition = finishCondition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSuccessConditionExp() {
|
||||
return successConditionExp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSuccessConditionExp(final String finishExp) {
|
||||
successConditionExp = finishExp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RolloutGroupErrorCondition getErrorCondition() {
|
||||
return errorCondition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setErrorCondition(final RolloutGroupErrorCondition errorCondition) {
|
||||
this.errorCondition = errorCondition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorConditionExp() {
|
||||
return errorConditionExp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setErrorConditionExp(final String errorExp) {
|
||||
errorConditionExp = errorExp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RolloutGroupErrorAction getErrorAction() {
|
||||
return errorAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setErrorAction(final RolloutGroupErrorAction errorAction) {
|
||||
this.errorAction = errorAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorActionExp() {
|
||||
return errorActionExp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setErrorActionExp(final String errorActionExp) {
|
||||
this.errorActionExp = errorActionExp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RolloutGroupSuccessAction getSuccessAction() {
|
||||
return successAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSuccessActionExp() {
|
||||
return successActionExp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalTargets() {
|
||||
return totalTargets;
|
||||
}
|
||||
|
||||
public void setTotalTargets(final long totalTargets) {
|
||||
this.totalTargets = totalTargets;
|
||||
}
|
||||
|
||||
public void setSuccessAction(final RolloutGroupSuccessAction successAction) {
|
||||
this.successAction = successAction;
|
||||
}
|
||||
|
||||
public void setSuccessActionExp(final String successActionExp) {
|
||||
this.successActionExp = successActionExp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the totalTargetCountStatus
|
||||
*/
|
||||
@Override
|
||||
public TotalTargetCountStatus getTotalTargetCountStatus() {
|
||||
if (totalTargetCountStatus == null) {
|
||||
totalTargetCountStatus = new TotalTargetCountStatus(totalTargets);
|
||||
}
|
||||
return totalTargetCountStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param totalTargetCountStatus
|
||||
* the totalTargetCountStatus to set
|
||||
*/
|
||||
@Override
|
||||
public void setTotalTargetCountStatus(final TotalTargetCountStatus totalTargetCountStatus) {
|
||||
this.totalTargetCountStatus = totalTargetCountStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RolloutGroup [rollout=" + rollout + ", status=" + status + ", rolloutTargetGroup=" + rolloutTargetGroup
|
||||
+ ", parent=" + parent + ", finishCondition=" + successCondition + ", finishExp=" + successConditionExp
|
||||
+ ", errorCondition=" + errorCondition + ", errorExp=" + errorConditionExp + ", getName()=" + getName()
|
||||
+ ", getId()=" + getId() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ConstraintMode;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.NamedAttributeNode;
|
||||
import javax.persistence.NamedEntityGraph;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.ExternalArtifact;
|
||||
import org.eclipse.hawkbit.repository.model.LocalArtifact;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.persistence.annotations.CascadeOnDelete;
|
||||
|
||||
/**
|
||||
* Base Software Module that is supported by OS level provisioning mechanism on
|
||||
* the edge controller, e.g. OS, JVM, AgentHub.
|
||||
*
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sp_base_software_module", uniqueConstraints = @UniqueConstraint(columnNames = { "module_type", "name",
|
||||
"version", "tenant" }, name = "uk_base_sw_mod"), indexes = {
|
||||
@Index(name = "sp_idx_base_sw_module_01", columnList = "tenant,deleted,name,version"),
|
||||
@Index(name = "sp_idx_base_sw_module_02", columnList = "tenant,deleted,module_type"),
|
||||
@Index(name = "sp_idx_base_sw_module_prim", columnList = "tenant,id") })
|
||||
@NamedEntityGraph(name = "SoftwareModule.artifacts", attributeNodes = { @NamedAttributeNode("artifacts") })
|
||||
public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implements SoftwareModule {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "module_type", nullable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_module_type"))
|
||||
private JpaSoftwareModuleType type;
|
||||
|
||||
@ManyToMany(mappedBy = "modules", targetEntity = JpaDistributionSet.class, fetch = FetchType.LAZY)
|
||||
private final List<DistributionSet> assignedTo = new ArrayList<>();
|
||||
|
||||
@Column(name = "deleted")
|
||||
private boolean deleted;
|
||||
|
||||
@Column(name = "vendor", nullable = true, length = 256)
|
||||
private String vendor;
|
||||
|
||||
@OneToMany(mappedBy = "softwareModule", cascade = { CascadeType.ALL }, targetEntity = JpaLocalArtifact.class)
|
||||
private List<LocalArtifact> artifacts;
|
||||
|
||||
@OneToMany(mappedBy = "softwareModule", cascade = { CascadeType.ALL }, targetEntity = JpaExternalArtifact.class)
|
||||
private List<ExternalArtifact> externalArtifacts;
|
||||
|
||||
@CascadeOnDelete
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE }, targetEntity = JpaSoftwareModuleMetadata.class)
|
||||
@JoinColumn(name = "sw_id", insertable = false, updatable = false)
|
||||
private final List<SoftwareModuleMetadata> metadata = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public JpaSoftwareModule() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* parameterized constructor.
|
||||
*
|
||||
* @param type
|
||||
* of the {@link SoftwareModule}
|
||||
* @param name
|
||||
* abstract name of the {@link SoftwareModule}
|
||||
* @param version
|
||||
* of the {@link SoftwareModule}
|
||||
* @param description
|
||||
* of the {@link SoftwareModule}
|
||||
* @param vendor
|
||||
* of the {@link SoftwareModule}
|
||||
*/
|
||||
public JpaSoftwareModule(final SoftwareModuleType type, final String name, final String version,
|
||||
final String description, final String vendor) {
|
||||
super(name, version, description);
|
||||
this.vendor = vendor;
|
||||
this.type = (JpaSoftwareModuleType) type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param artifact
|
||||
* is added to the assigned {@link Artifact}s.
|
||||
*/
|
||||
@Override
|
||||
public void addArtifact(final LocalArtifact artifact) {
|
||||
if (null == artifacts) {
|
||||
artifacts = new ArrayList<>(4);
|
||||
}
|
||||
|
||||
if (!artifacts.contains(artifact)) {
|
||||
artifacts.add(artifact);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param artifact
|
||||
* is added to the assigned {@link Artifact}s.
|
||||
*/
|
||||
@Override
|
||||
public void addArtifact(final ExternalArtifact artifact) {
|
||||
if (null == externalArtifacts) {
|
||||
externalArtifacts = new ArrayList<>(4);
|
||||
}
|
||||
|
||||
if (!externalArtifacts.contains(artifact)) {
|
||||
externalArtifacts.add(artifact);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param artifactId
|
||||
* to look for
|
||||
* @return found {@link Artifact}
|
||||
*/
|
||||
@Override
|
||||
public Optional<LocalArtifact> getLocalArtifact(final Long artifactId) {
|
||||
if (null == artifacts) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return artifacts.stream().filter(artifact -> artifact.getId().equals(artifactId)).findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileName
|
||||
* to look for
|
||||
* @return found {@link Artifact}
|
||||
*/
|
||||
@Override
|
||||
public Optional<LocalArtifact> getLocalArtifactByFilename(final String fileName) {
|
||||
if (null == artifacts) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return artifacts.stream().filter(artifact -> artifact.getFilename().equalsIgnoreCase(fileName.trim()))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the artifacts
|
||||
*/
|
||||
@Override
|
||||
public List<Artifact> getArtifacts() {
|
||||
final List<Artifact> result = new ArrayList<>();
|
||||
result.addAll(artifacts);
|
||||
result.addAll(externalArtifacts);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return local artifacts only
|
||||
*/
|
||||
@Override
|
||||
public List<LocalArtifact> getLocalArtifacts() {
|
||||
if (artifacts == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return artifacts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVendor() {
|
||||
return vendor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param artifact
|
||||
* is removed from the assigned {@link LocalArtifact}s.
|
||||
*/
|
||||
@Override
|
||||
public void removeArtifact(final LocalArtifact artifact) {
|
||||
if (null != artifacts) {
|
||||
artifacts.remove(artifact);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param artifact
|
||||
* is removed from the assigned {@link ExternalArtifact}s.
|
||||
*/
|
||||
@Override
|
||||
public void removeArtifact(final ExternalArtifact artifact) {
|
||||
if (null != externalArtifacts) {
|
||||
externalArtifacts.remove(artifact);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVendor(final String vendor) {
|
||||
this.vendor = vendor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoftwareModuleType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDeleted(final boolean deleted) {
|
||||
this.deleted = deleted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setType(final SoftwareModuleType type) {
|
||||
this.type = (JpaSoftwareModuleType) type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return immutable list of meta data elements.
|
||||
*/
|
||||
@Override
|
||||
public List<SoftwareModuleMetadata> getMetadata() {
|
||||
return Collections.unmodifiableList(metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SoftwareModule [deleted=" + deleted + ", name=" + getName() + ", version=" + getVersion()
|
||||
+ ", revision=" + getOptLockRevision() + ", Id=" + getId() + ", type=" + getType().getName() + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the assignedTo
|
||||
*/
|
||||
@Override
|
||||
public List<DistributionSet> getAssignedTo() {
|
||||
return assignedTo;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import javax.persistence.ConstraintMode;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||
|
||||
/**
|
||||
* Metadata for {@link SoftwareModule}.
|
||||
*
|
||||
*/
|
||||
@IdClass(SwMetadataCompositeKey.class)
|
||||
@Entity
|
||||
@Table(name = "sp_sw_metadata")
|
||||
public class JpaSoftwareModuleMetadata extends AbstractJpaMetaData implements SoftwareModuleMetadata {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@ManyToOne(targetEntity = JpaSoftwareModule.class, fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "sw_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_metadata_sw"))
|
||||
private SoftwareModule softwareModule;
|
||||
|
||||
public JpaSoftwareModuleMetadata() {
|
||||
// default public constructor for JPA
|
||||
}
|
||||
|
||||
public JpaSoftwareModuleMetadata(final String key, final SoftwareModule softwareModule, final String value) {
|
||||
super(key, value);
|
||||
this.softwareModule = softwareModule;
|
||||
}
|
||||
|
||||
public SwMetadataCompositeKey getId() {
|
||||
return new SwMetadataCompositeKey(softwareModule, getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoftwareModule getSoftwareModule() {
|
||||
return softwareModule;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSoftwareModule(final SoftwareModule softwareModule) {
|
||||
this.softwareModule = softwareModule;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + ((softwareModule == null) ? 0 : softwareModule.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
final JpaSoftwareModuleMetadata other = (JpaSoftwareModuleMetadata) obj;
|
||||
if (softwareModule == null) {
|
||||
if (other.softwareModule != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!softwareModule.equals(other.softwareModule)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
|
||||
/**
|
||||
* Type of a software modules.
|
||||
*
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sp_software_module_type", indexes = {
|
||||
@Index(name = "sp_idx_software_module_type_01", columnList = "tenant,deleted"),
|
||||
@Index(name = "sp_idx_software_module_type_prim", columnList = "tenant,id") }, uniqueConstraints = {
|
||||
@UniqueConstraint(columnNames = { "type_key", "tenant" }, name = "uk_smt_type_key"),
|
||||
@UniqueConstraint(columnNames = { "name", "tenant" }, name = "uk_smt_name") })
|
||||
public class JpaSoftwareModuleType extends AbstractJpaNamedEntity implements SoftwareModuleType {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "type_key", nullable = false, length = 64)
|
||||
private String key;
|
||||
|
||||
@Column(name = "max_ds_assignments", nullable = false)
|
||||
private int maxAssignments;
|
||||
|
||||
@Column(name = "colour", nullable = true, length = 16)
|
||||
private String colour;
|
||||
|
||||
@Column(name = "deleted")
|
||||
private boolean deleted;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param key
|
||||
* of the type
|
||||
* @param name
|
||||
* of the type
|
||||
* @param description
|
||||
* of the type
|
||||
* @param maxAssignments
|
||||
* assignments to a DS
|
||||
*/
|
||||
public JpaSoftwareModuleType(final String key, final String name, final String description,
|
||||
final int maxAssignments) {
|
||||
this(key, name, description, maxAssignments, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param key
|
||||
* of the type
|
||||
* @param name
|
||||
* of the type
|
||||
* @param description
|
||||
* of the type
|
||||
* @param maxAssignments
|
||||
* assignments to a DS
|
||||
* @param colour
|
||||
* of the type. It will be null by default
|
||||
*/
|
||||
public JpaSoftwareModuleType(final String key, final String name, final String description,
|
||||
final int maxAssignments, final String colour) {
|
||||
super();
|
||||
this.key = key;
|
||||
this.maxAssignments = maxAssignments;
|
||||
setDescription(description);
|
||||
setName(name);
|
||||
this.colour = colour;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default Constructor for JPA.
|
||||
*/
|
||||
public JpaSoftwareModuleType() {
|
||||
// Default Constructor for JPA.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxAssignments(final int maxAssignments) {
|
||||
this.maxAssignments = maxAssignments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxAssignments() {
|
||||
return maxAssignments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
public void setDeleted(final boolean deleted) {
|
||||
this.deleted = deleted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColour() {
|
||||
return colour;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColour(final String colour) {
|
||||
this.colour = colour;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SoftwareModuleType [key=" + key + ", getName()=" + getName() + ", getId()=" + getId() + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKey(final String key) {
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ConstraintMode;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.NamedAttributeNode;
|
||||
import javax.persistence.NamedEntityGraph;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.PrimaryKeyJoinColumn;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Transient;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
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.TargetTag;
|
||||
import org.eclipse.hawkbit.repository.model.helper.SecurityChecker;
|
||||
import org.eclipse.hawkbit.repository.model.helper.SecurityTokenGeneratorHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.SystemSecurityContextHolder;
|
||||
import org.eclipse.persistence.annotations.CascadeOnDelete;
|
||||
import org.springframework.data.domain.Persistable;
|
||||
|
||||
/**
|
||||
* JPA implementation of {@link Target}.
|
||||
*
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sp_target", indexes = {
|
||||
@Index(name = "sp_idx_target_01", columnList = "tenant,name,assigned_distribution_set"),
|
||||
@Index(name = "sp_idx_target_02", columnList = "tenant,name"),
|
||||
@Index(name = "sp_idx_target_03", columnList = "tenant,controller_id,assigned_distribution_set"),
|
||||
@Index(name = "sp_idx_target_04", columnList = "tenant,created_at"),
|
||||
@Index(name = "sp_idx_target_prim", columnList = "tenant,id") }, uniqueConstraints = @UniqueConstraint(columnNames = {
|
||||
"controller_id", "tenant" }, name = "uk_tenant_controller_id"))
|
||||
@NamedEntityGraph(name = "Target.detail", attributeNodes = { @NamedAttributeNode("tags"),
|
||||
@NamedAttributeNode(value = "assignedDistributionSet"), @NamedAttributeNode(value = "targetInfo") })
|
||||
public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Long>, Target {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "controller_id", length = 64)
|
||||
@Size(min = 1)
|
||||
@NotNull
|
||||
private String controllerId;
|
||||
|
||||
@Transient
|
||||
private boolean entityNew;
|
||||
|
||||
@ManyToMany(targetEntity = JpaTargetTag.class)
|
||||
@JoinTable(name = "sp_target_target_tag", joinColumns = {
|
||||
@JoinColumn(name = "target", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_targtag_target")) }, inverseJoinColumns = {
|
||||
@JoinColumn(name = "tag", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_targtag_tag")) })
|
||||
private Set<TargetTag> tags = new HashSet<>();
|
||||
|
||||
@CascadeOnDelete
|
||||
@OneToMany(fetch = FetchType.LAZY, orphanRemoval = true, cascade = {
|
||||
CascadeType.REMOVE }, targetEntity = JpaAction.class)
|
||||
@JoinColumn(name = "target", insertable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_act_hist_targ"))
|
||||
private final List<Action> actions = new ArrayList<>();
|
||||
|
||||
@ManyToOne(optional = true, fetch = FetchType.LAZY, targetEntity = JpaDistributionSet.class)
|
||||
@JoinColumn(name = "assigned_distribution_set", nullable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_assign_ds"))
|
||||
private JpaDistributionSet assignedDistributionSet;
|
||||
|
||||
@CascadeOnDelete
|
||||
@OneToOne(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY, targetEntity = JpaTargetInfo.class)
|
||||
@PrimaryKeyJoinColumn
|
||||
private JpaTargetInfo targetInfo;
|
||||
|
||||
/**
|
||||
* the security token of the target which allows if enabled to authenticate
|
||||
* with this security token.
|
||||
*/
|
||||
@Column(name = "sec_token", insertable = true, updatable = true, nullable = false, length = 128)
|
||||
private String securityToken;
|
||||
|
||||
@CascadeOnDelete
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE, CascadeType.PERSIST })
|
||||
@JoinColumn(name = "target_Id", insertable = false, updatable = false)
|
||||
private final List<RolloutTargetGroup> rolloutTargetGroup = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param controllerId
|
||||
* controller ID of the {@link Target}
|
||||
*/
|
||||
public JpaTarget(final String controllerId) {
|
||||
this.controllerId = controllerId;
|
||||
setName(controllerId);
|
||||
securityToken = SecurityTokenGeneratorHolder.getInstance().generateToken();
|
||||
targetInfo = new JpaTargetInfo(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* empty constructor for JPA.
|
||||
*/
|
||||
JpaTarget() {
|
||||
controllerId = null;
|
||||
securityToken = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSet getAssignedDistributionSet() {
|
||||
return assignedDistributionSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getControllerId() {
|
||||
return controllerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TargetTag> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public void setAssignedDistributionSet(final DistributionSet assignedDistributionSet) {
|
||||
this.assignedDistributionSet = (JpaDistributionSet) assignedDistributionSet;
|
||||
}
|
||||
|
||||
public void setControllerId(final String controllerId) {
|
||||
this.controllerId = controllerId;
|
||||
}
|
||||
|
||||
public void setTags(final Set<TargetTag> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> getActions() {
|
||||
return actions;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transient
|
||||
public boolean isNew() {
|
||||
return entityNew;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isNew
|
||||
* the isNew to set
|
||||
*/
|
||||
public void setNew(final boolean entityNew) {
|
||||
this.entityNew = entityNew;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the targetInfo
|
||||
*/
|
||||
@Override
|
||||
public TargetInfo getTargetInfo() {
|
||||
return targetInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param targetInfo
|
||||
* the targetInfo to set
|
||||
*/
|
||||
public void setTargetInfo(final TargetInfo targetInfo) {
|
||||
this.targetInfo = (JpaTargetInfo) targetInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the securityToken if the current security context contains the
|
||||
* necessary permission {@link SpPermission#READ_TARGET_SEC_TOKEN}
|
||||
* or the current context is executed as system code, otherwise
|
||||
* {@code null}.
|
||||
*/
|
||||
@Override
|
||||
public String getSecurityToken() {
|
||||
if (SystemSecurityContextHolder.getInstance().getSystemSecurityContext().isCurrentThreadSystemCode()
|
||||
|| SecurityChecker.hasPermission(SpPermission.READ_TARGET_SEC_TOKEN)) {
|
||||
return securityToken;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param securityToken
|
||||
* the securityToken to set
|
||||
*/
|
||||
public void setSecurityToken(final String securityToken) {
|
||||
this.securityToken = securityToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Target [controllerId=" + controllerId + ", getId()=" + getId() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
|
||||
/**
|
||||
* Stored target filter.
|
||||
*
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sp_target_filter_query", indexes = {
|
||||
@Index(name = "sp_idx_target_filter_query_01", columnList = "tenant,name") }, uniqueConstraints = @UniqueConstraint(columnNames = {
|
||||
"name", "tenant" }, name = "uk_tenant_custom_filter_name"))
|
||||
public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity implements TargetFilterQuery {
|
||||
private static final long serialVersionUID = 7493966984413479089L;
|
||||
|
||||
@Column(name = "name", length = 64)
|
||||
private String name;
|
||||
|
||||
@Column(name = "query", length = 1024)
|
||||
private String query;
|
||||
|
||||
public JpaTargetFilterQuery() {
|
||||
// Default constructor for JPA.
|
||||
}
|
||||
|
||||
/**
|
||||
* Public constructor.
|
||||
*
|
||||
* @param name
|
||||
* of the {@link TargetFilterQuery}.
|
||||
* @param query
|
||||
* of the {@link TargetFilterQuery}.
|
||||
*/
|
||||
public JpaTargetFilterQuery(final String name, final String query) {
|
||||
this.name = name;
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setQuery(final String query) {
|
||||
this.query = query;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.CollectionTable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ConstraintMode;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MapKeyColumn;
|
||||
import javax.persistence.MapsId;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.PollStatus;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetInfo;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.model.helper.SystemSecurityContextHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
|
||||
import org.eclipse.persistence.annotations.CascadeOnDelete;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.domain.Persistable;
|
||||
|
||||
/**
|
||||
* A table which contains all the information inserted, updated by the
|
||||
* controller itself. So this entity does not provide audit information because
|
||||
* changes on this entity are mostly only done by controller requests. That's
|
||||
* the reason that we store these information in a separated table so we don't
|
||||
* modifying the {@link Target} itself when a controller reports it's
|
||||
* {@link #lastTargetQuery} for example.
|
||||
*
|
||||
*/
|
||||
@Table(name = "sp_target_info", indexes = {
|
||||
@Index(name = "sp_idx_target_info_02", columnList = "target_id,update_status") })
|
||||
@Entity
|
||||
public class JpaTargetInfo implements Persistable<Long>, TargetInfo {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TargetInfo.class);
|
||||
|
||||
@Id
|
||||
private Long targetId;
|
||||
|
||||
@Transient
|
||||
private boolean entityNew;
|
||||
|
||||
@CascadeOnDelete
|
||||
@OneToOne(cascade = { CascadeType.MERGE,
|
||||
CascadeType.REMOVE }, fetch = FetchType.LAZY, targetEntity = JpaTarget.class)
|
||||
@JoinColumn(name = "target_id", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_stat_targ"))
|
||||
@MapsId
|
||||
private JpaTarget target;
|
||||
|
||||
@Column(name = "address", length = 512)
|
||||
private String address;
|
||||
|
||||
@Column(name = "last_target_query")
|
||||
private Long lastTargetQuery;
|
||||
|
||||
@Column(name = "install_date")
|
||||
private Long installationDate;
|
||||
|
||||
@Column(name = "update_status", nullable = false, length = 255)
|
||||
@Enumerated(EnumType.STRING)
|
||||
private TargetUpdateStatus updateStatus = TargetUpdateStatus.UNKNOWN;
|
||||
|
||||
@ManyToOne(optional = true, fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "installed_distribution_set", nullable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_inst_ds"))
|
||||
private JpaDistributionSet installedDistributionSet;
|
||||
|
||||
/**
|
||||
* Read only on management API. Are commited by controller.
|
||||
*/
|
||||
@ElementCollection
|
||||
@Column(name = "attribute_value", length = 128)
|
||||
@MapKeyColumn(name = "attribute_key", nullable = false, length = 32)
|
||||
@CollectionTable(name = "sp_target_attributes", joinColumns = {
|
||||
@JoinColumn(name = "target_id") }, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_attrib_target"))
|
||||
|
||||
private final Map<String, String> controllerAttributes = Collections.synchronizedMap(new HashMap<String, String>());
|
||||
|
||||
// set default request controller attributes to true, because we want to
|
||||
// request them the first
|
||||
// time
|
||||
@Column(name = "request_controller_attributes", nullable = false)
|
||||
private boolean requestControllerAttributes = true;
|
||||
|
||||
/**
|
||||
* Constructor for {@link TargetStatus}.
|
||||
*
|
||||
* @param target
|
||||
* related to this status.
|
||||
*/
|
||||
public JpaTargetInfo(final JpaTarget target) {
|
||||
this.target = target;
|
||||
targetId = target.getId();
|
||||
}
|
||||
|
||||
JpaTargetInfo() {
|
||||
target = null;
|
||||
targetId = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getId() {
|
||||
return targetId;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transient
|
||||
public boolean isNew() {
|
||||
return entityNew;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isNew
|
||||
* the isNew to set
|
||||
*/
|
||||
public void setNew(final boolean entityNew) {
|
||||
this.entityNew = entityNew;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the ipAddress
|
||||
*/
|
||||
@Override
|
||||
public URI getAddress() {
|
||||
if (address == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return URI.create(address);
|
||||
} catch (final IllegalArgumentException e) {
|
||||
LOG.warn("Invalid address provided. Cloud not be configured to URI", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param address
|
||||
* the ipAddress to set
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* If the given string violates RFC 2396
|
||||
*/
|
||||
public void setAddress(final String address) {
|
||||
// check if this is a real URI
|
||||
if (address != null) {
|
||||
URI.create(address);
|
||||
}
|
||||
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public Long getTargetId() {
|
||||
return targetId;
|
||||
}
|
||||
|
||||
public void setTargetId(final Long targetId) {
|
||||
this.targetId = targetId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Target getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public void setTarget(final JpaTarget target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getLastTargetQuery() {
|
||||
return lastTargetQuery;
|
||||
}
|
||||
|
||||
public void setLastTargetQuery(final Long lastTargetQuery) {
|
||||
this.lastTargetQuery = lastTargetQuery;
|
||||
}
|
||||
|
||||
public void setRequestControllerAttributes(final boolean requestControllerAttributes) {
|
||||
this.requestControllerAttributes = requestControllerAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getControllerAttributes() {
|
||||
return controllerAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRequestControllerAttributes() {
|
||||
return requestControllerAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getInstallationDate() {
|
||||
return installationDate;
|
||||
}
|
||||
|
||||
public void setInstallationDate(final Long installationDate) {
|
||||
this.installationDate = installationDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetUpdateStatus getUpdateStatus() {
|
||||
return updateStatus;
|
||||
}
|
||||
|
||||
public void setUpdateStatus(final TargetUpdateStatus updateStatus) {
|
||||
this.updateStatus = updateStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSet getInstalledDistributionSet() {
|
||||
return installedDistributionSet;
|
||||
}
|
||||
|
||||
public void setInstalledDistributionSet(final JpaDistributionSet installedDistributionSet) {
|
||||
this.installedDistributionSet = installedDistributionSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the poll time which holds the last poll time of the target, the
|
||||
* next poll time and the overdue time. In case the
|
||||
* {@link #lastTargetQuery} is not set e.g. the target never polled
|
||||
* before this method returns {@code null}
|
||||
*/
|
||||
@Override
|
||||
public PollStatus getPollStatus() {
|
||||
if (lastTargetQuery == null) {
|
||||
return null;
|
||||
}
|
||||
return SystemSecurityContextHolder.getInstance().getSystemSecurityContext().runAsSystem(() -> {
|
||||
final Duration pollTime = DurationHelper.formattedStringToDuration(TenantConfigurationManagementHolder
|
||||
.getInstance().getTenantConfigurationManagement()
|
||||
.getConfigurationValue(TenantConfigurationKey.POLLING_TIME_INTERVAL, String.class).getValue());
|
||||
final Duration overdueTime = DurationHelper.formattedStringToDuration(
|
||||
TenantConfigurationManagementHolder.getInstance().getTenantConfigurationManagement()
|
||||
.getConfigurationValue(TenantConfigurationKey.POLLING_OVERDUE_TIME_INTERVAL, String.class)
|
||||
.getValue());
|
||||
final LocalDateTime currentDate = LocalDateTime.now();
|
||||
final LocalDateTime lastPollDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(lastTargetQuery),
|
||||
ZoneId.systemDefault());
|
||||
final LocalDateTime nextPollDate = lastPollDate.plus(pollTime);
|
||||
final LocalDateTime overdueDate = nextPollDate.plus(overdueTime);
|
||||
return new PollStatus(lastPollDate, nextPollDate, overdueDate, currentDate);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((target == null) ? 0 : target.hashCode());
|
||||
result = prime * result + ((targetId == null) ? 0 : targetId.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof TargetInfo)) {
|
||||
return false;
|
||||
}
|
||||
final JpaTargetInfo other = (JpaTargetInfo) obj;
|
||||
if (target == null) {
|
||||
if (other.target != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!target.equals(other.target)) {
|
||||
return false;
|
||||
}
|
||||
if (targetId == null) {
|
||||
if (other.targetId != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!targetId.equals(other.targetId)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
|
||||
/**
|
||||
* A {@link TargetTag} is used to describe Target attributes and use them also
|
||||
* for filtering the target list.
|
||||
*
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sp_target_tag", indexes = {
|
||||
@Index(name = "sp_idx_target_tag_prim", columnList = "tenant,id") }, uniqueConstraints = @UniqueConstraint(columnNames = {
|
||||
"name", "tenant" }, name = "uk_targ_tag"))
|
||||
public class JpaTargetTag extends AbstractJpaTag implements TargetTag {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ManyToMany(mappedBy = "tags", targetEntity = JpaTarget.class, fetch = FetchType.LAZY)
|
||||
private List<Target> assignedToTargets;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param name
|
||||
* of {@link TargetTag}
|
||||
* @param description
|
||||
* of {@link TargetTag}
|
||||
* @param colour
|
||||
* of {@link TargetTag}
|
||||
*/
|
||||
public JpaTargetTag(final String name, final String description, final String colour) {
|
||||
super(name, description, colour);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public constructor.
|
||||
*
|
||||
* @param name
|
||||
* of the {@link TargetTag}
|
||||
**/
|
||||
public JpaTargetTag(final String name) {
|
||||
super(name, null, null);
|
||||
}
|
||||
|
||||
public JpaTargetTag() {
|
||||
// Default constructor for JPA.
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Target> getAssignedToTargets() {
|
||||
return assignedToTargets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + this.getClass().getName().hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof TargetTag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.TenantConfiguration;
|
||||
|
||||
/**
|
||||
* A JPA entity which stores the tenant specific configuration.
|
||||
*
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sp_tenant_configuration", uniqueConstraints = @UniqueConstraint(columnNames = { "conf_key",
|
||||
"tenant" }, name = "uk_tenant_key"))
|
||||
public class JpaTenantConfiguration extends AbstractJpaTenantAwareBaseEntity implements TenantConfiguration {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "conf_key", length = 128)
|
||||
private String key;
|
||||
|
||||
@Column(name = "conf_value", length = 512)
|
||||
@Basic
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* JPA default constructor.
|
||||
*/
|
||||
public JpaTenantConfiguration() {
|
||||
// JPA default constructor.
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key
|
||||
* the key of this configuration
|
||||
* @param value
|
||||
* the value of this configuration
|
||||
*/
|
||||
public JpaTenantConfiguration(final String key, final String value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKey(final String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(final String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ConstraintMode;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.eclipse.hawkbit.repository.model.TenantMetaData;
|
||||
|
||||
/**
|
||||
* Tenant entity with meta data that is configured globally for the entire
|
||||
* tenant. This entity is not tenant aware to allow the system to access it
|
||||
* through the {@link EntityManager} even before the actual tenant exists.
|
||||
*
|
||||
* Entities owned by the tenant are based on {@link TenantAwareBaseEntity}.
|
||||
*
|
||||
*/
|
||||
@Table(name = "sp_tenant", indexes = {
|
||||
@Index(name = "sp_idx_tenant_prim", columnList = "tenant,id") }, uniqueConstraints = {
|
||||
@UniqueConstraint(columnNames = { "tenant" }, name = "uk_tenantmd_tenant") })
|
||||
@Entity
|
||||
public class JpaTenantMetaData extends AbstractJpaBaseEntity implements TenantMetaData {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "tenant", nullable = false, length = 40)
|
||||
private String tenant;
|
||||
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "default_ds_type", nullable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_tenant_md_default_ds_type"))
|
||||
private JpaDistributionSetType defaultDsType;
|
||||
|
||||
/**
|
||||
* Default constructor needed for JPA entities.
|
||||
*/
|
||||
public JpaTenantMetaData() {
|
||||
// Default constructor needed for JPA entities.
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard constructor.
|
||||
*
|
||||
* @param defaultDsType
|
||||
* of this tenant
|
||||
* @param tenant
|
||||
*/
|
||||
public JpaTenantMetaData(final DistributionSetType defaultDsType, final String tenant) {
|
||||
super();
|
||||
this.defaultDsType = (JpaDistributionSetType) defaultDsType;
|
||||
this.tenant = tenant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSetType getDefaultDsType() {
|
||||
return defaultDsType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultDsType(final DistributionSetType defaultDsType) {
|
||||
this.defaultDsType = (JpaDistributionSetType) defaultDsType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTenant() {
|
||||
return tenant;
|
||||
}
|
||||
|
||||
public void setTenant(final String tenant) {
|
||||
this.tenant = tenant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + this.getClass().getName().hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof TenantMetaData)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.ConstraintMode;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinColumns;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.persistence.annotations.ExistenceChecking;
|
||||
import org.eclipse.persistence.annotations.ExistenceType;
|
||||
|
||||
/**
|
||||
* Entity with JPA annotation to store the information which {@link Target} is
|
||||
* in a specific {@link RolloutGroup}.
|
||||
*
|
||||
*/
|
||||
@IdClass(RolloutTargetGroupId.class)
|
||||
@Entity
|
||||
@Table(name = "sp_rollouttargetgroup")
|
||||
@ExistenceChecking(ExistenceType.ASSUME_NON_EXISTENCE)
|
||||
public class RolloutTargetGroup implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@ManyToOne(targetEntity = JpaRolloutGroup.class, fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST })
|
||||
@JoinColumn(name = "rolloutGroup_Id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rollouttargetgroup_group"))
|
||||
private RolloutGroup rolloutGroup;
|
||||
|
||||
@Id
|
||||
@ManyToOne(targetEntity = JpaTarget.class, fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST })
|
||||
@JoinColumn(name = "target_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_rollouttargetgroup_target"))
|
||||
private JpaTarget target;
|
||||
|
||||
@OneToMany(targetEntity = JpaAction.class, fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST })
|
||||
@JoinColumns(value = { @JoinColumn(name = "rolloutgroup", referencedColumnName = "rolloutGroup_Id"),
|
||||
@JoinColumn(name = "target", referencedColumnName = "target_id") })
|
||||
private List<Action> actions;
|
||||
|
||||
/**
|
||||
* default constructor for JPA.
|
||||
*/
|
||||
public RolloutTargetGroup() {
|
||||
// JPA constructor
|
||||
}
|
||||
|
||||
public RolloutTargetGroup(final RolloutGroup rolloutGroup, final Target target) {
|
||||
this.rolloutGroup = rolloutGroup;
|
||||
this.target = (JpaTarget) target;
|
||||
}
|
||||
|
||||
public RolloutTargetGroupId getId() {
|
||||
return new RolloutTargetGroupId(rolloutGroup, target);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
|
||||
/**
|
||||
* Combined unique key of the table {@link RolloutTargetGroup}.
|
||||
*
|
||||
*/
|
||||
public class RolloutTargetGroupId implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long rolloutGroup;
|
||||
private Long target;
|
||||
|
||||
/**
|
||||
* default constructor necessary for JPA.
|
||||
*/
|
||||
public RolloutTargetGroupId() {
|
||||
// default constructor necessary for JPA, empty.
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param rolloutGroup
|
||||
* the rollout group for this key
|
||||
* @param target
|
||||
* the target for this key
|
||||
*/
|
||||
public RolloutTargetGroupId(final RolloutGroup rolloutGroup, final Target target) {
|
||||
this.rolloutGroup = rolloutGroup.getId();
|
||||
this.target = target.getId();
|
||||
}
|
||||
|
||||
public Long getRolloutGroup() {
|
||||
return rolloutGroup;
|
||||
}
|
||||
|
||||
public Long getTarget() {
|
||||
return target;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
|
||||
/**
|
||||
* The Software Module meta data composite key which contains the meta data key
|
||||
* and the ID of the software module itself.
|
||||
*/
|
||||
public final class SwMetadataCompositeKey implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String key;
|
||||
|
||||
private Long softwareModule;
|
||||
|
||||
/**
|
||||
* Default constructor for JPA.
|
||||
*/
|
||||
public SwMetadataCompositeKey() {
|
||||
// Default constructor for JPA.
|
||||
}
|
||||
|
||||
/**
|
||||
* @param softwareModule
|
||||
* the software module for this meta data
|
||||
* @param key
|
||||
* the key of the meta data
|
||||
*/
|
||||
public SwMetadataCompositeKey(final SoftwareModule softwareModule, final String key) {
|
||||
this.softwareModule = softwareModule.getId();
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the key
|
||||
*/
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key
|
||||
* the key to set
|
||||
*/
|
||||
public void setKey(final String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the softwareModule
|
||||
*/
|
||||
public Long getSoftwareModule() {
|
||||
return softwareModule;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param softwareModule
|
||||
* the softwareModule to set
|
||||
*/
|
||||
public void setSoftwareModule(final Long softwareModule) {
|
||||
this.softwareModule = softwareModule;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + (key == null ? 0 : key.hashCode());
|
||||
result = prime * result + (softwareModule == null ? 0 : softwareModule.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) { // NOSONAR - as this is generated
|
||||
// code
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final SwMetadataCompositeKey other = (SwMetadataCompositeKey) obj;
|
||||
if (key == null) {
|
||||
if (other.key != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!key.equals(other.key)) {
|
||||
return false;
|
||||
}
|
||||
if (softwareModule == null) {
|
||||
if (other.softwareModule != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!softwareModule.equals(other.softwareModule)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.specifications;
|
||||
|
||||
import javax.persistence.criteria.Join;
|
||||
import javax.persistence.criteria.ListJoin;
|
||||
import javax.persistence.criteria.SetJoin;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaLocalArtifact;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaLocalArtifact_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule_;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.LocalArtifact;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
/**
|
||||
* Utility class for {@link Action}s {@link Specification}s. The class provides
|
||||
* Spring Data JPQL Specifications.
|
||||
*
|
||||
*/
|
||||
public final class ActionSpecifications {
|
||||
|
||||
private ActionSpecifications() {
|
||||
// utility class
|
||||
}
|
||||
|
||||
/**
|
||||
* Specification which joins all necessary tables to retrieve the dependency
|
||||
* between a target and a local file assignment through the assigen action
|
||||
* of the target. All actions are included, not only active actions.
|
||||
*
|
||||
* @param target
|
||||
* the target to verfiy if the given artifact is currently
|
||||
* assigned or had been assigned
|
||||
* @param localArtifact
|
||||
* the local artifact to check wherever the target had ever been
|
||||
* assigned
|
||||
* @return a specification to use with spring JPA
|
||||
*/
|
||||
public static Specification<JpaAction> hasTargetAssignedArtifact(final Target target,
|
||||
final LocalArtifact localArtifact) {
|
||||
return (actionRoot, query, criteriaBuilder) -> {
|
||||
final Join<JpaAction, JpaDistributionSet> dsJoin = actionRoot.join(JpaAction_.distributionSet);
|
||||
final SetJoin<JpaDistributionSet, JpaSoftwareModule> modulesJoin = dsJoin.join(JpaDistributionSet_.modules);
|
||||
final ListJoin<JpaSoftwareModule, JpaLocalArtifact> artifactsJoin = modulesJoin
|
||||
.join(JpaSoftwareModule_.artifacts);
|
||||
return criteriaBuilder.and(
|
||||
criteriaBuilder.equal(artifactsJoin.get(JpaLocalArtifact_.filename), localArtifact.getFilename()),
|
||||
criteriaBuilder.equal(actionRoot.get(JpaAction_.target), target));
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.specifications;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.Join;
|
||||
import javax.persistence.criteria.JoinType;
|
||||
import javax.persistence.criteria.ListJoin;
|
||||
import javax.persistence.criteria.Path;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.SetJoin;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
/**
|
||||
* Specifications class for {@link DistributionSet}s. The class provides Spring
|
||||
* Data JPQL Specifications.
|
||||
*
|
||||
*/
|
||||
public final class DistributionSetSpecification {
|
||||
private DistributionSetSpecification() {
|
||||
// utility class
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link DistributionSet}s by its
|
||||
* DELETED attribute.
|
||||
*
|
||||
* @param isDeleted
|
||||
* TRUE/FALSE are compared to the attribute DELETED. If NULL the
|
||||
* attribute is ignored
|
||||
* @return the {@link DistributionSet} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaDistributionSet> isDeleted(final Boolean isDeleted) {
|
||||
return (targetRoot, query, cb) -> cb.equal(targetRoot.<Boolean> get(JpaDistributionSet_.deleted), isDeleted);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link DistributionSet}s by its
|
||||
* COMPLETED attribute.
|
||||
*
|
||||
* @param isCompleted
|
||||
* TRUE/FALSE are compared to the attribute COMPLETED. If NULL
|
||||
* the attribute is ignored
|
||||
* @return the {@link DistributionSet} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaDistributionSet> isCompleted(final Boolean isCompleted) {
|
||||
return (targetRoot, query, cb) -> cb.equal(targetRoot.<Boolean> get(JpaDistributionSet_.complete), isCompleted);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link DistributionSet} with given
|
||||
* {@link DistributionSet#getId()}.
|
||||
*
|
||||
* @param distid
|
||||
* to search
|
||||
* @return the {@link DistributionSet} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaDistributionSet> byId(final Long distid) {
|
||||
return (targetRoot, query, cb) -> {
|
||||
final Predicate predicate = cb.equal(targetRoot.<Long> get(JpaDistributionSet_.id), distid);
|
||||
targetRoot.fetch(JpaDistributionSet_.modules, JoinType.LEFT);
|
||||
targetRoot.fetch(JpaDistributionSet_.tags, JoinType.LEFT);
|
||||
targetRoot.fetch(JpaDistributionSet_.type, JoinType.LEFT);
|
||||
query.distinct(true);
|
||||
|
||||
return predicate;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link DistributionSet} with given
|
||||
* {@link DistributionSet#getId()}s.
|
||||
*
|
||||
* @param distids
|
||||
* to search
|
||||
* @return the {@link DistributionSet} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaDistributionSet> byIds(final Collection<Long> distids) {
|
||||
return (targetRoot, query, cb) -> {
|
||||
final Predicate predicate = targetRoot.<Long> get(JpaDistributionSet_.id).in(distids);
|
||||
targetRoot.fetch(JpaDistributionSet_.modules, JoinType.LEFT);
|
||||
targetRoot.fetch(JpaDistributionSet_.tags, JoinType.LEFT);
|
||||
targetRoot.fetch(JpaDistributionSet_.type, JoinType.LEFT);
|
||||
query.distinct(true);
|
||||
return predicate;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link DistributionSet}s by
|
||||
* "like name or like description or like version".
|
||||
*
|
||||
* @param subString
|
||||
* to be filtered on
|
||||
* @return the {@link DistributionSet} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaDistributionSet> likeNameOrDescriptionOrVersion(final String subString) {
|
||||
return (targetRoot, query, cb) -> cb.or(
|
||||
cb.like(cb.lower(targetRoot.<String> get(JpaDistributionSet_.name)), subString.toLowerCase()),
|
||||
cb.like(cb.lower(targetRoot.<String> get(JpaDistributionSet_.version)), subString.toLowerCase()),
|
||||
cb.like(cb.lower(targetRoot.<String> get(JpaDistributionSet_.description)), subString.toLowerCase()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link DistributionSet}s by
|
||||
* "has at least one of the given tag names".
|
||||
*
|
||||
* @param tagNames
|
||||
* to be filtered on
|
||||
* @param selectDSWithNoTag
|
||||
* flag to select distribution sets with no tag
|
||||
* @return the {@link DistributionSet} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaDistributionSet> hasTags(final Collection<String> tagNames,
|
||||
final Boolean selectDSWithNoTag) {
|
||||
return (targetRoot, query, cb) -> {
|
||||
final SetJoin<JpaDistributionSet, JpaDistributionSetTag> tags = targetRoot.join(JpaDistributionSet_.tags,
|
||||
JoinType.LEFT);
|
||||
final Predicate predicate = getPredicate(tags, tagNames, selectDSWithNoTag, cb);
|
||||
query.distinct(true);
|
||||
return predicate;
|
||||
};
|
||||
}
|
||||
|
||||
private static Predicate getPredicate(final SetJoin<JpaDistributionSet, JpaDistributionSetTag> tags,
|
||||
final Collection<String> tagNames, final Boolean selectDSWithNoTag, final CriteriaBuilder cb) {
|
||||
tags.get(JpaDistributionSetTag_.name);
|
||||
final Path<String> exp = tags.get(JpaDistributionSetTag_.name);
|
||||
if (selectDSWithNoTag != null && selectDSWithNoTag) {
|
||||
if (tagNames != null) {
|
||||
return cb.or(exp.isNull(), exp.in(tagNames));
|
||||
} else {
|
||||
return exp.isNull();
|
||||
}
|
||||
} else {
|
||||
return exp.in(tagNames);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns query criteria {@link Specification} comparing case insensitive
|
||||
* "NAME == AND VERSION ==".
|
||||
*
|
||||
* @param name
|
||||
* to be filtered on
|
||||
* @param version
|
||||
* to be filtered on
|
||||
* @return the {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaDistributionSet> equalsNameAndVersionIgnoreCase(final String name,
|
||||
final String version) {
|
||||
return (targetRoot, query, cb) -> cb.and(
|
||||
cb.equal(cb.lower(targetRoot.<String> get(JpaDistributionSet_.name)), name.toLowerCase()),
|
||||
cb.equal(cb.lower(targetRoot.<String> get(JpaDistributionSet_.version)), version.toLowerCase()));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link DistributionSet} with given
|
||||
* {@link DistributionSet#getType()}.
|
||||
*
|
||||
* @param type
|
||||
* to search
|
||||
* @return the {@link DistributionSet} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaDistributionSet> byType(final DistributionSetType type) {
|
||||
return (targetRoot, query, cb) -> cb.equal(targetRoot.<JpaDistributionSetType> get(JpaDistributionSet_.type),
|
||||
type);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param installedTargetId
|
||||
* the targetID which is installed to a distribution set to
|
||||
* search for.
|
||||
* @return the specification to search for a distribution set which is
|
||||
* installed to the given targetId
|
||||
*/
|
||||
public static Specification<JpaDistributionSet> installedTarget(final String installedTargetId) {
|
||||
return (dsRoot, query, cb) -> {
|
||||
final ListJoin<JpaDistributionSet, JpaTargetInfo> installedTargetJoin = dsRoot
|
||||
.join(JpaDistributionSet_.installedAtTargets, JoinType.INNER);
|
||||
final Join<JpaTargetInfo, JpaTarget> targetJoin = installedTargetJoin.join(JpaTargetInfo_.target);
|
||||
return cb.equal(targetJoin.get(JpaTarget_.controllerId), installedTargetId);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param assignedTargetId
|
||||
* the targetID which is assigned to a distribution set to search
|
||||
* for.
|
||||
* @return the specification to search for a distribution set which is
|
||||
* assigned to the given targetId
|
||||
*/
|
||||
public static Specification<JpaDistributionSet> assignedTarget(final String assignedTargetId) {
|
||||
return (dsRoot, query, cb) -> {
|
||||
final ListJoin<JpaDistributionSet, JpaTarget> assignedTargetJoin = dsRoot
|
||||
.join(JpaDistributionSet_.assignedToTargets, JoinType.INNER);
|
||||
return cb.equal(assignedTargetJoin.get(JpaTarget_.controllerId), assignedTargetId);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.specifications;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType_;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
/**
|
||||
* Specifications class for {@link DistributionSetType}s. The class provides
|
||||
* Spring Data JPQL Specifications.
|
||||
*/
|
||||
public final class DistributionSetTypeSpecification {
|
||||
private DistributionSetTypeSpecification() {
|
||||
// utility class
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link DistributionSetType}s by its
|
||||
* DELETED attribute.
|
||||
*
|
||||
* @param isDeleted
|
||||
* TRUE/FALSE are compared to the attribute DELETED. If NULL the
|
||||
* attribute is ignored
|
||||
* @return the {@link DistributionSetType} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaDistributionSetType> isDeleted(final Boolean isDeleted) {
|
||||
return (targetRoot, query, cb) -> cb.equal(targetRoot.<Boolean> get(JpaDistributionSetType_.deleted),
|
||||
isDeleted);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link DistributionSetType} with
|
||||
* given {@link DistributionSetType#getId()} including fetching the elements
|
||||
* list.
|
||||
*
|
||||
* @param distid
|
||||
* to search
|
||||
* @return the {@link DistributionSet} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaDistributionSetType> byId(final Long distid) {
|
||||
return (targetRoot, query, cb) -> cb.equal(targetRoot.<Long> get(JpaDistributionSetType_.id), distid);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link DistributionSetType} with
|
||||
* given {@link DistributionSetType#getName())} including fetching the
|
||||
* elements list.
|
||||
*
|
||||
* @param name
|
||||
* to search
|
||||
* @return the {@link DistributionSet} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaDistributionSetType> byName(final String name) {
|
||||
return (targetRoot, query, cb) -> cb.equal(targetRoot.<String> get(JpaDistributionSetType_.name), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link DistributionSetType} with
|
||||
* given {@link DistributionSetType#getKey()} including fetching the
|
||||
* elements list.
|
||||
*
|
||||
* @param key
|
||||
* to search
|
||||
* @return the {@link DistributionSet} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaDistributionSetType> byKey(final String key) {
|
||||
return (targetRoot, query, cb) -> cb.equal(targetRoot.<String> get(JpaDistributionSetType_.key), key);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.specifications;
|
||||
|
||||
import javax.persistence.criteria.Predicate;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule_;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
/**
|
||||
* Specifications class for {@link SoftwareModule}s. The class provides Spring
|
||||
* Data JPQL Specifications
|
||||
*
|
||||
*/
|
||||
public final class SoftwareModuleSpecification {
|
||||
private SoftwareModuleSpecification() {
|
||||
// utility class
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link SoftwareModule}s by its ID
|
||||
* attribute.
|
||||
*
|
||||
* @param moduleId
|
||||
* to search for
|
||||
* @return the {@link SoftwareModule} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaSoftwareModule> byId(final Long moduleId) {
|
||||
return (targetRoot, query, cb) -> {
|
||||
final Predicate predicate = cb.equal(targetRoot.<Long> get(JpaSoftwareModule_.id), moduleId);
|
||||
targetRoot.fetch(JpaSoftwareModule_.type);
|
||||
return predicate;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link SoftwareModule}s where its
|
||||
* DELETED attribute is false.
|
||||
*
|
||||
* @return the {@link SoftwareModule} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaSoftwareModule> isDeletedFalse() {
|
||||
return (swRoot, query, cb) -> cb.equal(swRoot.<Boolean> get(JpaSoftwareModule_.deleted), Boolean.FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link SoftwareModule}s by
|
||||
* "like name or like version".
|
||||
*
|
||||
* @param subString
|
||||
* to be filtered on
|
||||
* @return the {@link SoftwareModule} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaSoftwareModule> likeNameOrVersion(final String subString) {
|
||||
return (targetRoot, query, cb) -> cb.or(
|
||||
cb.like(cb.lower(targetRoot.<String> get(JpaSoftwareModule_.name)), subString.toLowerCase()),
|
||||
cb.like(cb.lower(targetRoot.<String> get(JpaSoftwareModule_.version)), subString.toLowerCase()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link SoftwareModule}s by
|
||||
* "like name or like version".
|
||||
*
|
||||
* @param type
|
||||
* to be filtered on
|
||||
* @return the {@link SoftwareModule} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaSoftwareModule> equalType(final JpaSoftwareModuleType type) {
|
||||
return (targetRoot, query, cb) -> cb.equal(targetRoot.<JpaSoftwareModuleType> get(JpaSoftwareModule_.type),
|
||||
type);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.specifications;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.domain.Specifications;
|
||||
|
||||
/**
|
||||
* Helper class to easily combine {@link Specification} instances.
|
||||
*
|
||||
*/
|
||||
public final class SpecificationsBuilder {
|
||||
|
||||
private SpecificationsBuilder() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine all given specification with and. The first specification is the
|
||||
* where clause.
|
||||
*
|
||||
* @param specList
|
||||
* all specification wich will combine
|
||||
* @return <null> if the given specification list is empty
|
||||
*/
|
||||
public static <T> Specifications<T> combineWithAnd(final List<Specification<T>> specList) {
|
||||
if (specList.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Specifications<T> specs = Specifications.where(specList.get(0));
|
||||
specList.remove(0);
|
||||
for (final Specification<T> specification : specList) {
|
||||
specs = specs.and(specification);
|
||||
}
|
||||
return specs;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.specifications;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery_;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
/**
|
||||
* Specifications class for {@link TargetFilterQuery}s. The class provides
|
||||
* Spring Data JPQL Specifications.
|
||||
*
|
||||
*/
|
||||
public final class TargetFilterQuerySpecification {
|
||||
private TargetFilterQuerySpecification() {
|
||||
// utility class
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link JpaTargetFilterQuery}s based
|
||||
* on is {@link JpaTargetFilterQuery#getName()}.
|
||||
*
|
||||
* @param searchText
|
||||
* of the filter
|
||||
* @return the {@link JpaTargetFilterQuery} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaTargetFilterQuery> likeName(final String searchText) {
|
||||
return (targetFilterQueryRoot, query, cb) -> {
|
||||
final String searchTextToLower = searchText.toLowerCase();
|
||||
return cb.like(cb.lower(targetFilterQueryRoot.get(JpaTargetFilterQuery_.name)), searchTextToLower);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.specifications;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.Join;
|
||||
import javax.persistence.criteria.JoinType;
|
||||
import javax.persistence.criteria.Path;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.SetJoin;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetInfo;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
/**
|
||||
* Specifications class for {@link Target}s. The class provides Spring Data JPQL
|
||||
* Specifications.
|
||||
*
|
||||
*/
|
||||
public final class TargetSpecifications {
|
||||
private TargetSpecifications() {
|
||||
// utility class
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link Target}s including
|
||||
* {@link TargetTag}s and {@link TargetStatus}.
|
||||
*
|
||||
* @param controllerIDs
|
||||
* to search for
|
||||
*
|
||||
* @return the {@link Target} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaTarget> byControllerIdWithStatusAndTagsInJoin(
|
||||
final Collection<String> controllerIDs) {
|
||||
return (targetRoot, query, cb) -> {
|
||||
final Predicate predicate = targetRoot.get(JpaTarget_.controllerId).in(controllerIDs);
|
||||
targetRoot.fetch(JpaTarget_.tags, JoinType.LEFT);
|
||||
query.distinct(true);
|
||||
return predicate;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link Target}s including {
|
||||
* {@link TargetStatus}.
|
||||
*
|
||||
* @param controllerIDs
|
||||
* to search for
|
||||
*
|
||||
* @return the {@link Target} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaTarget> byControllerIdWithStatusAndAssignedInJoin(
|
||||
final Collection<String> controllerIDs) {
|
||||
return (targetRoot, query, cb) -> {
|
||||
|
||||
final Predicate predicate = targetRoot.get(JpaTarget_.controllerId).in(controllerIDs);
|
||||
targetRoot.fetch(JpaTarget_.assignedDistributionSet);
|
||||
return predicate;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link Target}s by "equal to given
|
||||
* {@link TargetUpdateStatus}".
|
||||
*
|
||||
* @param updateStatus
|
||||
* to be filtered on
|
||||
* @param fetch
|
||||
* {@code true} to fetch the {@link TargetInfo} otherwise only
|
||||
* join it.
|
||||
* @return the {@link Target} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaTarget> hasTargetUpdateStatus(final Collection<TargetUpdateStatus> updateStatus,
|
||||
final boolean fetch) {
|
||||
return (targetRoot, query, cb) -> {
|
||||
if (!query.getResultType().isAssignableFrom(Long.class)) {
|
||||
if (fetch) {
|
||||
targetRoot.fetch(JpaTarget_.targetInfo);
|
||||
} else {
|
||||
targetRoot.join(JpaTarget_.targetInfo);
|
||||
}
|
||||
return targetRoot.get(JpaTarget_.targetInfo).get(JpaTargetInfo_.updateStatus).in(updateStatus);
|
||||
}
|
||||
final Join<JpaTarget, JpaTargetInfo> targetInfoJoin = targetRoot.join(JpaTarget_.targetInfo);
|
||||
return targetInfoJoin.get(JpaTargetInfo_.updateStatus).in(updateStatus);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link Target}s by
|
||||
* "like controllerId or like description or like ip address".
|
||||
*
|
||||
* @param searchText
|
||||
* to be filtered on
|
||||
* @return the {@link Target} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaTarget> likeNameOrDescriptionOrIp(final String searchText) {
|
||||
return (targetRoot, query, cb) -> {
|
||||
final String searchTextToLower = searchText.toLowerCase();
|
||||
return cb.or(cb.like(cb.lower(targetRoot.get(JpaTarget_.name)), searchTextToLower),
|
||||
cb.like(cb.lower(targetRoot.get(JpaTarget_.description)), searchTextToLower));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link Target}s by
|
||||
* "like controllerId".
|
||||
*
|
||||
* @param distributionId
|
||||
* to be filtered on
|
||||
* @return the {@link Target} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaTarget> hasInstalledOrAssignedDistributionSet(@NotNull final Long distributionId) {
|
||||
return (targetRoot, query, cb) -> {
|
||||
final Join<JpaTarget, JpaTargetInfo> targetInfoJoin = targetRoot.join(JpaTarget_.targetInfo);
|
||||
return cb.or(
|
||||
cb.equal(targetInfoJoin.get(JpaTargetInfo_.installedDistributionSet).get(JpaDistributionSet_.id),
|
||||
distributionId),
|
||||
cb.equal(targetRoot.<JpaDistributionSet> get(JpaTarget_.assignedDistributionSet)
|
||||
.get(JpaDistributionSet_.id), distributionId));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all targets by given {@link Target#getControllerId()}s and which
|
||||
* are not yet assigned to given {@link DistributionSet}.
|
||||
*
|
||||
* @param tIDs
|
||||
* to search for.
|
||||
* @param distributionId
|
||||
* set that is not yet assigned
|
||||
* @return the {@link Target} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaTarget> hasControllerIdAndAssignedDistributionSetIdNot(final List<String> tIDs,
|
||||
@NotNull final Long distributionId) {
|
||||
return (targetRoot, query, cb) -> cb.and(targetRoot.get(JpaTarget_.controllerId).in(tIDs),
|
||||
cb.or(cb.notEqual(targetRoot.<JpaDistributionSet> get(JpaTarget_.assignedDistributionSet)
|
||||
.get(JpaDistributionSet_.id), distributionId),
|
||||
cb.isNull(targetRoot.<JpaDistributionSet> get(JpaTarget_.assignedDistributionSet))));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link Target}s by
|
||||
* "has no tag names"or "has at least on of the given tag names".
|
||||
*
|
||||
* @param tagNames
|
||||
* to be filtered on
|
||||
* @param selectTargetWithNoTag
|
||||
* flag to get targets with no tag assigned
|
||||
* @return the {@link Target} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaTarget> hasTags(final String[] tagNames, final Boolean selectTargetWithNoTag) {
|
||||
return (targetRoot, query, cb) -> {
|
||||
final Predicate predicate = getPredicate(targetRoot, cb, selectTargetWithNoTag, tagNames);
|
||||
query.distinct(true);
|
||||
return predicate;
|
||||
};
|
||||
}
|
||||
|
||||
private static Predicate getPredicate(final Root<JpaTarget> targetRoot, final CriteriaBuilder cb,
|
||||
final Boolean selectTargetWithNoTag, final String[] tagNames) {
|
||||
final SetJoin<JpaTarget, JpaTargetTag> tags = targetRoot.join(JpaTarget_.tags, JoinType.LEFT);
|
||||
final Path<String> exp = tags.get(JpaTargetTag_.name);
|
||||
if (selectTargetWithNoTag) {
|
||||
if (tagNames != null) {
|
||||
return cb.or(exp.isNull(), exp.in(tagNames));
|
||||
} else {
|
||||
return exp.isNull();
|
||||
}
|
||||
} else {
|
||||
return exp.in(tagNames);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link Target}s by assigned
|
||||
* distribution set.
|
||||
*
|
||||
* @param distributionSetId
|
||||
* the ID of the distribution set which must be assigned
|
||||
* @return the {@link Target} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaTarget> hasAssignedDistributionSet(final Long distributionSetId) {
|
||||
return (targetRoot, query, cb) -> cb.equal(
|
||||
targetRoot.<JpaDistributionSet> get(JpaTarget_.assignedDistributionSet).get(JpaDistributionSet_.id),
|
||||
distributionSetId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link Target}s by assigned
|
||||
* distribution set.
|
||||
*
|
||||
* @param distributionSetId
|
||||
* the ID of the distribution set which must be assigned
|
||||
* @return the {@link Target} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaTarget> hasInstalledDistributionSet(final Long distributionSetId) {
|
||||
return (targetRoot, query, cb) -> {
|
||||
final Join<JpaTarget, JpaTargetInfo> targetInfoJoin = targetRoot.join(JpaTarget_.targetInfo);
|
||||
return cb.equal(targetInfoJoin.get(JpaTargetInfo_.installedDistributionSet).get(JpaDistributionSet_.id),
|
||||
distributionSetId);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 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.model.helper;
|
||||
|
||||
import org.eclipse.hawkbit.eventbus.EntityPropertyChangeListener;
|
||||
import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* A singleton bean which holds the {@link AfterTransactionCommitExecutor} to
|
||||
* have to the cache manager in beans not instantiated by spring e.g. JPA
|
||||
* entities or {@link EntityPropertyChangeListener} which cannot be autowired.
|
||||
*
|
||||
*/
|
||||
public final class AfterTransactionCommitExecutorHolder {
|
||||
|
||||
private static final AfterTransactionCommitExecutorHolder SINGLETON = new AfterTransactionCommitExecutorHolder();
|
||||
|
||||
@Autowired
|
||||
private AfterTransactionCommitExecutor afterCommit;
|
||||
|
||||
private AfterTransactionCommitExecutorHolder() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the cache manager holder singleton instance
|
||||
*/
|
||||
public static AfterTransactionCommitExecutorHolder getInstance() {
|
||||
return SINGLETON;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the afterCommit
|
||||
*/
|
||||
public AfterTransactionCommitExecutor getAfterCommit() {
|
||||
return afterCommit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param afterCommit
|
||||
* the afterCommit to set
|
||||
*/
|
||||
public void setAfterCommit(final AfterTransactionCommitExecutor afterCommit) {
|
||||
this.afterCommit = afterCommit;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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.model.helper;
|
||||
|
||||
import org.eclipse.hawkbit.eventbus.CacheFieldEntityListener;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.CacheManager;
|
||||
|
||||
/**
|
||||
* A singleton bean which holds the {@link CacheManager} to have to the cache
|
||||
* manager in beans not instantiated by spring e.g. JPA entities or
|
||||
* {@link CacheFieldEntityListener} which cannot be autowired.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public final class CacheManagerHolder {
|
||||
|
||||
private static final CacheManagerHolder SINGLETON = new CacheManagerHolder();
|
||||
|
||||
@Autowired
|
||||
private CacheManager cacheManager;
|
||||
|
||||
private CacheManagerHolder() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the cache manager holder singleton instance
|
||||
*/
|
||||
public static CacheManagerHolder getInstance() {
|
||||
return SINGLETON;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the cacheManager
|
||||
*/
|
||||
public CacheManager getCacheManager() {
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normally not used when using spring-boot then the cachemanager is
|
||||
* autowired to the CacheManagerHolder, but for testing purposes.
|
||||
*
|
||||
* @param cacheManager
|
||||
* the cache manager to set for the cache manager holder.
|
||||
*/
|
||||
public void setCacheManager(final CacheManager cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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.model.helper;
|
||||
|
||||
import org.eclipse.hawkbit.eventbus.CacheFieldEntityListener;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
* A singleton bean which holds the {@link EventBus} to have to the cache
|
||||
* manager in beans not instantiated by spring e.g. JPA entities or
|
||||
* {@link CacheFieldEntityListener} which cannot be autowired.
|
||||
*
|
||||
*/
|
||||
public final class EventBusHolder {
|
||||
|
||||
private static final EventBusHolder SINGLETON = new EventBusHolder();
|
||||
|
||||
@Autowired
|
||||
private EventBus eventBus;
|
||||
|
||||
private EventBusHolder() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the cache manager holder singleton instance
|
||||
*/
|
||||
public static EventBusHolder getInstance() {
|
||||
return SINGLETON;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the eventBus
|
||||
*/
|
||||
public EventBus getEventBus() {
|
||||
return eventBus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param eventBus
|
||||
* the eventBus to set
|
||||
*/
|
||||
public void setEventBus(final EventBus eventBus) {
|
||||
this.eventBus = eventBus;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* 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.model.helper;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
/**
|
||||
* A helper class which allows to do runtime security checks.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public final class SecurityChecker {
|
||||
|
||||
private SecurityChecker() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the current {@link SecurityContext} for the permission.
|
||||
*
|
||||
* @param permission
|
||||
* the permission to check against the security context
|
||||
* @return {@code true} if the given permission is present in the security
|
||||
* context otherwise {@code false}
|
||||
*/
|
||||
public static boolean hasPermission(final String permission) {
|
||||
final SecurityContext context = SecurityContextHolder.getContext();
|
||||
if (context != null) {
|
||||
final Authentication authentication = context.getAuthentication();
|
||||
if (authentication != null) {
|
||||
for (final GrantedAuthority authority : authentication.getAuthorities()) {
|
||||
if (authority.getAuthority().equals(permission)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* 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.model.helper;
|
||||
|
||||
import org.eclipse.hawkbit.security.SecurityTokenGenerator;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* A singleton bean which holds the {@link SecurityTokenGenerator} and make it
|
||||
* accessible to beans which are not managed by spring, e.g. JPA entities.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public final class SecurityTokenGeneratorHolder {
|
||||
|
||||
private static final SecurityTokenGeneratorHolder INSTANCE = new SecurityTokenGeneratorHolder();
|
||||
|
||||
@Autowired
|
||||
private SecurityTokenGenerator securityTokenGenerator;
|
||||
|
||||
/**
|
||||
* private constructor.
|
||||
*/
|
||||
private SecurityTokenGeneratorHolder() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a singleton instance of the security token generator holder.
|
||||
*/
|
||||
public static SecurityTokenGeneratorHolder getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* delegates to {@link SecurityTokenGenerator#generateToken()}.
|
||||
*
|
||||
* @return the result {@link SecurityTokenGenerator#generateToken()}
|
||||
*/
|
||||
public String generateToken() {
|
||||
return securityTokenGenerator.generateToken();
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user