Feature horizontal scalability (#305)

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
Dennis Melzer
2016-11-03 15:53:53 +01:00
committed by Kai Zimmermann
parent 07cb62a3dd
commit 866bc72114
287 changed files with 4219 additions and 5046 deletions

View File

@@ -8,17 +8,21 @@
*/
package org.eclipse.hawkbit.autoconfigure.cache;
import static com.google.common.cache.CacheBuilder.newBuilder;
import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.cache.TenancyCacheManager;
import org.eclipse.hawkbit.cache.TenantAwareCacheManager;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
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.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.guava.GuavaCacheManager;
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
@@ -27,6 +31,8 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.google.common.cache.CacheBuilder;
/**
* A configuration for configuring the spring {@link CacheManager} for specific
* multi-tenancy caching. The caches between tenants must not interfere each
@@ -37,33 +43,58 @@ import org.springframework.context.annotation.Primary;
*/
@Configuration
@EnableCaching
public class CacheAutoConfiguration extends CachingConfigurerSupport {
public class CacheAutoConfiguration {
@Autowired
private TenantAware tenantAware;
@Autowired
@Qualifier("directCacheManager")
private CacheManager directCacheManager;
/**
* @return the default cache manager bean if none other cache manager is
* existing.
*/
@Override
@Bean
@ConditionalOnMissingBean
@Primary
public TenancyCacheManager cacheManager() {
return new TenantAwareCacheManager(directCacheManager(), tenantAware);
return new TenantAwareCacheManager(directCacheManager, tenantAware);
}
/**
* @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
* A separate configuration of the direct cache manager for the
* {@link TenantAwareCacheManager} that it can get overridden by another
* configuration.
*/
@Bean(name = "directCacheManager")
@ConditionalOnMissingBean(name = "directCacheManager")
public CacheManager directCacheManager() {
return new GuavaCacheManager();
@Configuration
@EnableConfigurationProperties(CacheProperties.class)
static class DirectCacheManagerConfiguration {
@Autowired
private CacheProperties cacheProperties;
/**
* @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 GuavaCacheManager cacheManager = new GuavaCacheManager();
if (cacheProperties.getTtl() > 0) {
final CacheBuilder<Object, Object> cacheBuilder = newBuilder()
.expireAfterWrite(cacheProperties.getTtl(), cacheProperties.getTtlUnit());
cacheManager.setCacheBuilder(cacheBuilder);
}
return cacheManager;
}
}
/**
@@ -198,6 +229,11 @@ public class CacheAutoConfiguration extends CachingConfigurerSupport {
delegate.clear();
}
@Override
public <T> T get(final Object key, final Callable<T> valueLoader) {
return delegate.get(key, valueLoader);
}
}
}

View File

@@ -0,0 +1,54 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.cache;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import java.util.concurrent.TimeUnit;
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.
*/
@ConfigurationProperties("hawkbit.cache.global")
public class CacheProperties {
/**
* TTL for cached entries in millis.
*/
private int ttl;
/**
* Initial delay in millis
*/
private int initialDelay;
public long getInitialDelay() {
return initialDelay;
}
public void setInitialDelay(final int initialDelay) {
this.initialDelay = initialDelay;
}
public int getTtl() {
return ttl;
}
public void setTtl(final int ttl) {
this.ttl = ttl;
}
public final TimeUnit getTtlUnit() {
return MILLISECONDS;
}
}

View File

@@ -8,10 +8,10 @@
*/
package org.eclipse.hawkbit.autoconfigure.cache;
import org.eclipse.hawkbit.cache.CacheConstants;
import org.eclipse.hawkbit.cache.TenancyCacheManager;
import org.eclipse.hawkbit.cache.DefaultDownloadIdCache;
import org.eclipse.hawkbit.cache.DownloadIdCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -20,9 +20,6 @@ import org.springframework.context.annotation.Configuration;
* A configuration for configuring a cache for the download id's.
*
* This is done by providing a named cache.
*
*
*
*/
@Configuration
public class DownloadIdCacheAutoConfiguration {
@@ -31,16 +28,18 @@ public class DownloadIdCacheAutoConfiguration {
private CacheManager cacheManager;
/**
* Bean for the download id cache.
* Bean for the downloadId cache that returns the DefaultDownloadIdCache.
* The DefaultDownloadIdCache cannot be used within a cluster because the
* downloadId cache is not shared among notes. This means, a downloadId
* which is stored on note A for downloading an artifact can only be used
* for downloading the artifact form node A.
*
* @return the cache
* @return the DefaultDownloadIdCache
*/
@Bean(name = CacheConstants.DOWNLOAD_ID_CACHE)
public Cache downloadIdCache() {
if (cacheManager instanceof TenancyCacheManager) {
return ((TenancyCacheManager) cacheManager).getDirectCache(CacheConstants.DOWNLOAD_ID_CACHE);
}
return cacheManager.getCache(CacheConstants.DOWNLOAD_ID_CACHE);
@Bean
@ConditionalOnMissingBean
public DownloadIdCache downloadIdCache() {
return new DefaultDownloadIdCache(cacheManager);
}
}

View File

@@ -1,27 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.cache;
import org.eclipse.hawkbit.cache.RedisConfiguration;
import org.eclipse.hawkbit.cache.annotation.EnableRedis;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
/**
* A configuration for configuring the redis configuration.
*
*
*
*/
@Configuration
@ConditionalOnClass(value = RedisConfiguration.class)
@EnableRedis
public class RedisAutoConfiguration {
}

View File

@@ -0,0 +1,131 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.event;
import java.util.concurrent.Executor;
import org.eclipse.hawkbit.event.BusProtoStuffMessageConverter;
import org.eclipse.hawkbit.repository.event.remote.RemoteTenantAwareEvent;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.bus.ConditionalOnBusEnabled;
import org.springframework.cloud.bus.ServiceMatcher;
import org.springframework.cloud.bus.jackson.RemoteApplicationEventScan;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.messaging.converter.MessageConverter;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
/**
* Auto configuration for the event bus.
*
*/
@Configuration
@RemoteApplicationEventScan(basePackages = "org.eclipse.hawkbit.repository.event.remote")
public class EventPublisherAutoConfiguration {
@Autowired
@Qualifier("asyncExecutor")
private Executor executor;
@Autowired
private TenantAware tenantAware;
/**
* Server internal event publisher that allows parallel event processing if
* the event listener is marked as so.
*
* @return publisher bean
*/
@Bean(name = AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)
public ApplicationEventMulticaster applicationEventMulticaster() {
final SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new TenantAwareApplicationEventPublisher(
tenantAware);
simpleApplicationEventMulticaster.setTaskExecutor(executor);
return simpleApplicationEventMulticaster;
}
/**
* Bean for creating a singleton instance of the
* {@link EventPublisherHolder}
*
* @return the singleton instance of the {@link EventPublisherHolder}
*/
@Bean
public EventPublisherHolder eventBusHolder() {
return EventPublisherHolder.getInstance();
}
private static class TenantAwareApplicationEventPublisher extends SimpleApplicationEventMulticaster {
private final TenantAware tenantAware;
@Autowired(required = false)
private ServiceMatcher serviceMatcher;
/**
* Constructor.
*
* @param tenantAware
* the tenant ware
*/
protected TenantAwareApplicationEventPublisher(final TenantAware tenantAware) {
this.tenantAware = tenantAware;
}
/**
* Was overridden that not every event has to run within a own
* tenantAware.
*/
@Override
public void multicastEvent(final ApplicationEvent event, final ResolvableType eventType) {
if (serviceMatcher == null || !(event instanceof RemoteTenantAwareEvent)) {
super.multicastEvent(event, eventType);
return;
}
final RemoteTenantAwareEvent remoteEvent = (RemoteTenantAwareEvent) event;
if (serviceMatcher.isFromSelf(remoteEvent)) {
super.multicastEvent(event, eventType);
return;
}
tenantAware.runAsTenant(remoteEvent.getTenant(), () -> {
super.multicastEvent(event, eventType);
return null;
});
}
}
@ConditionalOnBusEnabled
@ConditionalOnClass({ Schema.class, ProtostuffIOUtil.class })
protected static class BusProtoStuffAutoConfiguration {
/**
*
* @return the protostuff io message converter
*/
@Bean
public MessageConverter busProtoBufConverter() {
return new BusProtoStuffMessageConverter();
}
}
}

View File

@@ -1,66 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.eventbus;
import java.util.concurrent.Executor;
import org.eclipse.hawkbit.eventbus.EventBusSubscriberProcessor;
import org.eclipse.hawkbit.eventbus.EventSubscriber;
import org.eclipse.hawkbit.repository.jpa.model.helper.EventBusHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;
/**
* Auto configuration for the event bus.
*
*/
@Configuration
public class EventBusAutoConfiguration {
@Autowired
@Qualifier("asyncExecutor")
private Executor executor;
/**
* Server internal eventBus that allows parallel event processing if the
* subscriber is marked as so.
*
* @return eventbus bean
*/
@Bean
@ConditionalOnMissingBean
public EventBus eventBus() {
return new AsyncEventBus(executor);
}
/**
* @return the {@link EventBusSubscriberProcessor} to find classes annotated
* with {@link EventSubscriber}.
*/
@Bean
@ConditionalOnMissingBean
public EventBusSubscriberProcessor eventBusSubscriberProcessor() {
return new EventBusSubscriberProcessor();
}
/**
* @return the singleton instance of the {@link EventBusHolder}
*/
@Bean
public EventBusHolder eventBusHolder() {
return EventBusHolder.getInstance();
}
}

View File

@@ -10,7 +10,7 @@ package org.eclipse.hawkbit.autoconfigure.repository;
import org.eclipse.hawkbit.EnableJpaRepository;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.repository.jpa.rsql.VirtualPropertyResolver;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyResolver;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;

View File

@@ -23,7 +23,7 @@ import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.eclipse.hawkbit.ExcludePathAwareShallowETagFilter;
import org.eclipse.hawkbit.cache.CacheConstants;
import org.eclipse.hawkbit.cache.DownloadIdCache;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.im.authentication.TenantUserPasswordAuthenticationToken;
@@ -47,12 +47,10 @@ import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.boot.context.embedded.ServletListenerRegistrationBean;
import org.springframework.cache.Cache;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
@@ -436,8 +434,7 @@ public class SecurityManagedConfiguration {
private DdiSecurityProperties ddiSecurityConfiguration;
@Autowired
@Qualifier(CacheConstants.DOWNLOAD_ID_CACHE)
private Cache downloadIdCache;
private DownloadIdCache downloadIdCache;
@Override
protected void configure(final HttpSecurity http) throws Exception {

View File

@@ -9,15 +9,20 @@
package org.eclipse.hawkbit.autoconfigure.ui;
import org.eclipse.hawkbit.DistributedResourceBundleMessageSource;
import org.eclipse.hawkbit.ui.push.DelayedEventBusPushStrategy;
import org.eclipse.hawkbit.ui.push.EventPushStrategy;
import org.eclipse.hawkbit.ui.push.HawkbitEventProvider;
import org.eclipse.hawkbit.ui.push.UIEventProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.vaadin.spring.annotation.EnableVaadinExtensions;
import org.vaadin.spring.events.annotation.EnableEventBus;
import org.vaadin.spring.security.annotation.EnableVaadinSecurity;
import com.vaadin.spring.annotation.UIScope;
/**
* The hawkbit-ui autoconfiguration.
*/
@@ -48,4 +53,22 @@ public class UIAutoConfiguration {
return new HawkbitEventProvider();
}
/**
* The UI scoped event push strategy. Session scope is necessary, that every
* UI has an own strategy.
*
* @param applicationContext
* the context to add the listener
*
* @return the provider bean
*/
@Bean
@ConditionalOnMissingBean
@UIScope
public EventPushStrategy eventPushStrategy(final ConfigurableApplicationContext applicationContext) {
final DelayedEventBusPushStrategy delayedEventBusPushStrategy = new DelayedEventBusPushStrategy();
applicationContext.addApplicationListener(delayedEventBusPushStrategy);
return delayedEventBusPushStrategy;
}
}

View File

@@ -24,28 +24,12 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
@Configuration
public class WebMvcAutoConfiguration extends WebMvcConfigurerAdapter {
/*
* (non-Javadoc)
*
* @see
* org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
* #configurePathMatch
* (org.springframework.web.servlet.config.annotation.PathMatchConfigurer)
*/
@Override
public void configurePathMatch(final PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false);
configurer.setUseRegisteredSuffixPatternMatch(false);
}
/*
* (non-Javadoc)
*
* @see org.springframework.web.servlet.config.annotation.
* WebMvcConfigurerAdapter# configureContentNegotiation
* (org.springframework.web.servlet.config.annotation.
* ContentNegotiationConfigurer)
*/
@Override
public void configureContentNegotiation(final ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false);

View File

@@ -7,9 +7,8 @@ org.eclipse.hawkbit.autoconfigure.url.PropertyHostnameResolverAutoConfiguration,
org.eclipse.hawkbit.autoconfigure.web.WebMvcAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.cache.CacheAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.cache.DownloadIdCacheAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.eventbus.EventBusAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.event.EventPublisherAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.scheduling.AsyncConfigurerAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.cache.RedisAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.scheduling.ExecutorAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.amqp.AmqpAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.security.InMemoryUserManagementConfiguration

View File

@@ -14,6 +14,19 @@ security.basic.realm=HawkBit
security.user.name=admin
security.user.password=admin
# Spring cloud bus and stream
spring.cloud.bus.enabled=false
# Disable Cloud Bus default events
spring.cloud.bus.env.enabled=false
spring.cloud.bus.ack.enabled=false
spring.cloud.bus.trace.enabled=false
spring.cloud.bus.refresh.enabled=false
# Disable Cloud Bus endpoints
endpoints.spring.cloud.bus.refresh.enabled=false
endpoints.spring.cloud.bus.env.enabled=false
# Spring cloud bus and stream END
### JPA / Datasource - START
spring.jpa.database=H2
spring.jpa.show-sql=false