From 3d32d1d1c3e59b85a62c560a3c7621d5085c77b6 Mon Sep 17 00:00:00 2001 From: Kai Zimmermann Date: Tue, 26 Sep 2017 14:29:55 +0200 Subject: [PATCH] TargetPollEvent optional (#580) * TargetPollEvent can be disabled. Signed-off-by: kaizimmerm * Fixed test. Signed-off-by: kaizimmerm * Central filter introduced. Signed-off-by: kaizimmerm * Fix sonar issue. Signed-off-by: kaizimmerm * Add property to standard runtime. Signed-off-by: kaizimmerm * Add property to standard runtime. Signed-off-by: kaizimmerm --- .../EventPublisherAutoConfiguration.java | 24 +++++++- .../repository/RepositoryProperties.java | 18 +++++- .../event/ApplicationEventFilter.java | 28 ++++++++++ .../jpa/JpaControllerManagement.java | 4 -- .../RepositoryApplicationConfiguration.java | 9 +++ .../jpa/ControllerManagementTest.java | 10 ++++ .../repository/test/TestConfiguration.java | 56 +++++++++++++------ .../hawkbit-update-server/cf/manifest.yml | 5 +- .../src/main/resources/application.properties | 3 + 9 files changed, 132 insertions(+), 25 deletions(-) create mode 100644 hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/ApplicationEventFilter.java diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/repository/event/EventPublisherAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/repository/event/EventPublisherAutoConfiguration.java index cd228b81f..a76e5ff38 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/repository/event/EventPublisherAutoConfiguration.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/repository/event/EventPublisherAutoConfiguration.java @@ -11,12 +11,14 @@ package org.eclipse.hawkbit.autoconfigure.repository.event; import java.util.concurrent.Executor; import org.eclipse.hawkbit.event.BusProtoStuffMessageConverter; +import org.eclipse.hawkbit.repository.event.ApplicationEventFilter; 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.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.cloud.bus.ConditionalOnBusEnabled; import org.springframework.cloud.bus.ServiceMatcher; import org.springframework.cloud.bus.jackson.RemoteApplicationEventScan; @@ -58,7 +60,7 @@ public class EventPublisherAutoConfiguration { @Bean(name = AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME) public ApplicationEventMulticaster applicationEventMulticaster() { final SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new TenantAwareApplicationEventPublisher( - tenantAware); + tenantAware, applicationEventFilter()); simpleApplicationEventMulticaster.setTaskExecutor(executor); return simpleApplicationEventMulticaster; } @@ -74,10 +76,22 @@ public class EventPublisherAutoConfiguration { return EventPublisherHolder.getInstance(); } + /** + * @return default {@link ApplicationEventFilter} that does not filter any + * events + */ + @Bean + @ConditionalOnMissingBean + public ApplicationEventFilter applicationEventFilter() { + return e -> false; + } + private static class TenantAwareApplicationEventPublisher extends SimpleApplicationEventMulticaster { private final TenantAware tenantAware; + private final ApplicationEventFilter applicationEventFilter; + @Autowired(required = false) private ServiceMatcher serviceMatcher; @@ -87,8 +101,10 @@ public class EventPublisherAutoConfiguration { * @param tenantAware * the tenant ware */ - protected TenantAwareApplicationEventPublisher(final TenantAware tenantAware) { + protected TenantAwareApplicationEventPublisher(final TenantAware tenantAware, + final ApplicationEventFilter applicationEventFilter) { this.tenantAware = tenantAware; + this.applicationEventFilter = applicationEventFilter; } /** @@ -97,6 +113,10 @@ public class EventPublisherAutoConfiguration { */ @Override public void multicastEvent(final ApplicationEvent event, final ResolvableType eventType) { + if (applicationEventFilter.filter(event)) { + return; + } + if (serviceMatcher == null || !(event instanceof RemoteTenantAwareEvent)) { super.multicastEvent(event, eventType); return; diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RepositoryProperties.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RepositoryProperties.java index c60089fb3..4cbe068ef 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RepositoryProperties.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RepositoryProperties.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.repository; +import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent; import org.eclipse.hawkbit.repository.model.ActionStatus; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -24,7 +25,14 @@ public class RepositoryProperties { * is enforced you have to make sure that the feedback channel from the * devices i in order. */ - private boolean rejectActionStatusForClosedAction = false; + private boolean rejectActionStatusForClosedAction; + + /** + * Set to true if the repository should publish + * {@link TargetPollEvent}s in case a target connects to the repository. + * Activated by default but may be worth to disable if not needed. + */ + private boolean publishTargetPollEvent = true; public boolean isRejectActionStatusForClosedAction() { return rejectActionStatusForClosedAction; @@ -34,4 +42,12 @@ public class RepositoryProperties { this.rejectActionStatusForClosedAction = rejectActionStatusForClosedAction; } + public boolean isPublishTargetPollEvent() { + return publishTargetPollEvent; + } + + public void setPublishTargetPollEvent(final boolean publishTargetPollEvent) { + this.publishTargetPollEvent = publishTargetPollEvent; + } + } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/ApplicationEventFilter.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/ApplicationEventFilter.java new file mode 100644 index 000000000..1d1c964e3 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/ApplicationEventFilter.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.repository.event; + +import org.springframework.context.ApplicationEvent; + +/** + * ApplicationEventFilter for hawkBit internal {@link ApplicationEvent} + * publishing. + * + */ +@FunctionalInterface +public interface ApplicationEventFilter { + + /** + * + * @param event + * to verify + * @return true if event should be filtered + */ + boolean filter(final ApplicationEvent event); +} diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java index de0af20a9..702824861 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java @@ -48,7 +48,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.security.SystemSecurityContext; -import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -101,9 +100,6 @@ public class JpaControllerManagement implements ControllerManagement { @Autowired private TenantConfigurationManagement tenantConfigurationManagement; - @Autowired - private TenantAware tenantAware; - @Autowired private SystemSecurityContext systemSecurityContext; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java index e9017a310..b6684e39a 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java @@ -23,6 +23,7 @@ import org.eclipse.hawkbit.repository.DistributionSetTypeManagement; import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.PropertiesQuotaManagement; import org.eclipse.hawkbit.repository.RepositoryDefaultConfiguration; +import org.eclipse.hawkbit.repository.RepositoryProperties; import org.eclipse.hawkbit.repository.RolloutGroupManagement; import org.eclipse.hawkbit.repository.RolloutManagement; import org.eclipse.hawkbit.repository.RolloutStatusCache; @@ -39,8 +40,10 @@ import org.eclipse.hawkbit.repository.builder.DistributionSetTypeBuilder; import org.eclipse.hawkbit.repository.builder.RolloutBuilder; import org.eclipse.hawkbit.repository.builder.SoftwareModuleBuilder; import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder; +import org.eclipse.hawkbit.repository.event.ApplicationEventFilter; import org.eclipse.hawkbit.repository.event.remote.EventEntityManager; import org.eclipse.hawkbit.repository.event.remote.EventEntityManagerHolder; +import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent; import org.eclipse.hawkbit.repository.jpa.aspects.ExceptionMappingAspectHandler; import org.eclipse.hawkbit.repository.jpa.autoassign.AutoAssignChecker; import org.eclipse.hawkbit.repository.jpa.autoassign.AutoAssignScheduler; @@ -145,6 +148,12 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { return new RolloutStatusCache(tenantAware); } + @Bean + @ConditionalOnMissingBean + ApplicationEventFilter applicationEventFilter(final RepositoryProperties repositoryProperties) { + return e -> (e instanceof TargetPollEvent) && !repositoryProperties.isPublishTargetPollEvent(); + } + /** * @param distributionSetTypeManagement * to loading the {@link DistributionSetType} diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ControllerManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ControllerManagementTest.java index 425452ec2..fc2878e03 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ControllerManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ControllerManagementTest.java @@ -432,6 +432,16 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest { .as("register target with empty controllerId should fail"); } + @Test + @Description("Verify that controller registration does not result in a TargetPollEvent if feature is disabled") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), + @Expect(type = TargetPollEvent.class, count = 0) }) + public void targetPollEventNotSendIfDisabled() { + repositoryProperties.setPublishTargetPollEvent(false); + controllerManagement.findOrRegisterTargetIfItDoesNotexist("AA", LOCALHOST); + repositoryProperties.setPublishTargetPollEvent(true); + } + @Test @Description("Controller trys to finish an update process after it has been finished by an error action status.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/TestConfiguration.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/TestConfiguration.java index 03f535661..853e6c62c 100644 --- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/TestConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/TestConfiguration.java @@ -23,6 +23,7 @@ import org.eclipse.hawkbit.cache.DownloadIdCache; import org.eclipse.hawkbit.cache.TenantAwareCacheManager; import org.eclipse.hawkbit.event.BusProtoStuffMessageConverter; import org.eclipse.hawkbit.repository.RolloutStatusCache; +import org.eclipse.hawkbit.repository.event.ApplicationEventFilter; import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder; import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer; import org.eclipse.hawkbit.repository.rsql.VirtualPropertyResolver; @@ -44,6 +45,7 @@ import org.springframework.cache.guava.GuavaCacheManager; import org.springframework.cloud.bus.ConditionalOnBusEnabled; import org.springframework.cloud.bus.ServiceMatcher; import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationEvent; import org.springframework.context.annotation.AdviceMode; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -51,6 +53,7 @@ import org.springframework.context.annotation.Profile; import org.springframework.context.annotation.PropertySource; import org.springframework.context.event.SimpleApplicationEventMulticaster; import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.core.ResolvableType; import org.springframework.data.domain.AuditorAware; import org.springframework.integration.support.locks.DefaultLockRegistry; import org.springframework.integration.support.locks.LockRegistry; @@ -82,43 +85,43 @@ public class TestConfiguration implements AsyncConfigurer { } @Bean - public LockRegistry lockRegistry() { + LockRegistry lockRegistry() { return new DefaultLockRegistry(); } @Bean - public SecurityTokenGenerator securityTokenGenerator() { + SecurityTokenGenerator securityTokenGenerator() { return new SecurityTokenGenerator(); } @Bean - public SystemSecurityContext systemSecurityContext(final TenantAware tenantAware) { + SystemSecurityContext systemSecurityContext(final TenantAware tenantAware) { return new SystemSecurityContext(tenantAware); } @Bean - public ArtifactRepository artifactRepository(final ArtifactFilesystemProperties artifactFilesystemProperties) { + ArtifactRepository artifactRepository(final ArtifactFilesystemProperties artifactFilesystemProperties) { return new ArtifactFilesystemRepository(artifactFilesystemProperties); } @Bean - public TestdataFactory testdataFactory() { + TestdataFactory testdataFactory() { return new TestdataFactory(); } @Bean - public PropertyBasedArtifactUrlHandler testPropertyBasedArtifactUrlHandler( + PropertyBasedArtifactUrlHandler testPropertyBasedArtifactUrlHandler( final ArtifactUrlHandlerProperties urlHandlerProperties) { return new PropertyBasedArtifactUrlHandler(urlHandlerProperties); } @Bean - public TenantAware tenantAware() { + TenantAware tenantAware() { return new SecurityContextTenantAware(); } @Bean - public TenantAwareCacheManager cacheManager() { + TenantAwareCacheManager cacheManager() { return new TenantAwareCacheManager(new GuavaCacheManager(), tenantAware()); } @@ -128,29 +131,48 @@ public class TestConfiguration implements AsyncConfigurer { * @return the cache */ @Bean - public DownloadIdCache downloadIdCache() { + DownloadIdCache downloadIdCache() { return new DefaultDownloadIdCache(cacheManager()); } @Bean(name = AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME) - public SimpleApplicationEventMulticaster applicationEventMulticaster() { - final SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster(); + SimpleApplicationEventMulticaster applicationEventMulticaster(final ApplicationEventFilter applicationEventFilter) { + final SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new FilterEnabledApplicationEventPublisher( + applicationEventFilter); simpleApplicationEventMulticaster.setTaskExecutor(asyncExecutor()); return simpleApplicationEventMulticaster; } + private static class FilterEnabledApplicationEventPublisher extends SimpleApplicationEventMulticaster { + + private final ApplicationEventFilter applicationEventFilter; + + FilterEnabledApplicationEventPublisher(final ApplicationEventFilter applicationEventFilter) { + this.applicationEventFilter = applicationEventFilter; + } + + @Override + public void multicastEvent(final ApplicationEvent event, final ResolvableType eventType) { + if (applicationEventFilter.filter(event)) { + return; + } + + super.multicastEvent(event, eventType); + } + } + @Bean - public EventPublisherHolder eventBusHolder() { + EventPublisherHolder eventBusHolder() { return EventPublisherHolder.getInstance(); } @Bean - public Executor asyncExecutor() { + Executor asyncExecutor() { return new DelegatingSecurityContextExecutorService(Executors.newSingleThreadExecutor()); } @Bean - public AuditorAware auditorAware() { + AuditorAware auditorAware() { return new SpringSecurityAuditorAware(); } @@ -169,13 +191,13 @@ public class TestConfiguration implements AsyncConfigurer { * @return returns a VirtualPropertyReplacer */ @Bean - public VirtualPropertyReplacer virtualPropertyReplacer() { + VirtualPropertyReplacer virtualPropertyReplacer() { return new VirtualPropertyResolver(); } @Bean @ConditionalOnMissingBean - public ServiceMatcher serviceMatcher(final ApplicationContext applicationContext) { + ServiceMatcher serviceMatcher(final ApplicationContext applicationContext) { final ServiceMatcher serviceMatcher = new ServiceMatcher(); serviceMatcher.setMatcher(new AntPathMatcher(":")); serviceMatcher.setApplicationContext(applicationContext); @@ -188,7 +210,7 @@ public class TestConfiguration implements AsyncConfigurer { */ @Bean @ConditionalOnBusEnabled - public MessageConverter busProtoBufConverter() { + MessageConverter busProtoBufConverter() { return new BusProtoStuffMessageConverter(); } } diff --git a/hawkbit-runtime/hawkbit-update-server/cf/manifest.yml b/hawkbit-runtime/hawkbit-update-server/cf/manifest.yml index a1401afbc..3bbfaf71e 100644 --- a/hawkbit-runtime/hawkbit-update-server/cf/manifest.yml +++ b/hawkbit-runtime/hawkbit-update-server/cf/manifest.yml @@ -10,7 +10,8 @@ --- applications: - name: hawkbit - memory: 1024M + memory: 2048M + disk_quota: 2048M instances: 1 buildpack: https://github.com/cloudfoundry/java-buildpack path: @project.build.finalName@.jar @@ -20,3 +21,5 @@ applications: SPRING_PROFILES_ACTIVE: cloudsandbox,amqp CF_STAGING_TIMEOUT: 15 CF_STARTUP_TIMEOUT: 15 + JBP_CONFIG_JAVA_MAIN: '{ arguments: "-XX:+UseG1GC -XX:+UseStringDeduplication -XX:+UseCompressedOops -XX:+ExitOnOutOfMemoryError" }' + timeout: 180 diff --git a/hawkbit-runtime/hawkbit-update-server/src/main/resources/application.properties b/hawkbit-runtime/hawkbit-update-server/src/main/resources/application.properties index c0c129c82..63eb680f2 100644 --- a/hawkbit-runtime/hawkbit-update-server/src/main/resources/application.properties +++ b/hawkbit-runtime/hawkbit-update-server/src/main/resources/application.properties @@ -16,6 +16,9 @@ hawkbit.server.ddi.security.authentication.anonymous.enabled=true hawkbit.server.ddi.security.authentication.targettoken.enabled=true hawkbit.server.ddi.security.authentication.gatewaytoken.enabled=true +# Optional events +hawkbit.server.repository.publish-target-poll-event=false + ## Configuration for DMF/RabbitMQ integration spring.profiles.active=amqp spring.rabbitmq.username=guest