Move EventPublisher to hawkbit-repository-core making hawkbit-reposit… (#2576)

* Move EventPublisher to hawkbit-repository-core making hawkbit-repository-api non dependent to rabbit binders

* Move EventPublisherHolder to org.eclipse.hawkbit.repository.event

* Fix overriding default values of RemoteEvents and rabbit binder

---------

Co-authored-by: vasilchev <vasil.ilchev@bosch.com>
This commit is contained in:
Vasil Ilchev
2025-08-01 08:48:07 +03:00
committed by GitHub
parent 107ebca740
commit cca105c80e
8 changed files with 42 additions and 31 deletions

View File

@@ -50,7 +50,6 @@ hawkbit.lock=inMemory
# Disable discovery client of spring-cloud-commons # Disable discovery client of spring-cloud-commons
spring.cloud.discovery.enabled=false spring.cloud.discovery.enabled=false
# remote events configuration # remote events configuration
spring.config.import=classpath:/hawkbit-events-defaults.properties
# Optional: Use protostuff (if enabled) # Optional: Use protostuff (if enabled)
# spring.cloud.stream.default.content-type=application/binary+protostuff # spring.cloud.stream.default.content-type=application/binary+protostuff

View File

@@ -36,6 +36,5 @@ hawkbit.lock=inMemory
spring.cloud.discovery.enabled=false spring.cloud.discovery.enabled=false
# remote events configuration # remote events configuration
spring.config.import=classpath:/hawkbit-events-defaults.properties
# Optional: Use protostuff (if enabled) # Optional: Use protostuff (if enabled)
# spring.cloud.stream.default.content-type=application/binary+protostuff # spring.cloud.stream.default.content-type=application/binary+protostuff

View File

@@ -54,6 +54,5 @@ springdoc.swagger-ui.csrf.enabled=true
springdoc.swagger-ui.doc-expansion=none springdoc.swagger-ui.doc-expansion=none
# remote events configuration # remote events configuration
spring.config.import=classpath:/hawkbit-events-defaults.properties
# Optional: Use protostuff (if enabled) # Optional: Use protostuff (if enabled)
# spring.cloud.stream.default.content-type=application/binary+protostuff # spring.cloud.stream.default.content-type=application/binary+protostuff

View File

@@ -33,11 +33,6 @@
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
<dependency> <dependency>
<groupId>jakarta.validation</groupId> <groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId> <artifactId>jakarta.validation-api</artifactId>

View File

@@ -50,5 +50,9 @@
<artifactId>protostuff-runtime</artifactId> <artifactId>protostuff-runtime</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -9,35 +9,58 @@
*/ */
package org.eclipse.hawkbit.event; package org.eclipse.hawkbit.event;
import java.io.IOException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.function.Consumer; import java.util.function.Consumer;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.event.ApplicationEventFilter; import org.eclipse.hawkbit.repository.event.ApplicationEventFilter;
import org.eclipse.hawkbit.repository.event.EventPublisherHolder;
import org.eclipse.hawkbit.repository.event.remote.AbstractRemoteEvent; import org.eclipse.hawkbit.repository.event.remote.AbstractRemoteEvent;
import org.eclipse.hawkbit.repository.event.remote.RemoteTenantAwareEvent; import org.eclipse.hawkbit.repository.event.remote.RemoteTenantAwareEvent;
import org.eclipse.hawkbit.repository.event.EventPublisherHolder;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.stream.config.BindingProperties;
import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.event.ApplicationEventMulticaster; import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster; import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.support.ResourcePropertySource;
import org.springframework.messaging.converter.MessageConverter; import org.springframework.messaging.converter.MessageConverter;
/** /**
* Autoconfiguration for the events. * Autoconfiguration for the events.
*/ */
@Slf4j
@Configuration @Configuration
@PropertySource("classpath:/hawkbit-events-defaults.properties")
public class EventPublisherConfiguration { public class EventPublisherConfiguration {
private final ConfigurableEnvironment environment;
public EventPublisherConfiguration(ConfigurableEnvironment environment) {
this.environment = environment;
}
@PostConstruct
public void registerProperties() {
try {
ResourcePropertySource props = new ResourcePropertySource("classpath:/hawkbit-events-defaults.properties");
// load manually to ensure that they are with the lowest precedence allowing to override them
environment.getPropertySources().addLast(props);
} catch (IOException ex) {
log.error("Failed to load default properties for event publisher", ex);
}
}
/** /**
* Server internal event publisher that allows parallel event processing if the event listener is marked as so. * Server internal event publisher that allows parallel event processing if the event listener is marked as so.
* *
@@ -116,29 +139,22 @@ public class EventPublisherConfiguration {
return publisher::publishEvent; return publisher::publishEvent;
} }
@Bean
@ConditionalOnProperty(name = "org.eclipse.hawkbit.events.remote-enabled", havingValue = "true") @ConditionalOnProperty(name = "org.eclipse.hawkbit.events.remote-enabled", havingValue = "true")
@ConditionalOnProperty(name = "spring.cloud.stream.default.content-type", havingValue = "application/binary+stuff") public MessageConverter eventMessageConverter(BindingProperties bindingProperties) {
protected static class EventProtoStuffAutoConfiguration {
/** final String contentType = bindingProperties.getContentType();
* @return the protostuff io message converter for events
*/ if (contentType == null) {
@Bean throw new IllegalStateException("RemoteEvents are enabled and Content type must be specified in spring.cloud.stream.default.content-type.");
public MessageConverter eventProtoStuffConverter() {
return new EventProtoStuffMessageConverter();
} }
}
@ConditionalOnProperty(name = "org.eclipse.hawkbit.events.remote-enabled", havingValue = "true") if (contentType.equals("application/binary+protostuff")) {
@ConditionalOnProperty(name = "spring.cloud.stream.default.content-type", havingValue = "application/remote-event-json") return new EventProtoStuffMessageConverter();
protected static class EventJacksonAutoConfiguration { } else if (contentType.equals("application/remote-event-json")) {
/**
* @return the Jackson message converter for events
*/
@Bean
public MessageConverter eventJacksonMessageConverter() {
return new EventJacksonMessageConverter(); return new EventJacksonMessageConverter();
} else {
throw new IllegalStateException("Unsupported content type: " + contentType + ". Supported types: application/x-protostuff, application/remote-event-json");
} }
} }
} }

View File

@@ -16,8 +16,8 @@ import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.event.remote.AbstractRemoteEvent; import org.eclipse.hawkbit.repository.event.remote.AbstractRemoteEvent;
import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent; import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent;
import org.eclipse.hawkbit.repository.event.remote.MultiActionCancelEvent;
import org.eclipse.hawkbit.repository.event.remote.MultiActionAssignEvent; import org.eclipse.hawkbit.repository.event.remote.MultiActionAssignEvent;
import org.eclipse.hawkbit.repository.event.remote.MultiActionCancelEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;

View File

@@ -10,7 +10,6 @@
package org.eclipse.hawkbit.repository.jpa.model; package org.eclipse.hawkbit.repository.jpa.model;
import java.io.Serial; import java.io.Serial;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;