Remote Events migrated from Spring Bus to Spring Cloud Stream (#2563)

* Remote Events migrated from Spring Bus to Spring Cloud Stream

---------

Co-authored-by: vasilchev <vasil.ilchev@bosch.com>
This commit is contained in:
Vasil Ilchev
2025-07-30 16:58:00 +03:00
committed by GitHub
parent 10da0288d9
commit 4a8e60764f
49 changed files with 1147 additions and 461 deletions

View File

@@ -24,7 +24,6 @@ import org.eclipse.hawkbit.repository.artifact.ArtifactRepository;
import org.eclipse.hawkbit.repository.artifact.urlhandler.ArtifactUrlHandlerProperties;
import org.eclipse.hawkbit.repository.artifact.urlhandler.PropertyBasedArtifactUrlHandler;
import org.eclipse.hawkbit.cache.TenantAwareCacheManager;
import org.eclipse.hawkbit.event.BusProtoStuffMessageConverter;
import org.eclipse.hawkbit.repository.RolloutApprovalStrategy;
import org.eclipse.hawkbit.repository.RolloutStatusCache;
import org.eclipse.hawkbit.repository.event.ApplicationEventFilter;
@@ -51,7 +50,6 @@ import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.cloud.bus.ConditionalOnBusEnabled;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -63,7 +61,6 @@ 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;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;
@@ -181,7 +178,7 @@ public class TestConfiguration implements AsyncConfigurer {
}
@Bean
EventPublisherHolder eventBusHolder() {
EventPublisherHolder eventPublisherHolder() {
return EventPublisherHolder.getInstance();
}
@@ -208,15 +205,6 @@ public class TestConfiguration implements AsyncConfigurer {
return new RolloutTestApprovalStrategy();
}
/**
* @return the protostuff io message converter
*/
@Bean
@ConditionalOnBusEnabled
MessageConverter busProtoBufConverter() {
return new BusProtoStuffMessageConverter();
}
private static class FilterEnabledApplicationEventPublisher extends SimpleApplicationEventMulticaster {
private final ApplicationEventFilter applicationEventFilter;

View File

@@ -15,8 +15,12 @@ import static org.hamcrest.Matchers.equalTo;
import static org.junit.jupiter.api.Assertions.fail;
import java.io.Serial;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -27,10 +31,23 @@ import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j;
import org.awaitility.Awaitility;
import org.awaitility.core.ConditionTimeoutException;
import org.eclipse.hawkbit.repository.event.remote.AbstractRemoteEvent;
import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent;
import org.eclipse.hawkbit.repository.event.remote.MultiActionAssignEvent;
import org.eclipse.hawkbit.repository.event.remote.MultiActionCancelEvent;
import org.eclipse.hawkbit.repository.event.remote.service.CancelTargetAssignmentServiceEvent;
import org.eclipse.hawkbit.repository.event.remote.service.MultiActionAssignServiceEvent;
import org.eclipse.hawkbit.repository.event.remote.service.MultiActionCancelServiceEvent;
import org.eclipse.hawkbit.repository.event.remote.service.TargetAssignDistributionSetServiceEvent;
import org.eclipse.hawkbit.repository.event.remote.service.TargetAttributesRequestedServiceEvent;
import org.eclipse.hawkbit.repository.event.remote.service.TargetCreatedServiceEvent;
import org.eclipse.hawkbit.repository.event.remote.service.TargetDeletedServiceEvent;
import org.eclipse.hawkbit.repository.event.remote.RemoteIdEvent;
import org.eclipse.hawkbit.repository.event.remote.RemoteTenantAwareEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.springframework.cloud.bus.event.RemoteApplicationEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
@@ -86,7 +103,50 @@ public class EventVerifier extends AbstractTestExecutionListener {
}
private Optional<Expect[]> getExpectationsFrom(final Method testMethod) {
return Optional.ofNullable(testMethod.getAnnotation(ExpectEvents.class)).map(ExpectEvents::value);
final Optional<Expect[]> expectedEvents = Optional.ofNullable(testMethod.getAnnotation(ExpectEvents.class)).map(ExpectEvents::value);
if (expectedEvents.isPresent()) {
List<Expect> modifiedEvents = new ArrayList<>(Arrays.asList(expectedEvents.get()));
for (Expect event : expectedEvents.get()) {
final Class<?> type = event.type();
if (type.isAssignableFrom(TargetCreatedEvent.class)) {
modifiedEvents.add(toExpectServiceEvent(TargetCreatedServiceEvent.class, event.count()));
} else if (type.isAssignableFrom(TargetDeletedEvent.class)) {
modifiedEvents.add(toExpectServiceEvent(TargetDeletedServiceEvent.class, event.count()));
} else if (type.isAssignableFrom(TargetAssignDistributionSetEvent.class)) {
modifiedEvents.add(toExpectServiceEvent(TargetAssignDistributionSetServiceEvent.class, event.count()));
} else if (type.isAssignableFrom(MultiActionAssignEvent.class)) {
modifiedEvents.add(toExpectServiceEvent(MultiActionAssignServiceEvent.class, event.count()));
} else if (type.isAssignableFrom(MultiActionCancelEvent.class)) {
modifiedEvents.add(toExpectServiceEvent(MultiActionCancelServiceEvent.class, event.count()));
} else if (type.isAssignableFrom(TargetAttributesRequestedEvent.class)) {
modifiedEvents.add(toExpectServiceEvent(TargetAttributesRequestedServiceEvent.class, event.count()));
} else if (type.isAssignableFrom(CancelTargetAssignmentEvent.class)) {
modifiedEvents.add(toExpectServiceEvent(CancelTargetAssignmentServiceEvent.class, event.count()));
}
}
return Optional.of(modifiedEvents.toArray(new Expect[0]));
}
return Optional.empty();
}
private static Expect toExpectServiceEvent(final Class<?> clazz, final int count) {
return new Expect() {
@Override
public Class<? extends Annotation> annotationType() {
return Expect.class;
}
@Override
public Class<?> type() {
return clazz;
}
@Override
public int count() {
return count;
}
};
}
private void beforeTest(final TestContext testContext) {
@@ -129,12 +189,12 @@ public class EventVerifier extends AbstractTestExecutionListener {
testContext.getApplicationContext().getBean(ApplicationEventMulticaster.class).removeApplicationListener(eventCaptor);
}
private static class EventCaptor implements ApplicationListener<RemoteApplicationEvent> {
private static class EventCaptor implements ApplicationListener<AbstractRemoteEvent> {
private final ConcurrentHashMap<Class<?>, Integer> capturedEvents = new ConcurrentHashMap<>();
@Override
public void onApplicationEvent(final RemoteApplicationEvent event) {
public void onApplicationEvent(final AbstractRemoteEvent event) {
log.debug("Received event {}", event.getClass().getSimpleName());
if (ResetCounterMarkerEvent.class.isAssignableFrom(event.getClass())) {
@@ -170,13 +230,13 @@ public class EventVerifier extends AbstractTestExecutionListener {
}
}
private static final class ResetCounterMarkerEvent extends RemoteApplicationEvent {
private static final class ResetCounterMarkerEvent extends AbstractRemoteEvent {
@Serial
private static final long serialVersionUID = 1L;
private ResetCounterMarkerEvent() {
super(new Object(), "resetcounter", DEFAULT_DESTINATION_FACTORY.getDestination(null));
super("event-verifier");
}
}
}

View File

@@ -30,6 +30,8 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.awaitility.Awaitility;
import org.awaitility.core.ConditionFactory;
import org.eclipse.hawkbit.repository.artifact.ArtifactRepository;
import org.eclipse.hawkbit.repository.artifact.exception.ArtifactStoreException;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.ConfirmationManagement;
import org.eclipse.hawkbit.repository.ControllerManagement;
@@ -52,8 +54,6 @@ import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TargetTagManagement;
import org.eclipse.hawkbit.repository.TargetTypeManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.artifact.ArtifactRepository;
import org.eclipse.hawkbit.repository.artifact.exception.ArtifactStoreException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
@@ -78,7 +78,6 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.bus.ServiceMatcher;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Import;
import org.springframework.data.domain.PageRequest;
@@ -181,8 +180,6 @@ public abstract class AbstractIntegrationTest {
@Autowired
protected TestdataFactory testdataFactory;
@Autowired(required = false)
protected ServiceMatcher serviceMatcher;
@Autowired
protected ApplicationEventPublisher eventPublisher;
private static final String ARTIFACT_DIRECTORY = createTempDir().getAbsolutePath() + "/" + randomString(20);

View File

@@ -78,7 +78,4 @@ hawkbit.server.security.dos.maxTargetsPerAutoAssignment=20
hawkbit.server.security.dos.maxActionsPerTarget=20
# Quota - END
# Properties that are managed by autoconfigure module at runtime and not available during test - END
# disable spring cloud bus for tests
spring.cloud.bus.enabled=false
# Properties that are managed by autoconfigure module at runtime and not available during test - END