Refactor caches (#2775)

* added static usage of cache in order access it easier
* added mandatory (in hawkbit-core) registration - always tenant aware caches shall be used - hawkbit depends on it
* added per cache and tenant name configuration
* (not really realted to caches) but in order to be easier evicted entities after commit handlers are now statically accessed

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-10-24 16:42:40 +03:00
committed by GitHub
parent dc8211c64b
commit 98daa696de
48 changed files with 380 additions and 670 deletions

View File

@@ -41,10 +41,6 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-core</artifactId>

View File

@@ -1,71 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.autoconfigure.cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.eclipse.hawkbit.tenancy.cache.TenantCacheManager;
import org.eclipse.hawkbit.tenancy.cache.TenantAwareCacheManager;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
/**
* A configuration for configuring the spring {@link CacheManager} for specific multi-tenancy caching. The caches between
* tenants must not interfere each other.
* <p/>
* This is done by providing a special {@link TenantCacheManager} which generates a cache name included the current tenant.
*/
@Configuration
@EnableCaching
public class CacheAutoConfiguration {
/**
* @return the default cache manager bean if none other cache manager is existing.
*/
@Bean
@ConditionalOnMissingBean
@Primary
TenantCacheManager cacheManager(
@Qualifier("directCacheManager") final CacheManager directCacheManager, final TenantAware tenantAware) {
return new TenantAwareCacheManager(directCacheManager, tenantAware);
}
/**
* A separate configuration of the direct cache manager for the {@link TenantAwareCacheManager} that it can get overridden by another
* configuration.
*/
@Configuration
@EnableConfigurationProperties(CacheProperties.class)
static class DirectCacheManagerConfiguration {
/**
* @return the direct cache manager to access without tenant aware check, cause in sometimes it's necessary to access the cache
* directly without having the current tenant, e.g. initial creation of tenant
*/
@Bean(name = "directCacheManager")
@ConditionalOnMissingBean(name = "directCacheManager")
public CacheManager directCacheManager(final CacheProperties cacheProperties) {
final CaffeineCacheManager cacheManager = new CaffeineCacheManager();
if (cacheProperties.getTtl() > 0) {
cacheManager.setCaffeine(Caffeine.newBuilder().expireAfterWrite(cacheProperties.getTtl(), cacheProperties.getTtlUnit()));
}
return cacheManager;
}
}
}

View File

@@ -1,38 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.autoconfigure.cache;
import java.util.concurrent.TimeUnit;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Properties for configuring the cache within a cluster. The TTL (time to live) is used for the lifetime limit of data in caches.
* After lifetime the data gets reloaded out of the database.
*/
@Data
@ConfigurationProperties("hawkbit.cache")
public class CacheProperties {
/**
* TTL for cached entries in millis.
*/
private int ttl = 10_000; // default TTL - 10 seconds
/**
* Initial delay in millis
*/
private int initialDelay;
public final TimeUnit getTtlUnit() {
return TimeUnit.MILLISECONDS;
}
}

View File

@@ -1,5 +1,4 @@
org.eclipse.hawkbit.autoconfigure.artifact.PropertyBasedArtifactUrlResolverAutoConfiguration
org.eclipse.hawkbit.autoconfigure.cache.CacheAutoConfiguration
org.eclipse.hawkbit.autoconfigure.repository.event.EventPublisherAutoConfiguration
org.eclipse.hawkbit.autoconfigure.repository.JpaRepositoryAutoConfiguration
org.eclipse.hawkbit.autoconfigure.scheduling.AsyncConfigurerAutoConfiguration

View File

@@ -38,12 +38,14 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
@@ -53,12 +55,14 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<!-- TEST -->

View File

@@ -0,0 +1,128 @@
/**
* Copyright (c) 2025 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.tenancy;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import org.eclipse.hawkbit.tenancy.TenantAware.TenantResolver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.core.env.Environment;
import org.springframework.lang.Nullable;
/**
* A spring Cache Manager that handles the multi tenancy.
* <ul>
* <li>If a tenant is resolved by the {@link TenantResolver}, a dedicated cache manager for that tenant is used/created.</li>
* <li>If no tenant is resolved, a global cache manager is used.</li>
* </ul>
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@SuppressWarnings("java:S6548") // singleton holder ensures static access to spring resources in some places
public class TenantAwareCacheManager implements CacheManager {
private static final String CONFIG_PREFIX = "hawkbit.cache.";
private static final String CONFIG_SPEC = "spec";
private static final TenantAwareCacheManager SINGLETON = new TenantAwareCacheManager();
private CacheManager globalCacheManager;
private final Map<String, CacheManager> tenant2CacheManager = new ConcurrentHashMap<>();
private TenantResolver resolver;
private Environment env;
// default caffeine cache spec - see com.github.benmanes.caffeine.cache.CaffeineSpec javadoc for format details
private String defaultSpec;
public static TenantAwareCacheManager getInstance() {
return SINGLETON;
}
@Autowired
public void init(final TenantResolver resolver, final Environment env) {
this.resolver = resolver;
this.env = env;
defaultSpec = env.resolvePlaceholders("${" + CONFIG_PREFIX + CONFIG_SPEC + ":expireAfterWrite=${hawkbit.cache.ttl:10s}}");
globalCacheManager = new TenantCacheManager(null);
}
@Nullable
@Override
public Cache getCache(@NonNull final String name) {
return Optional.ofNullable(resolver.resolveTenant())
.map(currentTenant -> tenant2CacheManager.computeIfAbsent(currentTenant, TenantCacheManager::new))
.orElse(globalCacheManager)
.getCache(name);
}
@NonNull
@Override
public Collection<String> getCacheNames() {
final String currentTenant = resolver.resolveTenant();
if (currentTenant == null) {
return globalCacheManager.getCacheNames();
} else {
final CacheManager cacheManager = tenant2CacheManager.get(currentTenant);
return cacheManager == null ? List.of() : cacheManager.getCacheNames();
}
}
public void evictTenant(final String tenant) {
if (tenant == null) {
globalCacheManager.getCacheNames().forEach(name -> Optional.ofNullable(globalCacheManager.getCache(name)).ifPresent(Cache::clear));
} else {
tenant2CacheManager.remove(tenant);
}
}
private class TenantCacheManager implements CacheManager {
private final String tenant;
private final Map<String, Cache> caches = new ConcurrentHashMap<>();
private TenantCacheManager(final String tenant) {
this.tenant = tenant;
}
@Nullable
@Override
public Cache getCache(@NonNull final String name) {
return caches.computeIfAbsent(name, n -> {
// try tenant and cache specific config first
String spec = env.getProperty(CONFIG_PREFIX + (tenant == null ? "" : tenant + ".") + name + "." + CONFIG_SPEC);
if (spec == null) {
// try cache specific config next
spec = env.getProperty(CONFIG_PREFIX + name + "." + CONFIG_SPEC);
if (spec == null) {
spec = defaultSpec;
}
}
return new CaffeineCache(n, Caffeine.from(spec).build(), false);
});
}
@NonNull
@Override
public Collection<String> getCacheNames() {
return caches.keySet();
}
}
}

View File

@@ -0,0 +1,24 @@
/**
* Copyright (c) 2025 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.tenancy;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TenantCacheConfiguration {
@Bean
TenantAwareCacheManager cacheManager() {
return TenantAwareCacheManager.getInstance();
}
}

View File

@@ -1,103 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.tenancy.cache;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;
import lombok.NonNull;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.lang.Nullable;
/**
* A {@link CacheManager} delegator which wraps the {@link CacheManager#getCache(String)} and {@link CacheManager#getCacheNames()}
* to include the {@link TenantAware#getCurrentTenant()} when accessing a cache, so caches are seperated.
* <p/>
* Additionally, it also provides functionality to retrieve all caches overall tenants at once, for monitoring and system access.
*/
public class TenantAwareCacheManager implements TenantCacheManager {
private static final String TENANT_CACHE_DELIMITER = "|";
private final CacheManager delegate;
private final TenantAware tenantAware;
/**
* Constructor.
*
* @param delegate the {@link CacheManager} to delegate to.
* @param tenantAware the tenant aware to retrieve the current tenant
*/
public TenantAwareCacheManager(final CacheManager delegate, final TenantAware tenantAware) {
this.delegate = delegate;
this.tenantAware = tenantAware;
}
@Nullable
@Override
public Cache getCache(@NonNull final String name) {
final String currentTenant = tenantAware.getCurrentTenant();
if (isTenantInvalid(currentTenant)) {
return null;
}
return delegate.getCache(buildKey(currentTenant.toUpperCase(), name));
}
@NonNull
@Override
public Collection<String> getCacheNames() {
final String currentTenant = tenantAware.getCurrentTenant();
if (isTenantInvalid(currentTenant)) {
return Collections.emptyList();
}
return getCacheNames(currentTenant.toUpperCase());
}
@Override
public Cache getDirectCache(final String name) {
return delegate.getCache(name);
}
@Override
public void evictCaches(final String tenant) {
getCacheNames(tenant).forEach(
cacheName -> Optional.ofNullable(delegate.getCache(buildKey(tenant, cacheName))).ifPresent(Cache::clear));
}
/**
* A direct-access for retrieving all cache names overall tenants.
*
* @return all cache names without tenant check
*/
public Collection<String> getDirectCacheNames() {
return delegate.getCacheNames();
}
private static boolean isTenantInvalid(final String tenant) {
return tenant == null || tenant.contains(TENANT_CACHE_DELIMITER);
}
private static String buildKey(final String tenant, final String cacheName) {
return tenant + TENANT_CACHE_DELIMITER + cacheName;
}
private Collection<String> getCacheNames(final String tenant) {
final String tenantWithDelimiter = tenant + TENANT_CACHE_DELIMITER;
return delegate.getCacheNames().parallelStream()
.filter(cacheName -> cacheName.startsWith(tenantWithDelimiter))
.map(cacheName -> cacheName.substring(tenantWithDelimiter.length()))
.toList();
}
}

View File

@@ -1,35 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.tenancy.cache;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
/**
* A cache interface which handles multi tenancy.
*/
public interface TenantCacheManager extends CacheManager {
/**
* A direct-access for retrieving the cache without including the current tenant key. This is necessary e.g. for retrieving caches not for
* the current tenant.
*
* @param name the name of the cache to retrieve directly
* @return the cache associated with the name without tenancy separation
*/
Cache getDirectCache(String name);
/**
* Evicts all caches for a given tenant. All caches under a certain tenant gets evicted.
*
* @param tenant the tenant to evict caches
*/
void evictCaches(String tenant);
}

View File

@@ -1,2 +1,3 @@
org.eclipse.hawkbit.tenancy.TenantCacheConfiguration
org.eclipse.hawkbit.tenancy.TenantMetricsConfiguration.WebConfig
org.eclipse.hawkbit.tenancy.TenantMetricsConfiguration.RepositoryConfig

View File

@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.autoassign;
package org.eclipse.hawkbit.repository;
/**
* An interface declaration which contains the check for the auto assignment logic.

View File

@@ -32,10 +32,6 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>

View File

@@ -17,9 +17,6 @@ import java.util.stream.Collectors;
import jakarta.validation.constraints.NotNull;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.eclipse.hawkbit.tenancy.cache.TenantCacheManager;
import org.eclipse.hawkbit.tenancy.cache.TenantAwareCacheManager;
import org.eclipse.hawkbit.repository.event.remote.RolloutDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.RolloutGroupDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.RolloutStoppedEvent;
@@ -27,9 +24,9 @@ import org.eclipse.hawkbit.repository.event.remote.entity.AbstractActionEvent;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.cache.Cache;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.event.EventListener;
/**
@@ -39,35 +36,17 @@ public class RolloutStatusCache {
private static final String CACHE_RO_NAME = "RolloutStatus";
private static final String CACHE_GR_NAME = "RolloutGroupStatus";
private static final long DEFAULT_SIZE = 50_000;
private final TenantCacheManager cacheManager;
private static final TenantAwareCacheManager CACHE_MANAGER = TenantAwareCacheManager.getInstance();
private final TenantAware tenantAware;
/**
* @param tenantAware to get current tenant
* @param size the maximum size of the cache
*/
public RolloutStatusCache(final TenantAware tenantAware, final long size) {
this.tenantAware = tenantAware;
final Caffeine<Object, Object> cacheBuilder = Caffeine.newBuilder().maximumSize(size);
final CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();
caffeineCacheManager.setCaffeine(cacheBuilder);
this.cacheManager = new TenantAwareCacheManager(caffeineCacheManager, tenantAware);
}
/**
* @param tenantAware to get current tenant
*/
public RolloutStatusCache(final TenantAware tenantAware) {
this(tenantAware, DEFAULT_SIZE);
this.tenantAware = tenantAware;
}
/**
* Retrieves cached list of {@link TotalTargetCountActionStatus} of
* {@link Rollout}s.
* Retrieves cached list of {@link TotalTargetCountActionStatus} of {@link Rollout}s.
*
* @param rollouts rolloutIds to retrieve cache entries for
* @return map of cached entries
@@ -77,8 +56,7 @@ public class RolloutStatusCache {
}
/**
* Retrieves cached list of {@link TotalTargetCountActionStatus} of
* {@link Rollout}s.
* Retrieves cached list of {@link TotalTargetCountActionStatus} of {@link Rollout}s.
*
* @param rolloutId to retrieve cache entries for
* @return map of cached entries
@@ -88,8 +66,7 @@ public class RolloutStatusCache {
}
/**
* Retrieves cached list of {@link TotalTargetCountActionStatus} of
* {@link RolloutGroup}s.
* Retrieves cached list of {@link TotalTargetCountActionStatus} of {@link RolloutGroup}s.
*
* @param rolloutGroups rolloutGroupsIds to retrieve cache entries for
* @return map of cached entries
@@ -99,8 +76,7 @@ public class RolloutStatusCache {
}
/**
* Retrieves cached list of {@link TotalTargetCountActionStatus} of
* {@link RolloutGroup}.
* Retrieves cached list of {@link TotalTargetCountActionStatus} of {@link RolloutGroup}.
*
* @param groupId to retrieve cache entries for
* @return map of cached entries
@@ -120,8 +96,7 @@ public class RolloutStatusCache {
}
/**
* Put {@link TotalTargetCountActionStatus} for one {@link Rollout}s into
* cache.
* Put {@link TotalTargetCountActionStatus} for one {@link Rollout}s into cache.
*
* @param rolloutId the cache entries belong to
* @param status list to cache
@@ -131,8 +106,7 @@ public class RolloutStatusCache {
}
/**
* Put {@link TotalTargetCountActionStatus} for multiple
* {@link RolloutGroup}s into cache.
* Put {@link TotalTargetCountActionStatus} for multiple {@link RolloutGroup}s into cache.
*
* @param put map of cached entries
*/
@@ -141,8 +115,7 @@ public class RolloutStatusCache {
}
/**
* Put {@link TotalTargetCountActionStatus} for multiple
* {@link RolloutGroup}s into cache.
* Put {@link TotalTargetCountActionStatus} for multiple {@link RolloutGroup}s into cache.
*
* @param groupId the cache entries belong to
* @param status list to cache
@@ -154,52 +127,37 @@ public class RolloutStatusCache {
@EventListener(classes = AbstractActionEvent.class)
public void invalidateCachedTotalTargetCountActionStatus(final AbstractActionEvent event) {
if (event.getRolloutId() != null) {
final Cache cache = tenantAware.runAsTenant(event.getTenant(), () -> cacheManager.getCache(CACHE_RO_NAME));
final Cache cache = tenantAware.runAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_RO_NAME));
cache.evict(event.getRolloutId());
}
if (event.getRolloutGroupId() != null) {
final Cache cache = tenantAware.runAsTenant(event.getTenant(), () -> cacheManager.getCache(CACHE_GR_NAME));
final Cache cache = tenantAware.runAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_GR_NAME));
cache.evict(event.getRolloutGroupId());
}
}
@EventListener(classes = RolloutDeletedEvent.class)
public void invalidateCachedTotalTargetCountOnRolloutDelete(final RolloutDeletedEvent event) {
final Cache cache = tenantAware.runAsTenant(event.getTenant(), () -> cacheManager.getCache(CACHE_RO_NAME));
final Cache cache = tenantAware.runAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_RO_NAME));
cache.evict(event.getEntityId());
}
@EventListener(classes = RolloutGroupDeletedEvent.class)
public void invalidateCachedTotalTargetCountOnRolloutGroupDelete(final RolloutGroupDeletedEvent event) {
final Cache cache = tenantAware.runAsTenant(event.getTenant(), () -> cacheManager.getCache(CACHE_GR_NAME));
final Cache cache = tenantAware.runAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_GR_NAME));
cache.evict(event.getEntityId());
}
@EventListener(classes = RolloutStoppedEvent.class)
public void invalidateCachedTotalTargetCountOnRolloutStopped(final RolloutStoppedEvent event) {
final Cache cache = tenantAware.runAsTenant(event.getTenant(), () -> cacheManager.getCache(CACHE_RO_NAME));
final Cache cache = tenantAware.runAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_RO_NAME));
cache.evict(event.getRolloutId());
for (final long groupId : event.getRolloutGroupIds()) {
final Cache groupCache = tenantAware.runAsTenant(event.getTenant(),
() -> cacheManager.getCache(CACHE_GR_NAME));
groupCache.evict(groupId);
}
event.getRolloutGroupIds().forEach(
groupId -> tenantAware.runAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_GR_NAME)).evict(groupId));
}
/**
* Evicts all caches for a given tenant. All caches under a certain tenant
* gets evicted.
*
* @param tenant the tenant to evict caches
*/
public void evictCaches(final String tenant) {
cacheManager.evictCaches(tenant);
}
private @NotNull Map<Long, List<TotalTargetCountActionStatus>> retrieveFromCache(final List<Long> ids,
@NotNull final Cache cache) {
private @NotNull Map<Long, List<TotalTargetCountActionStatus>> retrieveFromCache(final List<Long> ids, @NotNull final Cache cache) {
return ids.stream()
.map(id -> cache.get(id, CachedTotalTargetCountActionStatus.class))
.filter(Objects::nonNull)
@@ -208,11 +166,9 @@ public class RolloutStatusCache {
private List<TotalTargetCountActionStatus> retrieveFromCache(final Long id, @NotNull final Cache cache) {
final CachedTotalTargetCountActionStatus cacheItem = cache.get(id, CachedTotalTargetCountActionStatus.class);
if (cacheItem == null) {
return Collections.emptyList();
}
return cacheItem.status();
}
@@ -226,11 +182,11 @@ public class RolloutStatusCache {
}
private @NotNull Cache getRolloutStatusCache() {
return Objects.requireNonNull(cacheManager.getCache(CACHE_RO_NAME), "Cache '" + CACHE_RO_NAME + "' is null!");
return Objects.requireNonNull(CACHE_MANAGER.getCache(CACHE_RO_NAME), "Cache '" + CACHE_RO_NAME + "' is null!");
}
private @NotNull Cache getGroupStatusCache() {
return Objects.requireNonNull(cacheManager.getCache(CACHE_GR_NAME), "Cache '" + CACHE_RO_NAME + "' is null!");
return Objects.requireNonNull(CACHE_MANAGER.getCache(CACHE_GR_NAME), "Cache '" + CACHE_RO_NAME + "' is null!");
}
private record CachedTotalTargetCountActionStatus(long id, List<TotalTargetCountActionStatus> status) {}

View File

@@ -21,6 +21,13 @@ hawkbit.controller.minPollingTime=00:00:30
# pollingTime(@time=t) = (maintenanceStartTime - t)/maintenanceWindowPollCount
hawkbit.controller.maintenanceWindowPollCount=3
# Cache config
hawkbit.cache.ttl=10s
hawkbit.cache.spec=expireAfterAccess=${hawkbit.cache.ttl}
hawkbit.cache.RolloutStatus.spec=maximumSize=50000,expireAfterAccess=${hawkbit.cache.ttl}
hawkbit.cache.RolloutGroupStatus.spec=maximumSize=50000,expireAfterAccess=${hawkbit.cache.ttl}
# Cache config - END
# Attention: if you want to use a maximumPollingTime greater 23:59:59 you have to update the DurationField in the configuration window
# Default tenant configuration - START

View File

@@ -1,73 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.executor;
import java.util.ArrayList;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* A Service which calls register runnable. This runnables will be executed after a successful spring transaction commit.
* The class is thread safe.
*/
@Slf4j
public class AfterTransactionCommitDefaultServiceExecutor implements AfterTransactionCommitExecutor {
private static class TransactionSynchronizationImpl implements TransactionSynchronization {
private final List<Runnable> afterCommitRunnables = new ArrayList<>();
@Override
// Exception squid:S1217 - Is aspectJ proxy
@SuppressWarnings({ "squid:S1217" })
public void afterCommit() {
log.debug("Transaction successfully committed, executing {} runnables", afterCommitRunnables.size());
for (final Runnable afterCommitRunnable : afterCommitRunnables) {
log.debug("Executing runnable {}", afterCommitRunnable);
try {
afterCommitRunnable.run();
} catch (final RuntimeException e) {
log.error("Failed to execute runnable {}", afterCommitRunnable, e);
}
}
}
@Override
@SuppressWarnings({ "squid:S1217" })
public void afterCompletion(final int status) {
log.debug("Transaction completed after commit with status {}", status == TransactionSynchronization.STATUS_COMMITTED ? "COMMITTED" : "ROLLEDBACK");
}
private void afterCommit(final Runnable runnable) {
afterCommitRunnables.add(runnable);
}
}
@Override
// Exception squid:S1217 - we want to run this synchronous
@SuppressWarnings("squid:S1217")
public void afterCommit(final Runnable runnable) {
log.debug("Submitting new runnable {} to run after transaction commit", runnable);
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.getSynchronizations().stream().filter(TransactionSynchronizationImpl.class::isInstance)
.map(TransactionSynchronizationImpl.class::cast).findAny().orElseGet(() -> {
final TransactionSynchronizationImpl newTS = new TransactionSynchronizationImpl();
TransactionSynchronizationManager.registerSynchronization(newTS);
return newTS;
}).afterCommit(runnable);
} else {
log.info("Transaction synchronization is NOT ACTIVE/ INACTIVE. Executing right now runnable {}", runnable);
runnable.run();
}
}
}

View File

@@ -9,18 +9,66 @@
*/
package org.eclipse.hawkbit.repository.jpa.executor;
/**
* A interface to register a runnable, which will be executed after a successful
* spring transaction.
*/
@FunctionalInterface
public interface AfterTransactionCommitExecutor {
import java.util.ArrayList;
import java.util.List;
/**
* Register a runnable which will be executed after a successful spring
* transaction.
*
* @param runnable the after commit runnable
*/
void afterCommit(Runnable runnable);
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* A hook to register a runnable, which will be executed after a successful spring transaction.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Slf4j
public class AfterTransactionCommitExecutor {
// Exception squid:S1217 - we want to run this synchronous
@SuppressWarnings("squid:S1217")
public static void afterCommit(final Runnable runnable) {
log.debug("Submitting new runnable {} to run after transaction commit", runnable);
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.getSynchronizations().stream().filter(TransactionSynchronizationImpl.class::isInstance)
.map(TransactionSynchronizationImpl.class::cast).findAny().orElseGet(() -> {
final TransactionSynchronizationImpl newTS = new TransactionSynchronizationImpl();
TransactionSynchronizationManager.registerSynchronization(newTS);
return newTS;
}).afterCommit(runnable);
} else {
log.info("Transaction synchronization is NOT ACTIVE/ INACTIVE. Executing right now runnable {}", runnable);
runnable.run();
}
}
private static class TransactionSynchronizationImpl implements TransactionSynchronization {
private final List<Runnable> afterCommitRunnables = new ArrayList<>();
@Override
// Exception squid:S1217 - Is aspectJ proxy
@SuppressWarnings({ "squid:S1217" })
public void afterCommit() {
log.debug("Transaction successfully committed, executing {} runnables", afterCommitRunnables.size());
for (final Runnable afterCommitRunnable : afterCommitRunnables) {
log.debug("Executing runnable {}", afterCommitRunnable);
try {
afterCommitRunnable.run();
} catch (final RuntimeException e) {
log.error("Failed to execute runnable {}", afterCommitRunnable, e);
}
}
}
@Override
@SuppressWarnings({ "squid:S1217" })
public void afterCompletion(final int status) {
log.debug("Transaction completed after commit with status {}", status == TransactionSynchronization.STATUS_COMMITTED ? "COMMITTED" : "ROLLEDBACK");
}
private void afterCommit(final Runnable runnable) {
afterCommitRunnables.add(runnable);
}
}
}

View File

@@ -1,42 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.model.helper;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.springframework.beans.factory.annotation.Autowired;
/**
* A singleton bean which holds the {@link AfterTransactionCommitExecutor} to provide it to in beans not instantiated by spring e.g. JPA
* entities which cannot be autowired.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@SuppressWarnings("java:S6548") // java:S6548 - singleton holder ensures static access to spring resources in some places
public final class AfterTransactionCommitExecutorHolder {
private static final AfterTransactionCommitExecutorHolder SINGLETON = new AfterTransactionCommitExecutorHolder();
@Getter
private AfterTransactionCommitExecutor afterCommit;
/**
* @return the cache manager holder singleton instance
*/
public static AfterTransactionCommitExecutorHolder getInstance() {
return SINGLETON;
}
@Autowired // spring setter injection
public void setAfterTransactionCommitExecutor(final AfterTransactionCommitExecutor afterCommit) {
this.afterCommit = afterCommit;
}
}

View File

@@ -28,7 +28,7 @@ import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.tenancy.TenantAwareAuthenticationDetails;
import org.springframework.data.annotation.CreatedBy;
@@ -199,7 +199,7 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity {
protected static void doNotify(final Runnable runnable) {
// fire events onl AFTER transaction commit
AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(runnable);
AfterTransactionCommitExecutor.afterCommit(runnable);
}
protected boolean isController() {

View File

@@ -28,7 +28,7 @@ import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.tenancy.TenantAwareAuthenticationDetails;
import org.springframework.data.annotation.CreatedBy;
@@ -199,7 +199,7 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity {
protected static void doNotify(final Runnable runnable) {
// fire events onl AFTER transaction commit
AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(runnable);
AfterTransactionCommitExecutor.afterCommit(runnable);
}
protected boolean isController() {

View File

@@ -19,11 +19,11 @@ import jakarta.validation.Validation;
import io.micrometer.core.instrument.MeterRegistry;
import org.aopalliance.intercept.MethodInvocation;
import org.eclipse.hawkbit.ContextAware;
import org.eclipse.hawkbit.artifact.encryption.ArtifactEncryption;
import org.eclipse.hawkbit.artifact.encryption.ArtifactEncryptionSecretsStorage;
import org.eclipse.hawkbit.artifact.encryption.ArtifactEncryptionService;
import org.eclipse.hawkbit.repository.ActionFields;
import org.eclipse.hawkbit.repository.AutoAssignExecutor;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.PropertiesQuotaManagement;
import org.eclipse.hawkbit.repository.QueryField;
@@ -32,16 +32,12 @@ import org.eclipse.hawkbit.repository.RepositoryConfiguration;
import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.RolloutApprovalStrategy;
import org.eclipse.hawkbit.repository.RolloutExecutor;
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.repository.RolloutHandler;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.RolloutStatusCache;
import org.eclipse.hawkbit.repository.SecurityTokenGeneratorHolder;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.autoassign.AutoAssignExecutor;
import org.eclipse.hawkbit.repository.event.ApplicationEventFilter;
import org.eclipse.hawkbit.repository.event.remote.EventEntityManager;
import org.eclipse.hawkbit.repository.event.remote.EventEntityManagerHolder;
@@ -49,16 +45,12 @@ import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.aspects.ExceptionMappingAspectHandler;
import org.eclipse.hawkbit.repository.jpa.autoassign.AutoAssignChecker;
import org.eclipse.hawkbit.repository.jpa.autoassign.AutoAssignScheduler;
import org.eclipse.hawkbit.repository.jpa.autocleanup.AutoActionCleanup;
import org.eclipse.hawkbit.repository.jpa.autocleanup.AutoCleanupScheduler;
import org.eclipse.hawkbit.repository.jpa.autocleanup.CleanupTask;
import org.eclipse.hawkbit.repository.jpa.cluster.DistributedLockRepository;
import org.eclipse.hawkbit.repository.jpa.cluster.LockProperties;
import org.eclipse.hawkbit.repository.jpa.event.JpaEventEntityManager;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitDefaultServiceExecutor;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
@@ -67,7 +59,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.EntityInterceptorHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.TenantAwareHolder;
import org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison;
@@ -79,13 +70,11 @@ import org.eclipse.hawkbit.repository.jpa.repository.ArtifactRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutTargetGroupRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleTypeRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.rollout.RolloutScheduler;
import org.eclipse.hawkbit.repository.jpa.scheduler.RolloutScheduler;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.PauseRolloutGroupAction;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupActionEvaluator;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupConditionEvaluator;
@@ -94,11 +83,12 @@ import org.eclipse.hawkbit.repository.jpa.rollout.condition.StartNextGroupRollou
import org.eclipse.hawkbit.repository.jpa.rollout.condition.ThresholdRolloutGroupErrorCondition;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.ThresholdRolloutGroupSuccessCondition;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlParser;
import org.eclipse.hawkbit.repository.jpa.scheduler.AutoAssignScheduler;
import org.eclipse.hawkbit.repository.jpa.scheduler.JpaAutoAssignExecutor;
import org.eclipse.hawkbit.repository.jpa.scheduler.JpaRolloutHandler;
import org.eclipse.hawkbit.repository.jpa.utils.ExceptionMapper;
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.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.helper.SystemSecurityContextHolder;
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyResolver;
@@ -151,10 +141,12 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess
@EnableScheduling
@EnableRetry
@EntityScan("org.eclipse.hawkbit.repository.jpa.model")
@ComponentScan("org.eclipse.hawkbit.repository.jpa.management")
@ComponentScan({ "org.eclipse.hawkbit.repository.jpa.management", "org.eclipse.hawkbit.repository.jpa.scheduler" })
@PropertySource("classpath:/hawkbit-jpa-defaults.properties")
@Import({ JpaConfiguration.class, RepositoryConfiguration.class, LockProperties.class, DataSourceAutoConfiguration.class,
SystemManagementCacheKeyGenerator.class })
@Import({
RepositoryConfiguration.class,
JpaConfiguration.class, LockProperties.class, SystemManagementCacheKeyGenerator.class,
DataSourceAutoConfiguration.class})
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class JpaRepositoryConfiguration {
@@ -275,12 +267,6 @@ public class JpaRepositoryConfiguration {
return new SystemManagementCacheKeyGenerator(tenantAware);
}
@Bean
@ConditionalOnMissingBean
AfterTransactionCommitDefaultServiceExecutor afterTransactionCommitDefaultServiceExecutor() {
return new AfterTransactionCommitDefaultServiceExecutor();
}
@Bean
@ConditionalOnMissingBean
QuotaManagement staticQuotaManagement(final HawkbitSecurityProperties securityProperties) {
@@ -347,14 +333,6 @@ public class JpaRepositoryConfiguration {
return EntityInterceptorHolder.getInstance();
}
/**
* @return the singleton instance of the {@link AfterTransactionCommitExecutorHolder}
*/
@Bean
AfterTransactionCommitExecutorHolder afterTransactionCommitExecutorHolder() {
return AfterTransactionCommitExecutorHolder.getInstance();
}
/**
* @return {@link ExceptionMappingAspectHandler} aspect bean
*/
@@ -371,24 +349,6 @@ public class JpaRepositoryConfiguration {
return new JpaRolloutHandler(tenantAware, rolloutManagement, rolloutExecutor, lockRegistry, txManager, meterRegistry);
}
@Bean
@ConditionalOnMissingBean
RolloutExecutor rolloutExecutor(
final ActionRepository actionRepository, final RolloutGroupRepository rolloutGroupRepository,
final RolloutTargetGroupRepository rolloutTargetGroupRepository,
final RolloutRepository rolloutRepository, final TargetManagement<? extends Target> targetManagement,
final DeploymentManagement deploymentManagement, final RolloutGroupManagement rolloutGroupManagement,
final RolloutManagement rolloutManagement, final QuotaManagement quotaManagement,
final RolloutGroupEvaluationManager evaluationManager, final RolloutApprovalStrategy rolloutApprovalStrategy,
final EntityManager entityManager, final PlatformTransactionManager txManager,
final AfterTransactionCommitExecutor afterCommit,
final TenantAware tenantAware, final ContextAware contextAware, final RepositoryProperties repositoryProperties) {
return new JpaRolloutExecutor(actionRepository, rolloutGroupRepository, rolloutTargetGroupRepository,
rolloutRepository, targetManagement, deploymentManagement, rolloutGroupManagement, rolloutManagement,
quotaManagement, evaluationManager, rolloutApprovalStrategy, entityManager, txManager, afterCommit,
tenantAware, contextAware, repositoryProperties);
}
/**
* {@link DefaultRolloutApprovalStrategy} bean.
*
@@ -425,25 +385,6 @@ public class JpaRepositoryConfiguration {
return new JpaEventEntityManager(aware, entityManager);
}
/**
* {@link AutoAssignChecker} bean.
*
* @param targetFilterQueryManagement to get all target filter queries
* @param targetManagement to get targets
* @param deploymentManagement to assign distribution sets to targets
* @param transactionManager to run transactions
* @return a new {@link AutoAssignChecker}
*/
@Bean
@ConditionalOnMissingBean
AutoAssignExecutor autoAssignExecutor(
final TargetFilterQueryManagement<? extends TargetFilterQuery> targetFilterQueryManagement,
final TargetManagement<? extends Target> targetManagement,
final DeploymentManagement deploymentManagement,
final PlatformTransactionManager transactionManager, final ContextAware contextAware) {
return new AutoAssignChecker(targetFilterQueryManagement, targetManagement, deploymentManagement, transactionManager, contextAware);
}
/**
* {@link AutoAssignScheduler} bean.
* <p/>
@@ -454,7 +395,7 @@ public class JpaRepositoryConfiguration {
* @param systemSecurityContext to run as system
* @param autoAssignExecutor to run a check as tenant
* @param lockRegistry to lock the tenant for auto assignment
* @return a new {@link AutoAssignChecker}
* @return a new {@link JpaAutoAssignExecutor}
*/
@Bean
@ConditionalOnMissingBean

View File

@@ -9,6 +9,8 @@
*/
package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor.afterCommit;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -27,7 +29,6 @@ import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.management.JpaDeploymentManagement.MaxAssignmentsExceededInfo;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
@@ -59,7 +60,6 @@ import org.springframework.util.StringUtils;
public abstract class AbstractDsAssignmentStrategy {
protected final TargetRepository targetRepository;
protected final AfterTransactionCommitExecutor afterCommit;
protected final ActionRepository actionRepository;
private final ActionStatusRepository actionStatusRepository;
@@ -72,13 +72,11 @@ public abstract class AbstractDsAssignmentStrategy {
@SuppressWarnings("java:S107")
AbstractDsAssignmentStrategy(
final TargetRepository targetRepository,
final AfterTransactionCommitExecutor afterCommit,
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig,
final BooleanSupplier confirmationFlowConfig, final RepositoryProperties repositoryProperties,
final Consumer<MaxAssignmentsExceededInfo> maxAssignmentExceededHandler) {
this.targetRepository = targetRepository;
this.afterCommit = afterCommit;
this.actionRepository = actionRepository;
this.actionStatusRepository = actionStatusRepository;
this.quotaManagement = quotaManagement;
@@ -133,7 +131,7 @@ public abstract class AbstractDsAssignmentStrategy {
}
protected void sendTargetUpdatedEvent(final JpaTarget target) {
afterCommit.afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new TargetUpdatedEvent(target)));
afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new TargetUpdatedEvent(target)));
}
/**
@@ -209,8 +207,7 @@ public abstract class AbstractDsAssignmentStrategy {
* @param action the action of the assignment
*/
protected void cancelAssignDistributionSetEvent(final Action action) {
afterCommit.afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher()
.publishEvent(new CancelTargetAssignmentEvent(action)));
afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new CancelTargetAssignmentEvent(action)));
}
protected boolean isMultiAssignmentsEnabled() {
@@ -279,8 +276,8 @@ public abstract class AbstractDsAssignmentStrategy {
return;
}
final String tenant = actions.get(0).getTenant();
afterCommit.afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher()
.publishEvent(new CancelTargetAssignmentEvent(tenant, actions)));
afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(
new CancelTargetAssignmentEvent(tenant, actions)));
}
private void assertActionsPerTargetQuota(final Target target) {

View File

@@ -9,6 +9,8 @@
*/
package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor.afterCommit;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
@@ -38,7 +40,6 @@ import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
import org.eclipse.hawkbit.repository.jpa.repository.ArtifactRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
import org.eclipse.hawkbit.repository.jpa.specifications.ArtifactSpecifications;
@@ -185,7 +186,7 @@ public class JpaArtifactManagement implements ArtifactManagement {
artifactRepository.deleteById(id);
final String sha1Hash = toDelete.getSha1Hash();
AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(() -> clearArtifactBinary(sha1Hash));
afterCommit(() -> clearArtifactBinary(sha1Hash));
}
/**
@@ -202,11 +203,10 @@ public class JpaArtifactManagement implements ArtifactManagement {
void clearArtifactBinary(final String sha1Hash) {
DeploymentHelper.runInNewTransaction(txManager, "clearArtifactBinary", status -> {
// countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse will skip ACM checks and will return total count as it should be
if (artifactRepository.countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse(sha1Hash,
tenantAware.getCurrentTenant()) <= 0) { // 1 artifact is the one being deleted!
if (artifactRepository.countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse(sha1Hash, tenantAware.getCurrentTenant()) <= 0) {
// removes the real artifact ONLY AFTER the delete of artifact or software module
// in local history has passed successfully (caller has permission and no errors)
AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(() -> {
afterCommit(() -> {
try {
log.debug("deleting artifact from repository {}", sha1Hash);
artifactStorage.deleteBySha1(tenantAware.getCurrentTenant(), sha1Hash);

View File

@@ -9,6 +9,7 @@
*/
package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor.afterCommit;
import static org.eclipse.hawkbit.repository.model.Action.Status.DOWNLOADED;
import static org.eclipse.hawkbit.repository.model.Action.Status.FINISHED;
import static org.eclipse.hawkbit.repository.model.Target.CONTROLLER_ATTRIBUTE_MAX_KEY_SIZE;
@@ -70,7 +71,6 @@ import org.eclipse.hawkbit.repository.exception.SoftwareModuleNotAssignedToTarge
import org.eclipse.hawkbit.repository.jpa.Jpa;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
@@ -153,7 +153,6 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
private final Duration maxPollingTime;
private final PlatformTransactionManager txManager;
private final EntityManager entityManager;
private final AfterTransactionCommitExecutor afterCommit;
private final SystemSecurityContext systemSecurityContext;
private final TenantAware tenantAware;
@@ -168,7 +167,6 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
final DistributionSetManagement<? extends DistributionSet> distributionSetManagement,
final TenantConfigurationManagement tenantConfigurationManagement, final ControllerPollProperties controllerPollProperties,
final PlatformTransactionManager txManager, final EntityManager entityManager,
final AfterTransactionCommitExecutor afterCommit,
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware,
final ScheduledExecutorService executorService) {
super(actionRepository, actionStatusRepository, quotaManagement, repositoryProperties);
@@ -190,7 +188,6 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
: DurationHelper.fromString(controllerPollProperties.getMaxPollingTime());
this.txManager = txManager;
this.entityManager = entityManager;
this.afterCommit = afterCommit;
this.systemSecurityContext = systemSecurityContext;
this.tenantAware = tenantAware;
@@ -676,9 +673,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
}
final Target result = targetRepository.save(jpaTarget);
afterCommit.afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new TargetPollEvent(result)));
afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new TargetPollEvent(result)));
return result;
}
@@ -726,7 +721,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
pollChunks.forEach(chunk -> {
setLastTargetQuery(tenant, System.currentTimeMillis(), chunk);
chunk.forEach(controllerId -> afterCommit.afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher()
chunk.forEach(controllerId -> afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher()
.publishEvent(new TargetPollEvent(controllerId, tenant))));
});
@@ -787,7 +782,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
toUpdate.setUpdateStatus(TargetUpdateStatus.REGISTERED);
}
toUpdate.setLastTargetQuery(System.currentTimeMillis());
afterCommit.afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new TargetPollEvent(toUpdate)));
afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new TargetPollEvent(toUpdate)));
return targetRepository.save(toUpdate);
}
return toUpdate;
@@ -926,8 +921,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
}
private void cancelAssignDistributionSetEvent(final Action action) {
afterCommit.afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher()
.publishEvent(new CancelTargetAssignmentEvent(action)));
afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new CancelTargetAssignmentEvent(action)));
}
/**

View File

@@ -57,7 +57,6 @@ import org.eclipse.hawkbit.repository.jpa.Jpa;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
@@ -151,7 +150,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
protected JpaDeploymentManagement(final EntityManager entityManager, final ActionRepository actionRepository,
final JpaDistributionSetManagement distributionSetManagement, final TargetRepository targetRepository,
final ActionStatusRepository actionStatusRepository, final AuditorAware<String> auditorProvider,
final AfterTransactionCommitExecutor afterCommit, final PlatformTransactionManager txManager,
final PlatformTransactionManager txManager,
final TenantConfigurationManagement tenantConfigurationManagement, final QuotaManagement quotaManagement,
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware, final AuditorAware<String> auditorAware,
final JpaProperties jpaProperties, final RepositoryProperties repositoryProperties) {
@@ -166,10 +165,10 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
maxAssignmentsExceededInfo.targetId,
maxAssignmentsExceededInfo.requested,
maxAssignmentsExceededInfo.quotaExceededException);
onlineDsAssignmentStrategy = new OnlineDsAssignmentStrategy(targetRepository, afterCommit, actionRepository, actionStatusRepository,
onlineDsAssignmentStrategy = new OnlineDsAssignmentStrategy(targetRepository, actionRepository, actionStatusRepository,
quotaManagement, this::isMultiAssignmentsEnabled, this::isConfirmationFlowEnabled, repositoryProperties,
maxAssignmentsExceededHandler);
offlineDsAssignmentStrategy = new OfflineDsAssignmentStrategy(targetRepository, afterCommit, actionRepository, actionStatusRepository,
offlineDsAssignmentStrategy = new OfflineDsAssignmentStrategy(targetRepository, actionRepository, actionStatusRepository,
quotaManagement, this::isMultiAssignmentsEnabled, this::isConfirmationFlowEnabled, repositoryProperties,
maxAssignmentsExceededHandler);
this.tenantConfigurationManagement = tenantConfigurationManagement;

View File

@@ -9,6 +9,8 @@
*/
package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor.afterCommit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -52,7 +54,6 @@ import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
import org.eclipse.hawkbit.repository.jpa.Jpa;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
@@ -60,13 +61,13 @@ 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.ql.QLSupport;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.StartNextGroupRolloutGroupSuccessAction;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications;
import org.eclipse.hawkbit.repository.jpa.specifications.RolloutSpecification;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
@@ -85,8 +86,8 @@ 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.security.SystemSecurityContext;
import org.eclipse.hawkbit.utils.ObjectCopyUtil;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.utils.ObjectCopyUtil;
import org.eclipse.hawkbit.utils.TenantConfigHelper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
@@ -121,7 +122,7 @@ public class JpaRolloutManagement implements RolloutManagement {
RolloutStatus.PAUSED, RolloutStatus.APPROVAL_DENIED);
@Value("${hawkbit.repository.jpa.management.rollout.max.actions.per.transaction:5000}")
private int MAX_ACTIONS;
private int maxActions;
private final EntityManager entityManager;
private final RolloutRepository rolloutRepository;
@@ -136,7 +137,6 @@ public class JpaRolloutManagement implements RolloutManagement {
private final TenantAware tenantAware;
private final TenantConfigurationManagement tenantConfigurationManagement;
private final QuotaManagement quotaManagement;
private final AfterTransactionCommitExecutor afterCommit;
private final SystemSecurityContext systemSecurityContext;
private final ContextAware contextAware;
private final RepositoryProperties repositoryProperties;
@@ -157,7 +157,6 @@ public class JpaRolloutManagement implements RolloutManagement {
final TenantAware tenantAware,
final TenantConfigurationManagement tenantConfigurationManagement,
final QuotaManagement quotaManagement,
final AfterTransactionCommitExecutor afterCommit,
final SystemSecurityContext systemSecurityContext, final ContextAware contextAware,
final RepositoryProperties repositoryProperties) {
this.entityManager = entityManager;
@@ -173,12 +172,11 @@ public class JpaRolloutManagement implements RolloutManagement {
this.tenantAware = tenantAware;
this.tenantConfigurationManagement = tenantConfigurationManagement;
this.quotaManagement = quotaManagement;
this.afterCommit = afterCommit;
this.systemSecurityContext = systemSecurityContext;
this.contextAware = contextAware;
this.repositoryProperties = repositoryProperties;
this.onlineDsAssignmentStrategy = new OnlineDsAssignmentStrategy(targetRepository, afterCommit, actionRepository, actionStatusRepository,
this.onlineDsAssignmentStrategy = new OnlineDsAssignmentStrategy(targetRepository, actionRepository, actionStatusRepository,
quotaManagement, this::isMultiAssignmentsEnabled, this::isConfirmationFlowEnabled, repositoryProperties, null);
}
@@ -187,7 +185,7 @@ public class JpaRolloutManagement implements RolloutManagement {
}
public void publishRolloutGroupCreatedEventAfterCommit(final RolloutGroup group, final Rollout rollout) {
afterCommit.afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher()
afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher()
.publishEvent(new RolloutGroupCreatedEvent(group, rollout.getId())));
}
@@ -524,7 +522,7 @@ public class JpaRolloutManagement implements RolloutManagement {
ActionSpecifications
.byRolloutIdAndActiveAndStatusIsNot(rollout.getId(),
List.of(Action.Status.CANCELING)), // avoid cancelling state here, because it is count as still active
Pageable.ofSize(MAX_ACTIONS))
Pageable.ofSize(maxActions))
.getContent();
log.info("Found {} active actions for rollout {}, performing soft cancel.", actions.size(), rollout.getId());
@@ -535,7 +533,7 @@ public class JpaRolloutManagement implements RolloutManagement {
}
private void forceQuitActionsOfRollout(final Rollout rollout) {
final List<JpaAction> actions = findActiveActionsForRollout(rollout.getId(), Pageable.ofSize(MAX_ACTIONS))
final List<JpaAction> actions = findActiveActionsForRollout(rollout.getId(), Pageable.ofSize(maxActions))
.getContent();
log.info("Found {} active actions for rollout {}", actions.size(), rollout.getId());

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_DELAY;
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_MAX;
import static org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor.afterCommit;
import java.util.Collection;
import java.util.Collections;
@@ -33,7 +34,6 @@ import org.eclipse.hawkbit.repository.exception.LockedException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
import org.eclipse.hawkbit.repository.jpa.specifications.SoftwareModuleSpecification;
@@ -195,8 +195,7 @@ public class JpaSoftwareModuleManagement extends
jpaRepository.getAccessController()
.ifPresent(accessController -> accessController.assertOperationAllowed(AccessController.Operation.DELETE, swModule));
final Set<String> sha1Hashes = swModule.getArtifacts().stream().map(Artifact::getSha1Hash).collect(Collectors.toSet());
AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit()
.afterCommit(() -> sha1Hashes.forEach(((JpaArtifactManagement) artifactManagement)::clearArtifactBinary));
afterCommit(() -> sha1Hashes.forEach(((JpaArtifactManagement) artifactManagement)::clearArtifactBinary));
}
private void assertDistributionSetExists(final long distributionSetId) {

View File

@@ -16,9 +16,8 @@ import jakarta.persistence.EntityManager;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.artifact.ArtifactStorage;
import org.eclipse.hawkbit.tenancy.cache.TenantCacheManager;
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager;
import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.RolloutStatusCache;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TenantStatsManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
@@ -98,8 +97,6 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
private final SystemSecurityContext systemSecurityContext;
private final TenantAware tenantAware;
private final PlatformTransactionManager txManager;
private final TenantCacheManager cacheManager;
private final RolloutStatusCache rolloutStatusCache;
private final EntityManager entityManager;
private final RepositoryProperties repositoryProperties;
@@ -116,7 +113,6 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
final TenantConfigurationRepository tenantConfigurationRepository, final TenantMetaDataRepository tenantMetaDataRepository,
final TenantStatsManagement systemStatsManagement, final SystemManagementCacheKeyGenerator currentTenantCacheKeyGenerator,
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware, final PlatformTransactionManager txManager,
final TenantCacheManager cacheManager, final RolloutStatusCache rolloutStatusCache,
final EntityManager entityManager, final RepositoryProperties repositoryProperties,
final JpaProperties properties) {
this.targetRepository = targetRepository;
@@ -136,8 +132,6 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
this.systemSecurityContext = systemSecurityContext;
this.tenantAware = tenantAware;
this.txManager = txManager;
this.cacheManager = cacheManager;
this.rolloutStatusCache = rolloutStatusCache;
this.entityManager = entityManager;
this.repositoryProperties = repositoryProperties;
@@ -166,8 +160,7 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
}
final String tenant = t.toUpperCase();
cacheManager.evictCaches(tenant);
rolloutStatusCache.evictCaches(tenant);
TenantAwareCacheManager.getInstance().evictTenant(tenant);
tenantAware.runAsTenant(tenant, () -> DeploymentHelper.runInNewTransaction(txManager, "deleteTenant", status -> {
tenantMetaDataRepository.deleteByTenantIgnoreCase(tenant);
tenantConfigurationRepository.deleteByTenant(tenant);

View File

@@ -32,6 +32,7 @@ import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
@@ -50,6 +51,8 @@ public class JpaTargetTypeManagement
extends AbstractJpaRepositoryManagement<JpaTargetType, TargetTypeManagement.Create, TargetTypeManagement.Update, TargetTypeRepository, TargetTypeFields>
implements TargetTypeManagement<JpaTargetType>{
private static final String CACHE_TARGET_TYPE_NAME = "targetType";
private final TargetRepository targetRepository;
private final DistributionSetTypeRepository distributionSetTypeRepository;
private final QuotaManagement quotaManagement;
@@ -76,6 +79,7 @@ public class JpaTargetTypeManagement
}
@Override
@Cacheable(value = CACHE_TARGET_TYPE_NAME, key = "#key")
public Optional<TargetType> findByKey(final String key) {
return jpaRepository.findOne(TargetTypeSpecification.hasKey(key)).map(TargetType.class::cast);
}

View File

@@ -10,6 +10,7 @@
package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_GATEWAY_SECURITY_TOKEN;
import static org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor.afterCommit;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.BATCH_ASSIGNMENTS_ENABLED;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED;
@@ -37,7 +38,6 @@ import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.exception.TenantConfigurationValidatorException;
import org.eclipse.hawkbit.repository.exception.TenantConfigurationValueChangeNotAllowedException;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTenantConfiguration;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
@@ -48,13 +48,13 @@ import org.eclipse.hawkbit.repository.model.TenantConfiguration;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.repository.model.helper.SystemSecurityContextHolder;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager;
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
import org.eclipse.hawkbit.tenancy.configuration.PollingTime;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationContext;
@@ -79,28 +79,24 @@ import org.springframework.validation.annotation.Validated;
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "tenant-configuration-management" }, matchIfMissing = true)
public class JpaTenantConfigurationManagement implements TenantConfigurationManagement {
private static final String CACHE_TENANT_CONFIGURATION_NAME = "tenantConfiguration";
private static final ConfigurableConversionService CONVERSION_SERVICE = new DefaultConversionService();
private final TenantConfigurationRepository tenantConfigurationRepository;
private final TenantConfigurationProperties tenantConfigurationProperties;
private final ApplicationContext applicationContext;
private final CacheManager cacheManager;
private final AfterTransactionCommitExecutor afterCommitExecutor;
public JpaTenantConfigurationManagement(
final TenantConfigurationRepository tenantConfigurationRepository,
final TenantConfigurationProperties tenantConfigurationProperties,
final CacheManager cacheManager, final AfterTransactionCommitExecutor afterCommitExecutor,
final ApplicationContext applicationContext) {
this.tenantConfigurationRepository = tenantConfigurationRepository;
this.tenantConfigurationProperties = tenantConfigurationProperties;
this.cacheManager = cacheManager;
this.afterCommitExecutor = afterCommitExecutor;
this.applicationContext = applicationContext;
}
@Override
@CacheEvict(value = "tenantConfiguration", key = "#configurationKeyName")
@CacheEvict(value = CACHE_TENANT_CONFIGURATION_NAME, key = "#configurationKeyName")
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
@@ -114,8 +110,8 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public <T extends Serializable> Map<String, TenantConfigurationValue<T>> addOrUpdateConfiguration(final Map<String, T> configurations) {
// Register a callback to be invoked after the transaction is committed - for cache eviction
afterCommitExecutor.afterCommit(() -> {
final Cache cache = cacheManager.getCache("tenantConfiguration");
afterCommit(() -> {
final Cache cache = TenantAwareCacheManager.getInstance().getCache(CACHE_TENANT_CONFIGURATION_NAME);
if (cache != null) {
configurations.keySet().forEach(cache::evict);
}
@@ -125,7 +121,7 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
}
@Override
@CacheEvict(value = "tenantConfiguration", key = "#configurationKeyName")
@CacheEvict(value = CACHE_TENANT_CONFIGURATION_NAME, key = "#configurationKeyName")
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
@@ -135,7 +131,7 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
@Override
// TODO - check if cache works
// @Cacheable(value = "tenantConfiguration", key = "#configurationKeyName")
// @Cacheable(value = CACHE_TENANT_CONFIGURATION_NAME, key = "#configurationKeyName")
public <T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(final String configurationKeyName) {
checkAccess(configurationKeyName);
@@ -145,7 +141,7 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
}
@Override
@Cacheable(value = "tenantConfiguration", key = "#configurationKeyName")
@Cacheable(value = CACHE_TENANT_CONFIGURATION_NAME, key = "#configurationKeyName")
public <T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(
final String configurationKeyName, final Class<T> propertyType) {
checkAccess(configurationKeyName);
@@ -163,8 +159,8 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
final TenantConfigurationKey key = tenantConfigurationProperties.fromKeyName(configurationKeyName);
if (!key.getDataType().isAssignableFrom(propertyType)) {
throw new TenantConfigurationValidatorException(String.format(
"Cannot parse the database value of type %s into the type %s.", key.getDataType(), propertyType));
throw new TenantConfigurationValidatorException(
String.format("Cannot parse the database value of type %s into the type %s.", key.getDataType(), propertyType));
}
return CONVERSION_SERVICE.convert(key.getDefaultValue(), propertyType);
@@ -372,7 +368,7 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
}
private void evictCacheEntryByKeyIfPresent(final String key) {
final Cache cache = cacheManager.getCache("tenantConfiguration");
final Cache cache = TenantAwareCacheManager.getInstance().getCache(CACHE_TENANT_CONFIGURATION_NAME);
if (cache != null) {
cache.evictIfPresent(key);
}

View File

@@ -45,12 +45,11 @@ class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
OfflineDsAssignmentStrategy(
final TargetRepository targetRepository,
final AfterTransactionCommitExecutor afterCommit,
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig,
final BooleanSupplier confirmationFlowConfig, final RepositoryProperties repositoryProperties,
final Consumer<MaxAssignmentsExceededInfo> maxAssignmentExceededHandler) {
super(targetRepository, afterCommit, actionRepository, actionStatusRepository,
super(targetRepository, actionRepository, actionStatusRepository,
quotaManagement, multiAssignmentsConfig, confirmationFlowConfig, repositoryProperties, maxAssignmentExceededHandler);
}

View File

@@ -9,6 +9,8 @@
*/
package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor.afterCommit;
import java.util.Collections;
import java.util.List;
import java.util.function.BooleanSupplier;
@@ -26,7 +28,6 @@ import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEv
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.management.JpaDeploymentManagement.MaxAssignmentsExceededInfo;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
@@ -49,13 +50,13 @@ import org.springframework.util.CollectionUtils;
*/
class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
OnlineDsAssignmentStrategy(final TargetRepository targetRepository,
final AfterTransactionCommitExecutor afterCommit,
OnlineDsAssignmentStrategy(
final TargetRepository targetRepository,
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig,
final BooleanSupplier confirmationFlowConfig, final RepositoryProperties repositoryProperties,
final Consumer<MaxAssignmentsExceededInfo> maxAssignmentExceededHandler) {
super(targetRepository, afterCommit, actionRepository, actionStatusRepository,
super(targetRepository, actionRepository, actionStatusRepository,
quotaManagement, multiAssignmentsConfig, confirmationFlowConfig, repositoryProperties, maxAssignmentExceededHandler);
}
@@ -124,8 +125,8 @@ class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
}
@Override
public void setAssignedDistributionSetAndTargetStatus(final JpaDistributionSet set, final List<List<Long>> targetIds,
final String currentUser) {
public void setAssignedDistributionSetAndTargetStatus(
final JpaDistributionSet set, final List<List<Long>> targetIds, final String currentUser) {
final long now = System.currentTimeMillis();
targetIds.forEach(targetIdsChunk -> {
if (targetRepository.count(AccessController.Operation.UPDATE,
@@ -223,10 +224,8 @@ class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
return;
}
afterCommit.afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher()
.publishEvent(new TargetAssignDistributionSetEvent(
tenant, distributionSetId, actions,
actions.get(0).isMaintenanceWindowAvailable())));
afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(
new TargetAssignDistributionSetEvent(tenant, distributionSetId, actions, actions.get(0).isMaintenanceWindowAvailable())));
}
/**
@@ -237,8 +236,7 @@ class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
* @param actions assigned to the targets
*/
private void sendMultiActionCancelEvent(final String tenant, final List<Action> actions) {
afterCommit.afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher()
.publishEvent(new MultiActionCancelEvent(tenant, actions)));
afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new MultiActionCancelEvent(tenant, actions)));
}
/**
@@ -248,7 +246,6 @@ class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
* @param actions assigned to the targets
*/
private void sendMultiActionAssignEvent(final String tenant, final List<Action> actions) {
afterCommit.afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher()
.publishEvent(new MultiActionAssignEvent(tenant, actions)));
afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new MultiActionAssignEvent(tenant, actions)));
}
}

View File

@@ -36,7 +36,6 @@ public interface TenantConfigurationRepository extends BaseEntityRepository<JpaT
/**
* 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);
@@ -44,7 +43,7 @@ public interface TenantConfigurationRepository extends BaseEntityRepository<JpaT
/**
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety
* reasons (this is a "delete everything" query after all) we add the tenant
* manually to query even if this will by done by {@link EntityManager}
* manually to query even if this will be done by {@link EntityManager}
* anyhow. The DB should take care of optimizing this away.
*
* @param tenant to delete data from
@@ -53,5 +52,4 @@ public interface TenantConfigurationRepository extends BaseEntityRepository<JpaT
@Transactional
@Query("DELETE FROM JpaTenantConfiguration t WHERE t.tenant = :tenant")
void deleteByTenant(@Param("tenant") String tenant);
}

View File

@@ -15,6 +15,7 @@ import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
public class BlockWhenFullPolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
try {

View File

@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.autoassign;
package org.eclipse.hawkbit.repository.jpa.scheduler;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@@ -15,8 +15,8 @@ import java.util.concurrent.locks.Lock;
import io.micrometer.core.instrument.MeterRegistry;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.AutoAssignExecutor;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.autoassign.AutoAssignExecutor;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantMetricsConfiguration;
import org.springframework.integration.support.locks.LockRegistry;
@@ -28,7 +28,7 @@ import org.springframework.scheduling.annotation.Scheduled;
@Slf4j
public class AutoAssignScheduler {
private static final String PROP_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.autoassign.scheduler.fixedDelay:2000}";
private static final String PROP_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.scheduler.scheduler.fixedDelay:2000}";
private final SystemManagement systemManagement;
private final SystemSecurityContext systemSecurityContext;
@@ -70,7 +70,7 @@ public class AutoAssignScheduler {
// workaround eclipselink that is currently not possible to execute a query without multitenancy if MultiTenant
// annotation is used - https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So iterate through all tenants and execute the rollout
// check for each tenant separately.
final Lock lock = lockRegistry.obtain("autoassign");
final Lock lock = lockRegistry.obtain("scheduler");
if (!lock.tryLock()) {
return null;
}
@@ -79,20 +79,20 @@ public class AutoAssignScheduler {
try {
log.debug("Auto assign scheduled execution has acquired lock and started for each tenant.");
systemManagement.forEachTenant(tenant -> {
final long startNanoT = System.nanoTime();
final long startNanoT = System.nanoTime();
autoAssignExecutor.checkAllTargets();
meterRegistry
.map(mReg -> mReg.timer(
"hawkbit.autoassign.executor",
"hawkbit.scheduler.executor",
TenantMetricsConfiguration.TENANT_TAG, tenant))
.ifPresent(timer -> timer.record(System.nanoTime() - startNanoT, TimeUnit.NANOSECONDS));
});
} finally {
lock.unlock();
meterRegistry
.map(mReg -> mReg.timer("hawkbit.autoassign.executor.all"))
.map(mReg -> mReg.timer("hawkbit.scheduler.executor.all"))
.ifPresent(timer -> timer.record(System.nanoTime() - startNano, TimeUnit.NANOSECONDS));
log.debug("Auto assign scheduled execution has released lock and finished.");
}

View File

@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.autoassign;
package org.eclipse.hawkbit.repository.jpa.scheduler;
import java.util.Collections;
import java.util.List;
@@ -21,7 +21,7 @@ import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.autoassign.AutoAssignExecutor;
import org.eclipse.hawkbit.repository.AutoAssignExecutor;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
import org.eclipse.hawkbit.repository.model.Action;
@@ -31,6 +31,7 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
@@ -43,7 +44,8 @@ import org.springframework.util.StringUtils;
* query, that match the TFQ and that don't have the auto assign DS in their action history.
*/
@Slf4j
public class AutoAssignChecker implements AutoAssignExecutor {
@Service
public class JpaAutoAssignExecutor implements AutoAssignExecutor {
/**
* The message which is added to the action status when a distribution set is assigned to a target.
@@ -62,7 +64,7 @@ public class AutoAssignChecker implements AutoAssignExecutor {
private final PlatformTransactionManager transactionManager;
private final ContextAware contextAware;
public AutoAssignChecker(
public JpaAutoAssignExecutor(
final TargetFilterQueryManagement<? extends TargetFilterQuery> targetFilterQueryManagement,
final TargetManagement<? extends Target> targetManagement, final DeploymentManagement deploymentManagement,
final PlatformTransactionManager transactionManager, final ContextAware contextAware) {

View File

@@ -7,7 +7,9 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa;
package org.eclipse.hawkbit.repository.jpa.scheduler;
import static org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor.afterCommit;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
@@ -37,7 +39,6 @@ import org.eclipse.hawkbit.repository.event.remote.RolloutStoppedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.RolloutUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.management.JpaRolloutManagement;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
@@ -69,6 +70,7 @@ import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionException;
@@ -76,6 +78,7 @@ import org.springframework.transaction.TransactionException;
* A Jpa implementation of {@link RolloutExecutor}
*/
@Slf4j
@Service
public class JpaRolloutExecutor implements RolloutExecutor {
/**
@@ -112,7 +115,6 @@ public class JpaRolloutExecutor implements RolloutExecutor {
private final RolloutApprovalStrategy rolloutApprovalStrategy;
private final EntityManager entityManager;
private final PlatformTransactionManager txManager;
private final AfterTransactionCommitExecutor afterCommit;
private final TenantAware tenantAware;
private final ContextAware contextAware;
private final RepositoryProperties repositoryProperties;
@@ -127,7 +129,6 @@ public class JpaRolloutExecutor implements RolloutExecutor {
final RolloutManagement rolloutManagement, final QuotaManagement quotaManagement,
final RolloutGroupEvaluationManager evaluationManager, final RolloutApprovalStrategy rolloutApprovalStrategy,
final EntityManager entityManager, final PlatformTransactionManager txManager,
final AfterTransactionCommitExecutor afterCommit,
final TenantAware tenantAware, final ContextAware contextAware, final RepositoryProperties repositoryProperties) {
this.actionRepository = actionRepository;
this.rolloutGroupRepository = rolloutGroupRepository;
@@ -142,7 +143,6 @@ public class JpaRolloutExecutor implements RolloutExecutor {
this.rolloutApprovalStrategy = rolloutApprovalStrategy;
this.entityManager = entityManager;
this.txManager = txManager;
this.afterCommit = afterCommit;
this.tenantAware = tenantAware;
this.contextAware = contextAware;
this.repositoryProperties = repositoryProperties;
@@ -327,8 +327,8 @@ public class JpaRolloutExecutor implements RolloutExecutor {
rolloutRepository.save(rollout);
final List<Long> groupIds = rollout.getRolloutGroups().stream().map(RolloutGroup::getId).toList();
afterCommit.afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new RolloutStoppedEvent(
tenantAware.getCurrentTenant(), rollout.getId(), groupIds)));
afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(
new RolloutStoppedEvent(tenantAware.getCurrentTenant(), rollout.getId(), groupIds)));
}
}
@@ -392,8 +392,7 @@ public class JpaRolloutExecutor implements RolloutExecutor {
.map(Action::getId)
.toList();
actionRepository.deleteAllById(actionIds);
afterCommit.afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher()
.publishEvent(new RolloutUpdatedEvent(rollout)));
afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new RolloutUpdatedEvent(rollout)));
} catch (final RuntimeException e) {
log.error("Exception during deletion of actions of rollout {}", rollout, e);
}

View File

@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa;
package org.eclipse.hawkbit.repository.jpa.scheduler;
import java.util.List;
import java.util.Optional;

View File

@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.rollout;
package org.eclipse.hawkbit.repository.jpa.scheduler;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@@ -16,6 +16,7 @@ import io.micrometer.core.instrument.MeterRegistry;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.RolloutHandler;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.jpa.rollout.BlockWhenFullPolicy;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantMetricsConfiguration;
import org.springframework.scheduling.annotation.Scheduled;

View File

@@ -124,5 +124,4 @@ class ConcurrentDistributionSetInvalidationTest extends AbstractJpaIntegrationTe
return slowGroupRepo;
}
}
}

View File

@@ -22,8 +22,8 @@ import java.util.Optional;
import org.eclipse.hawkbit.repository.Identifiable;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement.AutoAssignDistributionSetUpdate;
import org.eclipse.hawkbit.repository.autoassign.AutoAssignExecutor;
import org.eclipse.hawkbit.repository.jpa.autoassign.AutoAssignScheduler;
import org.eclipse.hawkbit.repository.AutoAssignExecutor;
import org.eclipse.hawkbit.repository.jpa.scheduler.AutoAssignScheduler;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;

View File

@@ -22,7 +22,7 @@ import java.util.concurrent.Callable;
import lombok.SneakyThrows;
import org.eclipse.hawkbit.ContextAware;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.autoassign.AutoAssignExecutor;
import org.eclipse.hawkbit.repository.AutoAssignExecutor;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;

View File

@@ -22,7 +22,7 @@ import java.util.Arrays;
import java.util.Optional;
import org.eclipse.hawkbit.repository.Identifiable;
import org.eclipse.hawkbit.repository.jpa.rollout.RolloutScheduler;
import org.eclipse.hawkbit.repository.jpa.scheduler.RolloutScheduler;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Target;
import org.junit.jupiter.api.Test;

View File

@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.autoassign;
package org.eclipse.hawkbit.repository.jpa.scheduler;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -44,18 +44,18 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
/**
* Test class for {@link AutoAssignChecker}.
* Test class for {@link JpaAutoAssignExecutor}.
* <p/>
* Feature: Component Tests - Repository<br/>
* Story: Auto assign checker
*/
@SuppressWarnings("java:S6813") // constructor injects are not possible for test classes
class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest {
class JpaAutoAssignExecutorIntTest extends AbstractJpaIntegrationTest {
private static final String SPACE_AND_DESCRIPTION = " description";
@Autowired
private AutoAssignChecker autoAssignChecker;
private JpaAutoAssignExecutor autoAssignChecker;
@Autowired
private DeploymentManagement deploymentManagement;

View File

@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.autoassign;
package org.eclipse.hawkbit.repository.jpa.scheduler;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -45,7 +45,7 @@ import org.springframework.transaction.PlatformTransactionManager;
* Story: Auto assign checker
*/
@ExtendWith(MockitoExtension.class)
class AutoAssignCheckerTest {
class JpaAutoAssignExecutorTest {
@Mock
private TargetFilterQueryManagement<? extends TargetFilterQuery> targetFilterQueryManagement;
@@ -58,11 +58,11 @@ class AutoAssignCheckerTest {
@Mock
private ContextAware contextAware;
private AutoAssignChecker autoAssignChecker;
private JpaAutoAssignExecutor autoAssignChecker;
@BeforeEach
void before() {
autoAssignChecker = new AutoAssignChecker(
autoAssignChecker = new JpaAutoAssignExecutor(
targetFilterQueryManagement, targetManagement, deploymentManagement, transactionManager, contextAware);
}

View File

@@ -22,8 +22,8 @@ import org.eclipse.hawkbit.artifact.fs.FileArtifactProperties;
import org.eclipse.hawkbit.artifact.fs.FileArtifactStorage;
import org.eclipse.hawkbit.artifact.urlresolver.PropertyBasedArtifactUrlResolver;
import org.eclipse.hawkbit.artifact.urlresolver.PropertyBasedArtifactUrlResolverProperties;
import org.eclipse.hawkbit.tenancy.cache.TenantAwareCacheManager;
import org.eclipse.hawkbit.im.authentication.Hierarchy;
import org.eclipse.hawkbit.repository.RepositoryConfiguration;
import org.eclipse.hawkbit.repository.RolloutApprovalStrategy;
import org.eclipse.hawkbit.repository.RolloutStatusCache;
import org.eclipse.hawkbit.repository.event.ApplicationEventFilter;
@@ -49,10 +49,10 @@ import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
@@ -78,6 +78,10 @@ import org.springframework.security.concurrent.DelegatingSecurityContextSchedule
@PropertySource("classpath:/hawkbit-test-defaults.properties")
public class TestConfiguration implements AsyncConfigurer {
@Configuration
@Import(RepositoryConfiguration.class)
static class OverridePropertiesSourceFromRepositoryConfiguration {}
@Override
public Executor getAsyncExecutor() {
return asyncExecutor();
@@ -104,7 +108,7 @@ public class TestConfiguration implements AsyncConfigurer {
*/
@Bean
RolloutStatusCache rolloutStatusCache(final TenantAware tenantAware) {
return new RolloutStatusCache(tenantAware, 0);
return new RolloutStatusCache(tenantAware);
}
@Bean
@@ -158,11 +162,6 @@ public class TestConfiguration implements AsyncConfigurer {
return org.mockito.Mockito.spy(new SecurityContextTenantAware(authoritiesResolver, securityContextSerializer, tenantResolver));
}
@Bean
TenantAwareCacheManager cacheManager(final TenantAware tenantAware) {
return new TenantAwareCacheManager(new CaffeineCacheManager(), tenantAware);
}
@Bean(name = AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)
SimpleApplicationEventMulticaster applicationEventMulticaster(final ApplicationEventFilter applicationEventFilter) {
final SimpleApplicationEventMulticaster simpleApplicationEventMulticaster =

View File

@@ -14,9 +14,9 @@ import java.util.List;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.tenancy.cache.TenantAwareCacheManager;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
@@ -38,7 +38,6 @@ public class CleanupTestExecutionListener extends AbstractTestExecutionListener
SecurityContextSwitch.callAsPrivileged(() -> {
final ApplicationContext applicationContext = testContext.getApplicationContext();
clearTestRepository(
applicationContext.getBean(TenantAwareCacheManager.class),
applicationContext.getBean(SystemSecurityContext.class),
applicationContext.getBean(SystemManagement.class));
return null;
@@ -46,10 +45,8 @@ public class CleanupTestExecutionListener extends AbstractTestExecutionListener
}
private void clearTestRepository(
final TenantAwareCacheManager cacheManager,
final SystemSecurityContext systemSecurityContext,
final SystemManagement systemManagement) {
final List<String> tenants = systemSecurityContext.runAsSystem(() -> systemManagement.findTenants(PAGE).getContent());
tenants.forEach(tenant -> {
try {
@@ -58,6 +55,6 @@ public class CleanupTestExecutionListener extends AbstractTestExecutionListener
log.error("Error while delete tenant", e);
}
});
cacheManager.getDirectCacheNames().forEach(name -> cacheManager.getDirectCache(name).clear());
TenantAwareCacheManager.getInstance().evictTenant(null);
}
}

View File

@@ -20,9 +20,13 @@ spring.jpa.properties.eclipselink.logging.parameters=true
# Test utility properties for easier fault investigation - END
# Default properties for test that can be overridden during test run - START
# Enforce persistence of targetpolls for test predictability.
# Enforce persistence of target polls for test predictability.
hawkbit.server.repository.eagerPollPersistence=true
# disables Rollout and RoulloutGroup status cache for tests
hawkbit.cache.RolloutStatus.spec=maximumSize=0
hawkbit.cache.RolloutGroupStatus.spec=maximumSize=0
# Default properties for test that can be overridden during test run - END
# Properties that are managed by autoconfigure module at runtime and not available during test - START

View File

@@ -74,10 +74,6 @@
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<!-- Test -->
<dependency>