Split into separate maven modules.
Signed-off-by: Kai Zimmermann <kai.zimmermann@bosch-si.com>
This commit is contained in:
47
hawkbit-repository/hawkbit-repository-jpa/README.md
Normal file
47
hawkbit-repository/hawkbit-repository-jpa/README.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# hawkBit metadata repository
|
||||
|
||||
The repository is in charge for managing the meta data of the update server, e.g. provisioning targets, distribution sets, software modules etc.
|
||||
|
||||
# Build
|
||||
|
||||
[indent=0]
|
||||
----
|
||||
$ mvn clean install
|
||||
----
|
||||
|
||||
Note, in order to build correctly in your IDE, you have to add ./target/generated-sources/apt to your build path.
|
||||
|
||||
#Concepts
|
||||
|
||||
## Rollout
|
||||
A rollout consists of the distribution set and a target-query-filter which defines the targets to be updated in this rollout. The targets within this filter are split up in configured amount of _Deployment Groups_ at creation time.
|
||||
|
||||
When starting a rollout, for all targets within this rollout deployment actions will be created. The deployment actions of the first group will be started immediately all other deployment actions will be scheduled.
|
||||
|
||||
> Due rollouts might include a large number of targets and deployment group, creation as well as starting a rollout might take some time and therefore the creation and starting of an rollout is executed asynchronously. The creation and starting progress is reflected by the rollout's status attribute
|
||||
|
||||
### Rollout Creation
|
||||
The targets reflected by the target-query-filter is interpreted at creation time. Only targets which have been created at that time are included in the rollout. Targets which are created after the rollout creation will not be included. At rollout creation time all necessary deployment groups containing the targets will be created. Dynamically adding targets to a created or running rollout is currently not supported.
|
||||
|
||||
### Rollout Starting
|
||||
The rollout is using the same concept based on _deployment acionts_ per target. All necessary _deployment acionts_ will be created at starting point but not all _deployment acionts_ will be set to running. Only the _deployment acionts_ of the first _deployment group_ is set to running, all other actions are created in _scheduled_ state.
|
||||
If targets having not finished _deployment actions_ at rollout starting time, these action are tried to cancel (state: canceling). Scheduled actions from another rollout are canceled directly on server side because they were never running before and can be safely canceled.
|
||||
|
||||
>Multiple rollouts can be running at the same time, but if the rollouts effect the same targets they will interfer the targets _deployment actions_ e.g. canceling/cancel already running/scheduled _deployment actions_.
|
||||
|
||||
### Deployment Group Condition/Action
|
||||
#### Success Condition/Action
|
||||
A _success condition_ defines when a deployment group is interpreted as success and triggers the _success action_. E.g. a threshold of successfully updated targets within the group.
|
||||
|
||||
The _success action_ is executed if the _success_condition_ is fullfilled. Currently only the _success action_ starting the next following deployment group is available.
|
||||
|
||||
#### Error Condition/Action
|
||||
A _error condition_ defines when a deployment group is interpreted as failure and triggers the _error action_. E.g. a threshold of update failures of targets within the group.
|
||||
|
||||
The _error action_ is executed if the _error condition_ is fullfilled. Currently only the _error action_ pausing the whole rollout is available, a paused rollout can be resumed by a user.
|
||||
|
||||
### Rollout Scheduler
|
||||
The rollout is managed by a scheduler task which is checking for rollouts in _running_ state. This is done atomic by updating the row in the database, so only the rollouts are checked and processed for the current instance to avoid that multiple instances are processing a rollout.
|
||||
|
||||
>Due the scheduler, it might take some time until a rollouts is processed and switching is state or starting the next deployment group.
|
||||
|
||||
238
hawkbit-repository/hawkbit-repository-jpa/pom.xml
Normal file
238
hawkbit-repository/hawkbit-repository-jpa/pom.xml
Normal file
@@ -0,0 +1,238 @@
|
||||
<!--
|
||||
|
||||
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
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
<artifactId>hawkbit-repository</artifactId>
|
||||
</parent>
|
||||
<artifactId>hawkbit-repository-jpa</artifactId>
|
||||
<name>hawkBit :: Repository JPA Implementation</name>
|
||||
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>com.ethlo.eclipselink.tools</id>
|
||||
<url>http://ethlo.com/maven</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>com.ethlo.eclipselink.tools</id>
|
||||
<url>http://ethlo.com/maven</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Hawkbit -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-repository-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-artifact-repository-mongo</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net._01001111</groupId>
|
||||
<artifactId>jlorem</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.persistence</groupId>
|
||||
<artifactId>org.eclipse.persistence.jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mariadb.jdbc</groupId>
|
||||
<artifactId>mariadb-java-client</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.el</groupId>
|
||||
<artifactId>javax.el-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ru.yandex.qatools.allure</groupId>
|
||||
<artifactId>allure-junit-adaptor</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-rest-webmvc</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-aspects</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.easytesting</groupId>
|
||||
<artifactId>fest-assert-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.easytesting</groupId>
|
||||
<artifactId>fest-assert</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.flapdoodle.embed</groupId>
|
||||
<artifactId>de.flapdoodle.embed.mongo</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-web</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cz.jirutka.rsql</groupId>
|
||||
<artifactId>rsql-parser</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.bsc.maven</groupId>
|
||||
<artifactId>maven-processor-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>process</id>
|
||||
<goals>
|
||||
<goal>process</goal>
|
||||
</goals>
|
||||
<phase>generate-sources</phase>
|
||||
<configuration>
|
||||
<processors>
|
||||
<processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor>
|
||||
</processors>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-jpamodelgen</artifactId>
|
||||
<version>${hibernate.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<!-- Static weaver for EclipseLink -->
|
||||
<plugin>
|
||||
<groupId>com.ethlo.persistence.tools</groupId>
|
||||
<artifactId>eclipselink-maven-plugin</artifactId>
|
||||
<version>2.6.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>weave</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.persistence</groupId>
|
||||
<artifactId>org.eclipse.persistence.jpa</artifactId>
|
||||
<version>${eclipselink.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.javassist</groupId>
|
||||
<artifactId>javassist</artifactId>
|
||||
<version>3.20.0-GA</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>test-jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user