diff --git a/.github/workflows/reusable_workflow_verify.yaml b/.github/workflows/reusable_workflow_verify.yaml
index 6b454fa66..4c4d0cef1 100644
--- a/.github/workflows/reusable_workflow_verify.yaml
+++ b/.github/workflows/reusable_workflow_verify.yaml
@@ -32,6 +32,12 @@ jobs:
- 5672:5672
steps:
+ - name: Parameters
+ run: |
+ echo "Repository: ${{ inputs.repository }},"
+ echo "Ref: ${{ inputs.ref }},"
+ echo "Maven Properties: ${{ inputs.maven_properties }}"
+
- uses: actions/checkout@v4
with:
repository: ${{ inputs.repository }}
diff --git a/.github/workflows/verify-hibernate.yaml b/.github/workflows/verify-hibernate.yaml
index 107ba1c54..200a1da86 100644
--- a/.github/workflows/verify-hibernate.yaml
+++ b/.github/workflows/verify-hibernate.yaml
@@ -22,6 +22,6 @@ jobs:
verify-hibernate:
uses: ./.github/workflows/reusable_workflow_verify.yaml
with:
- repository: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repository || github.repositor }}
+ repository: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name || github.repository }}
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || github.ref }}
maven_properties: '-Djpa.vendor=hibernate -Dlogging.level.org.hibernate.collection.spi.AbstractPersistentCollection=ERROR'
\ No newline at end of file
diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml
index abdaa56a6..6fa5cfca2 100644
--- a/.github/workflows/verify.yaml
+++ b/.github/workflows/verify.yaml
@@ -22,5 +22,5 @@ jobs:
verify:
uses: ./.github/workflows/reusable_workflow_verify.yaml
with:
- repository: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repository || github.repositor }}
+ repository: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name || github.repository }}
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || github.ref }}
\ No newline at end of file
diff --git a/hawkbit-ddi/hawkbit-ddi-server/src/main/resources/application.properties b/hawkbit-ddi/hawkbit-ddi-server/src/main/resources/application.properties
index bb50ac518..a26f83e3f 100644
--- a/hawkbit-ddi/hawkbit-ddi-server/src/main/resources/application.properties
+++ b/hawkbit-ddi/hawkbit-ddi-server/src/main/resources/application.properties
@@ -38,13 +38,6 @@ hawkbit.server.ddi.security.authentication.gatewaytoken.enabled=false
# Optional events
hawkbit.server.repository.publish-target-poll-event=false
-## Configuration for DMF/RabbitMQ integration
-spring.rabbitmq.username=guest
-spring.rabbitmq.password=guest
-spring.rabbitmq.virtual-host=/
-spring.rabbitmq.host=localhost
-spring.rabbitmq.port=5672
-
# Enable CORS and specify the allowed origins:
#hawkbit.server.security.cors.enabled=true
#hawkbit.server.security.cors.allowedOrigins=http://localhost
@@ -57,11 +50,7 @@ hawkbit.lock=inMemory
# Disable discovery client of spring-cloud-commons
spring.cloud.discovery.enabled=false
-# Enable communication between services
-spring.cloud.bus.enabled=true
-spring.cloud.bus.ack.enabled=false
-spring.cloud.bus.refresh.enabled=false
-spring.cloud.bus.env.enabled=false
+# Configure communication between services
endpoints.spring.cloud.bus.refresh.enabled=false
endpoints.spring.cloud.bus.env.enabled=false
spring.cloud.stream.bindings.springCloudBusInput.group=ddi-server
diff --git a/hawkbit-ddi/hawkbit-ddi-server/src/test/java/org/eclipse/hawkbit/app/ddi/AbstractSecurityTest.java b/hawkbit-ddi/hawkbit-ddi-server/src/test/java/org/eclipse/hawkbit/app/ddi/AbstractSecurityTest.java
index fb25b9592..2553cf36c 100644
--- a/hawkbit-ddi/hawkbit-ddi-server/src/test/java/org/eclipse/hawkbit/app/ddi/AbstractSecurityTest.java
+++ b/hawkbit-ddi/hawkbit-ddi-server/src/test/java/org/eclipse/hawkbit/app/ddi/AbstractSecurityTest.java
@@ -19,9 +19,9 @@ import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
-@SpringBootTest(properties = { "hawkbit.dmf.rabbitmq.enabled=false" })
+@SpringBootTest
@ExtendWith(SharedSqlTestDatabaseExtension.class)
-public abstract class AbstractSecurityTest {
+abstract class AbstractSecurityTest {
protected MockMvc mvc;
@Autowired
diff --git a/hawkbit-ddi/hawkbit-ddi-server/src/test/java/org/eclipse/hawkbit/app/ddi/AllowedHostNamesTest.java b/hawkbit-ddi/hawkbit-ddi-server/src/test/java/org/eclipse/hawkbit/app/ddi/AllowedHostNamesTest.java
index 7a93f7e66..fafe4155c 100644
--- a/hawkbit-ddi/hawkbit-ddi-server/src/test/java/org/eclipse/hawkbit/app/ddi/AllowedHostNamesTest.java
+++ b/hawkbit-ddi/hawkbit-ddi-server/src/test/java/org/eclipse/hawkbit/app/ddi/AllowedHostNamesTest.java
@@ -16,14 +16,14 @@ import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.test.context.TestPropertySource;
-@TestPropertySource(properties = {
- "spring.flyway.enabled=true", // if hibernate is used there could be db inconsistencies when executing tests with and without flyway
- "hawkbit.server.security.allowedHostNames=localhost",
- "hawkbit.server.security.httpFirewallIgnoredPaths=/index.html" })
/**
* Feature: Integration Test - Security
* Story: Allowed Host Names
*/
+@TestPropertySource(properties = {
+ "spring.flyway.enabled=true", // if hibernate is used there could be db inconsistencies when executing tests with and without flyway
+ "hawkbit.server.security.allowedHostNames=localhost",
+ "hawkbit.server.security.httpFirewallIgnoredPaths=/index.html" })
class AllowedHostNamesTest extends AbstractSecurityTest {
/**
diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java
deleted file mode 100644
index df528940d..000000000
--- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java
+++ /dev/null
@@ -1,324 +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.amqp;
-
-import java.sql.SQLException;
-import java.time.Duration;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Pattern;
-
-import lombok.ToString;
-import lombok.extern.slf4j.Slf4j;
-import org.eclipse.hawkbit.artifact.repository.urlhandler.ArtifactUrlHandler;
-import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings;
-import org.eclipse.hawkbit.repository.ConfirmationManagement;
-import org.eclipse.hawkbit.repository.ControllerManagement;
-import org.eclipse.hawkbit.repository.DeploymentManagement;
-import org.eclipse.hawkbit.repository.DistributionSetManagement;
-import org.eclipse.hawkbit.repository.EntityFactory;
-import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
-import org.eclipse.hawkbit.repository.SystemManagement;
-import org.eclipse.hawkbit.repository.TargetManagement;
-import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
-import org.eclipse.hawkbit.security.SystemSecurityContext;
-import org.springframework.amqp.core.Binding;
-import org.springframework.amqp.core.BindingBuilder;
-import org.springframework.amqp.core.FanoutExchange;
-import org.springframework.amqp.core.Queue;
-import org.springframework.amqp.core.QueueBuilder;
-import org.springframework.amqp.rabbit.connection.ConnectionFactory;
-import org.springframework.amqp.rabbit.core.RabbitAdmin;
-import org.springframework.amqp.rabbit.core.RabbitTemplate;
-import org.springframework.amqp.rabbit.listener.ConditionalRejectingErrorHandler;
-import org.springframework.amqp.rabbit.listener.FatalExceptionStrategy;
-import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
-import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
-import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.cloud.bus.ServiceMatcher;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.PropertySource;
-import org.springframework.retry.backoff.ExponentialBackOffPolicy;
-import org.springframework.retry.support.RetryTemplate;
-import org.springframework.util.ErrorHandler;
-
-/**
- * Spring configuration for AMQP based DMF communication for indirect device integration.
- */
-@Slf4j
-@EnableConfigurationProperties({ AmqpProperties.class, AmqpDeadletterProperties.class })
-@ConditionalOnProperty(prefix = "hawkbit.dmf.rabbitmq", name = "enabled", matchIfMissing = true)
-@PropertySource("classpath:/hawkbit-dmf-defaults.properties")
-public class AmqpConfiguration {
-
- private final AmqpProperties amqpProperties;
- private final AmqpDeadletterProperties amqpDeadletterProperties;
- private final ConnectionFactory rabbitConnectionFactory;
- private ServiceMatcher serviceMatcher;
-
- public AmqpConfiguration(final AmqpProperties amqpProperties, final AmqpDeadletterProperties amqpDeadletterProperties,
- final ConnectionFactory rabbitConnectionFactory) {
- this.amqpProperties = amqpProperties;
- this.amqpDeadletterProperties = amqpDeadletterProperties;
- this.rabbitConnectionFactory = rabbitConnectionFactory;
- }
-
- @Autowired(required = false) // spring setter injection
- public void setServiceMatcher(final ServiceMatcher serviceMatcher) {
- this.serviceMatcher = serviceMatcher;
- }
-
- @Bean
- public FatalExceptionStrategy sqlFatalSQLExceptionStrategy(final AmqpProperties amqpProperties) {
- return new SqlFatalExceptionStrategy(amqpProperties.getFatalSqlExceptionPolicy());
- }
-
- /**
- * Creates a custom error handler bean.
- *
- * @param fatalExceptionStrategies list of {@link FatalExceptionStrategy} handlers. isFatal will be called for causes,
- * up to the first fatal, so the implementation don't need to iterate over the causes.
- * @return the delegating error handler bean
- */
- @Bean
- @ConditionalOnMissingBean
- public ErrorHandler errorHandler(
- final List fatalExceptionStrategies,
- @Value("${hawkbit.dmf.rabbitmq.fatal-exception-types:}") final List fatalExceptionTypes) {
- return new ConditionalRejectingErrorHandler(new RequeueExceptionStrategy(fatalExceptionStrategies, fatalExceptionTypes));
- }
-
- /**
- * Create a {@link RabbitAdmin} and ignore declaration exceptions.
- * {@link RabbitAdmin#setIgnoreDeclarationExceptions(boolean)}
- *
- * @return the bean
- */
- @Bean
- public RabbitAdmin rabbitAdmin() {
- final RabbitAdmin rabbitAdmin = new RabbitAdmin(rabbitConnectionFactory);
- rabbitAdmin.setIgnoreDeclarationExceptions(true);
- return rabbitAdmin;
- }
-
- /**
- * @return {@link RabbitTemplate} with automatic retry, published confirms and {@link Jackson2JsonMessageConverter}.
- */
- @Bean
- public RabbitTemplate rabbitTemplate() {
- final RabbitTemplate rabbitTemplate = new RabbitTemplate(rabbitConnectionFactory);
- rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
-
- final RetryTemplate retryTemplate = new RetryTemplate();
- retryTemplate.setBackOffPolicy(new ExponentialBackOffPolicy());
- rabbitTemplate.setRetryTemplate(retryTemplate);
-
- rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
- if (ack) {
- log.debug("Message with {} confirmed by broker.", correlationData);
- } else {
- log.error("Broker is unable to handle message with {} : {}", correlationData, cause);
- }
- });
-
- return rabbitTemplate;
- }
-
- /**
- * Create the DMF API receiver queue for retrieving DMF messages.
- *
- * @return the receiver queue
- */
- @Bean
- public Queue dmfReceiverQueue() {
- return new Queue(
- amqpProperties.getReceiverQueue(),
- true, false, false,
- amqpDeadletterProperties.getDeadLetterExchangeArgs(amqpProperties.getDeadLetterExchange()));
- }
-
- /**
- * Create the DMF API receiver queue for authentication requests called by 3rd
- * party artifact storages for download authorization by devices.
- *
- * @return the receiver queue
- */
- @Bean
- public Queue authenticationReceiverQueue() {
- return QueueBuilder.nonDurable(amqpProperties.getAuthenticationReceiverQueue())
- .autoDelete()
- .withArguments(getTTLMaxArgsAuthenticationQueue())
- .build();
- }
-
- /**
- * Create DMF exchange.
- *
- * @return the fanout exchange
- */
- @Bean
- public FanoutExchange dmfSenderExchange() {
- return new FanoutExchange(AmqpSettings.DMF_EXCHANGE);
- }
-
- /**
- * Create the Binding {@link AmqpConfiguration#dmfReceiverQueue()} to
- * {@link AmqpConfiguration#dmfSenderExchange()}.
- *
- * @return the binding and create the queue and exchange
- */
- @Bean
- public Binding bindDmfSenderExchangeToDmfQueue() {
- return BindingBuilder.bind(dmfReceiverQueue()).to(dmfSenderExchange());
- }
-
- /**
- * Create dead letter queue.
- *
- * @return the queue
- */
- @Bean
- public Queue deadLetterQueue() {
- return amqpDeadletterProperties.createDeadletterQueue(amqpProperties.getDeadLetterQueue());
- }
-
- /**
- * Create the dead letter fanout exchange.
- *
- * @return the fanout exchange
- */
- @Bean
- public FanoutExchange deadLetterExchange() {
- return new FanoutExchange(amqpProperties.getDeadLetterExchange());
- }
-
- /**
- * Create the Binding deadLetterQueue to deadLetterExchange.
- *
- * @return the binding
- */
- @Bean
- public Binding bindDeadLetterQueueToDeadLetterExchange() {
- return BindingBuilder.bind(deadLetterQueue()).to(deadLetterExchange());
- }
-
- /**
- * Create AMQP handler service bean.
- *
- * @param rabbitTemplate for converting messages
- * @param amqpMessageDispatcherService to sending events to DMF client
- * @param controllerManagement for target repo access
- * @param entityFactory to create entities
- * @return handler service bean
- */
- @Bean
- @ConditionalOnMissingBean
- public AmqpMessageHandlerService amqpMessageHandlerService(
- final RabbitTemplate rabbitTemplate,
- final AmqpMessageDispatcherService amqpMessageDispatcherService,
- final ControllerManagement controllerManagement, final EntityFactory entityFactory,
- final SystemSecurityContext systemSecurityContext,
- final TenantConfigurationManagement tenantConfigurationManagement,
- final ConfirmationManagement confirmationManagement) {
- return new AmqpMessageHandlerService(
- rabbitTemplate, amqpMessageDispatcherService, controllerManagement,
- entityFactory, systemSecurityContext, tenantConfigurationManagement, confirmationManagement);
- }
-
- /**
- * Create default amqp sender service bean.
- *
- * @return the default amqp sender service bean
- */
- @Bean
- @ConditionalOnMissingBean
- public AmqpMessageSenderService amqpSenderServiceBean() {
- return new DefaultAmqpMessageSenderService(rabbitTemplate());
- }
-
- /**
- * Create RabbitListenerContainerFactory bean if no listenerContainerFactory bean found
- *
- * @return RabbitListenerContainerFactory bean
- */
- @Bean
- @ConditionalOnMissingBean(name = "listenerContainerFactory")
- public RabbitListenerContainerFactory listenerContainerFactory(
- final SimpleRabbitListenerContainerFactoryConfigurer configurer, final ErrorHandler errorHandler) {
- final ConfigurableRabbitListenerContainerFactory factory = new ConfigurableRabbitListenerContainerFactory(
- amqpProperties.isMissingQueuesFatal(), amqpProperties.getDeclarationRetries(), errorHandler);
- configurer.configure(factory, rabbitConnectionFactory);
- return factory;
- }
-
- @Bean
- @ConditionalOnMissingBean(AmqpMessageDispatcherService.class)
- AmqpMessageDispatcherService amqpMessageDispatcherService(
- final RabbitTemplate rabbitTemplate,
- final AmqpMessageSenderService amqpSenderService, final ArtifactUrlHandler artifactUrlHandler,
- final SystemSecurityContext systemSecurityContext, final SystemManagement systemManagement,
- final TargetManagement targetManagement, final DistributionSetManagement distributionSetManagement,
- final SoftwareModuleManagement softwareModuleManagement, final DeploymentManagement deploymentManagement,
- final TenantConfigurationManagement tenantConfigurationManagement) {
- return new AmqpMessageDispatcherService(rabbitTemplate, amqpSenderService, artifactUrlHandler,
- systemSecurityContext, systemManagement, targetManagement, serviceMatcher, distributionSetManagement,
- softwareModuleManagement, deploymentManagement, tenantConfigurationManagement);
- }
-
- private static Map getTTLMaxArgsAuthenticationQueue() {
- final Map args = new HashMap<>(2);
- args.put("x-message-ttl", Duration.ofSeconds(30).toMillis());
- args.put("x-max-length", 1_000);
- return args;
- }
-
- @ToString
- private static class SqlFatalExceptionStrategy implements FatalExceptionStrategy {
-
- private final boolean fatalByDefault;
- private final List unlessErrorCodeIn;
- private final List unlessSqlStateIn;
- private final List unlessMessageMatches;
-
- public SqlFatalExceptionStrategy(final AmqpProperties.FatalSqlExceptionPolicy fatalSqlExceptions) {
- this.fatalByDefault = fatalSqlExceptions.isByDefault();
- this.unlessErrorCodeIn = fatalSqlExceptions.getUnlessErrorCodeIn();
- this.unlessSqlStateIn = fatalSqlExceptions.getUnlessSqlStateIn();
- this.unlessMessageMatches = fatalSqlExceptions.getUnlessMessageMatches();
- }
-
- @Override
- public boolean isFatal(final Throwable t) {
- if (t instanceof SQLException sqlException) {
- if (unlessErrorCodeIn.contains(sqlException.getErrorCode())) {
- return !fatalByDefault;
- } else if (unlessSqlStateIn.contains(sqlException.getSQLState())) {
- return !fatalByDefault;
- } else {
- for (final Pattern pattern : unlessMessageMatches) {
- if (pattern.matcher(sqlException.getMessage()).matches()) {
- return !fatalByDefault;
- }
- }
- return fatalByDefault;
- }
- }
- return false;
- }
- }
-}
\ No newline at end of file
diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java
index cf6d0e98d..bd5bec24b 100644
--- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java
+++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java
@@ -25,11 +25,6 @@ public class AmqpProperties {
private static final int DEFAULT_QUEUE_DECLARATION_RETRIES = 50;
- /**
- * Enable DMF API based on AMQP 0.9
- */
- private boolean enabled = true;
-
/**
* DMF API dead letter queue.
*/
diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/DmfApiConfiguration.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/DmfApiConfiguration.java
index 4648e8fbc..05df8ee6b 100644
--- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/DmfApiConfiguration.java
+++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/DmfApiConfiguration.java
@@ -9,14 +9,319 @@
*/
package org.eclipse.hawkbit.amqp;
+import java.sql.SQLException;
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import lombok.ToString;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.hawkbit.artifact.repository.urlhandler.ArtifactUrlHandler;
+import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings;
+import org.eclipse.hawkbit.repository.ConfirmationManagement;
+import org.eclipse.hawkbit.repository.ControllerManagement;
+import org.eclipse.hawkbit.repository.DeploymentManagement;
+import org.eclipse.hawkbit.repository.DistributionSetManagement;
+import org.eclipse.hawkbit.repository.EntityFactory;
+import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
+import org.eclipse.hawkbit.repository.SystemManagement;
+import org.eclipse.hawkbit.repository.TargetManagement;
+import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
+import org.eclipse.hawkbit.security.SystemSecurityContext;
+import org.springframework.amqp.core.Binding;
+import org.springframework.amqp.core.BindingBuilder;
+import org.springframework.amqp.core.FanoutExchange;
+import org.springframework.amqp.core.Queue;
+import org.springframework.amqp.core.QueueBuilder;
+import org.springframework.amqp.rabbit.connection.ConnectionFactory;
+import org.springframework.amqp.rabbit.core.RabbitAdmin;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.amqp.rabbit.listener.ConditionalRejectingErrorHandler;
+import org.springframework.amqp.rabbit.listener.FatalExceptionStrategy;
+import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
+import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
+import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.cloud.bus.ServiceMatcher;
+import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.retry.backoff.ExponentialBackOffPolicy;
+import org.springframework.retry.support.RetryTemplate;
+import org.springframework.util.ErrorHandler;
/**
- * Enable Device Management Federation API.
+ * Spring configuration for AMQP based DMF communication for indirect device integration.
*/
-@Configuration
+@Slf4j
@ComponentScan
-@Import(AmqpConfiguration.class)
-public class DmfApiConfiguration {}
\ No newline at end of file
+@EnableConfigurationProperties({ AmqpProperties.class, AmqpDeadletterProperties.class })
+@ConditionalOnProperty(prefix = "hawkbit.dmf", name = "enabled", matchIfMissing = true)
+@PropertySource("classpath:/hawkbit-dmf-defaults.properties")
+public class DmfApiConfiguration {
+
+ private final AmqpProperties amqpProperties;
+ private final AmqpDeadletterProperties amqpDeadletterProperties;
+ private final ConnectionFactory rabbitConnectionFactory;
+
+ private ServiceMatcher serviceMatcher;
+
+ public DmfApiConfiguration(
+ final AmqpProperties amqpProperties, final AmqpDeadletterProperties amqpDeadletterProperties,
+ final ConnectionFactory rabbitConnectionFactory) {
+ this.amqpProperties = amqpProperties;
+ this.amqpDeadletterProperties = amqpDeadletterProperties;
+ this.rabbitConnectionFactory = rabbitConnectionFactory;
+ }
+
+ @Autowired(required = false) // spring setter injection
+ public void setServiceMatcher(final ServiceMatcher serviceMatcher) {
+ this.serviceMatcher = serviceMatcher;
+ }
+
+ @Bean
+ public FatalExceptionStrategy sqlFatalSQLExceptionStrategy(final AmqpProperties amqpProperties) {
+ return new SqlFatalExceptionStrategy(amqpProperties.getFatalSqlExceptionPolicy());
+ }
+
+ /**
+ * Creates a custom error handler bean.
+ *
+ * @param fatalExceptionStrategies list of {@link FatalExceptionStrategy} handlers. isFatal will be called for causes,
+ * up to the first fatal, so the implementation don't need to iterate over the causes.
+ * @return the delegating error handler bean
+ */
+ @Bean
+ @ConditionalOnMissingBean
+ public ErrorHandler errorHandler(
+ final List fatalExceptionStrategies,
+ @Value("${hawkbit.dmf.rabbitmq.fatal-exception-types:}") final List fatalExceptionTypes) {
+ return new ConditionalRejectingErrorHandler(new RequeueExceptionStrategy(fatalExceptionStrategies, fatalExceptionTypes));
+ }
+
+ /**
+ * Create a {@link RabbitAdmin} and ignore declaration exceptions.
+ * {@link RabbitAdmin#setIgnoreDeclarationExceptions(boolean)}
+ *
+ * @return the bean
+ */
+ @Bean
+ public RabbitAdmin rabbitAdmin() {
+ final RabbitAdmin rabbitAdmin = new RabbitAdmin(rabbitConnectionFactory);
+ rabbitAdmin.setIgnoreDeclarationExceptions(true);
+ return rabbitAdmin;
+ }
+
+ /**
+ * @return {@link RabbitTemplate} with automatic retry, published confirms and {@link Jackson2JsonMessageConverter}.
+ */
+ @Bean
+ public RabbitTemplate rabbitTemplate() {
+ final RabbitTemplate rabbitTemplate = new RabbitTemplate(rabbitConnectionFactory);
+ rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
+
+ final RetryTemplate retryTemplate = new RetryTemplate();
+ retryTemplate.setBackOffPolicy(new ExponentialBackOffPolicy());
+ rabbitTemplate.setRetryTemplate(retryTemplate);
+
+ rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
+ if (ack) {
+ log.debug("Message with {} confirmed by broker.", correlationData);
+ } else {
+ log.error("Broker is unable to handle message with {} : {}", correlationData, cause);
+ }
+ });
+
+ return rabbitTemplate;
+ }
+
+ /**
+ * Create the DMF API receiver queue for retrieving DMF messages.
+ *
+ * @return the receiver queue
+ */
+ @Bean
+ public Queue dmfReceiverQueue() {
+ return new Queue(
+ amqpProperties.getReceiverQueue(),
+ true, false, false,
+ amqpDeadletterProperties.getDeadLetterExchangeArgs(amqpProperties.getDeadLetterExchange()));
+ }
+
+ /**
+ * Create the DMF API receiver queue for authentication requests called by 3rd
+ * party artifact storages for download authorization by devices.
+ *
+ * @return the receiver queue
+ */
+ @Bean
+ public Queue authenticationReceiverQueue() {
+ return QueueBuilder.nonDurable(amqpProperties.getAuthenticationReceiverQueue())
+ .autoDelete()
+ .withArguments(getTTLMaxArgsAuthenticationQueue())
+ .build();
+ }
+
+ /**
+ * Create DMF exchange.
+ *
+ * @return the fanout exchange
+ */
+ @Bean
+ public FanoutExchange dmfSenderExchange() {
+ return new FanoutExchange(AmqpSettings.DMF_EXCHANGE);
+ }
+
+ /**
+ * Create the Binding {@link DmfApiConfiguration#dmfReceiverQueue()} to
+ * {@link DmfApiConfiguration#dmfSenderExchange()}.
+ *
+ * @return the binding and create the queue and exchange
+ */
+ @Bean
+ public Binding bindDmfSenderExchangeToDmfQueue() {
+ return BindingBuilder.bind(dmfReceiverQueue()).to(dmfSenderExchange());
+ }
+
+ /**
+ * Create dead letter queue.
+ *
+ * @return the queue
+ */
+ @Bean
+ public Queue deadLetterQueue() {
+ return amqpDeadletterProperties.createDeadletterQueue(amqpProperties.getDeadLetterQueue());
+ }
+
+ /**
+ * Create the dead letter fanout exchange.
+ *
+ * @return the fanout exchange
+ */
+ @Bean
+ public FanoutExchange deadLetterExchange() {
+ return new FanoutExchange(amqpProperties.getDeadLetterExchange());
+ }
+
+ /**
+ * Create the Binding deadLetterQueue to deadLetterExchange.
+ *
+ * @return the binding
+ */
+ @Bean
+ public Binding bindDeadLetterQueueToDeadLetterExchange() {
+ return BindingBuilder.bind(deadLetterQueue()).to(deadLetterExchange());
+ }
+
+ /**
+ * Create AMQP handler service bean.
+ *
+ * @param rabbitTemplate for converting messages
+ * @param amqpMessageDispatcherService to sending events to DMF client
+ * @param controllerManagement for target repo access
+ * @param entityFactory to create entities
+ * @return handler service bean
+ */
+ @Bean
+ @ConditionalOnMissingBean
+ public AmqpMessageHandlerService amqpMessageHandlerService(
+ final RabbitTemplate rabbitTemplate,
+ final AmqpMessageDispatcherService amqpMessageDispatcherService,
+ final ControllerManagement controllerManagement, final EntityFactory entityFactory,
+ final SystemSecurityContext systemSecurityContext,
+ final TenantConfigurationManagement tenantConfigurationManagement,
+ final ConfirmationManagement confirmationManagement) {
+ return new AmqpMessageHandlerService(
+ rabbitTemplate, amqpMessageDispatcherService, controllerManagement,
+ entityFactory, systemSecurityContext, tenantConfigurationManagement, confirmationManagement);
+ }
+
+ /**
+ * Create default amqp sender service bean.
+ *
+ * @return the default amqp sender service bean
+ */
+ @Bean
+ @ConditionalOnMissingBean
+ public AmqpMessageSenderService amqpSenderServiceBean() {
+ return new DefaultAmqpMessageSenderService(rabbitTemplate());
+ }
+
+ /**
+ * Create RabbitListenerContainerFactory bean if no listenerContainerFactory bean found
+ *
+ * @return RabbitListenerContainerFactory bean
+ */
+ @Bean
+ @ConditionalOnMissingBean(name = "listenerContainerFactory")
+ public RabbitListenerContainerFactory listenerContainerFactory(
+ final SimpleRabbitListenerContainerFactoryConfigurer configurer, final ErrorHandler errorHandler) {
+ final ConfigurableRabbitListenerContainerFactory factory = new ConfigurableRabbitListenerContainerFactory(
+ amqpProperties.isMissingQueuesFatal(), amqpProperties.getDeclarationRetries(), errorHandler);
+ configurer.configure(factory, rabbitConnectionFactory);
+ return factory;
+ }
+
+ @Bean
+ @ConditionalOnMissingBean(AmqpMessageDispatcherService.class)
+ AmqpMessageDispatcherService amqpMessageDispatcherService(
+ final RabbitTemplate rabbitTemplate,
+ final AmqpMessageSenderService amqpSenderService, final ArtifactUrlHandler artifactUrlHandler,
+ final SystemSecurityContext systemSecurityContext, final SystemManagement systemManagement,
+ final TargetManagement targetManagement, final DistributionSetManagement distributionSetManagement,
+ final SoftwareModuleManagement softwareModuleManagement, final DeploymentManagement deploymentManagement,
+ final TenantConfigurationManagement tenantConfigurationManagement) {
+ return new AmqpMessageDispatcherService(rabbitTemplate, amqpSenderService, artifactUrlHandler,
+ systemSecurityContext, systemManagement, targetManagement, serviceMatcher, distributionSetManagement,
+ softwareModuleManagement, deploymentManagement, tenantConfigurationManagement);
+ }
+
+ private static Map getTTLMaxArgsAuthenticationQueue() {
+ final Map args = new HashMap<>(2);
+ args.put("x-message-ttl", Duration.ofSeconds(30).toMillis());
+ args.put("x-max-length", 1_000);
+ return args;
+ }
+
+ @ToString
+ private static class SqlFatalExceptionStrategy implements FatalExceptionStrategy {
+
+ private final boolean fatalByDefault;
+ private final List unlessErrorCodeIn;
+ private final List unlessSqlStateIn;
+ private final List unlessMessageMatches;
+
+ public SqlFatalExceptionStrategy(final AmqpProperties.FatalSqlExceptionPolicy fatalSqlExceptions) {
+ this.fatalByDefault = fatalSqlExceptions.isByDefault();
+ this.unlessErrorCodeIn = fatalSqlExceptions.getUnlessErrorCodeIn();
+ this.unlessSqlStateIn = fatalSqlExceptions.getUnlessSqlStateIn();
+ this.unlessMessageMatches = fatalSqlExceptions.getUnlessMessageMatches();
+ }
+
+ @Override
+ public boolean isFatal(final Throwable t) {
+ if (t instanceof SQLException sqlException) {
+ if (unlessErrorCodeIn.contains(sqlException.getErrorCode())) {
+ return !fatalByDefault;
+ } else if (unlessSqlStateIn.contains(sqlException.getSQLState())) {
+ return !fatalByDefault;
+ } else {
+ for (final Pattern pattern : unlessMessageMatches) {
+ if (pattern.matcher(sqlException.getMessage()).matches()) {
+ return !fatalByDefault;
+ }
+ }
+ return fatalByDefault;
+ }
+ }
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/hawkbit-dmf/hawkbit-dmf-server/src/main/resources/application.properties b/hawkbit-dmf/hawkbit-dmf-server/src/main/resources/application.properties
index ceb47e613..60e3d08ad 100644
--- a/hawkbit-dmf/hawkbit-dmf-server/src/main/resources/application.properties
+++ b/hawkbit-dmf/hawkbit-dmf-server/src/main/resources/application.properties
@@ -24,14 +24,6 @@ logging.pattern.console=%clr(%d{${logging.pattern.dateformat:yyyy-MM-dd'T'HH:mm:
# Optional events
hawkbit.server.repository.publish-target-poll-event=false
-## Configuration for DMF/RabbitMQ integration
-hawkbit.dmf.rabbitmq.enabled=true
-spring.rabbitmq.username=guest
-spring.rabbitmq.password=guest
-spring.rabbitmq.virtual-host=/
-spring.rabbitmq.host=localhost
-spring.rabbitmq.port=5672
-
spring.main.web-application-type=none
hawkbit.server.security.dos.filter.enabled=false
@@ -43,11 +35,7 @@ hawkbit.lock=inMemory
# Disable discovery client of spring-cloud-commons
spring.cloud.discovery.enabled=false
-# Enable communication between services
-spring.cloud.bus.enabled=true
-spring.cloud.bus.ack.enabled=false
-spring.cloud.bus.refresh.enabled=false
-spring.cloud.bus.env.enabled=false
+# Configure communication between services
endpoints.spring.cloud.bus.refresh.enabled=false
endpoints.spring.cloud.bus.env.enabled=false
spring.cloud.stream.bindings.springCloudBusInput.group=dmf-server
diff --git a/hawkbit-mgmt/hawkbit-mgmt-server/src/main/resources/application.properties b/hawkbit-mgmt/hawkbit-mgmt-server/src/main/resources/application.properties
index d71ed5ab3..5d3f86195 100644
--- a/hawkbit-mgmt/hawkbit-mgmt-server/src/main/resources/application.properties
+++ b/hawkbit-mgmt/hawkbit-mgmt-server/src/main/resources/application.properties
@@ -37,24 +37,13 @@ server.servlet.encoding.force=true
# Optional events
hawkbit.server.repository.publish-target-poll-event=false
-## Configuration for DMF/RabbitMQ integration
-spring.rabbitmq.username=guest
-spring.rabbitmq.password=guest
-spring.rabbitmq.virtual-host=/
-spring.rabbitmq.host=localhost
-spring.rabbitmq.port=5672
-
# Enable CORS and specify the allowed origins:
#hawkbit.server.security.cors.enabled=true
#hawkbit.server.security.cors.allowedOrigins=http://localhost
# Disable discovery client of spring-cloud-commons
spring.cloud.discovery.enabled=false
-# Enable communication between services
-spring.cloud.bus.enabled=true
-spring.cloud.bus.ack.enabled=false
-spring.cloud.bus.refresh.enabled=false
-spring.cloud.bus.env.enabled=false
+# Configure communication between services
endpoints.spring.cloud.bus.refresh.enabled=false
endpoints.spring.cloud.bus.env.enabled=false
spring.cloud.stream.bindings.springCloudBusInput.group=mgmt-server
diff --git a/hawkbit-mgmt/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/AbstractSecurityTest.java b/hawkbit-mgmt/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/AbstractSecurityTest.java
index a2a52cbd8..04e3a7760 100644
--- a/hawkbit-mgmt/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/AbstractSecurityTest.java
+++ b/hawkbit-mgmt/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/AbstractSecurityTest.java
@@ -20,9 +20,9 @@ import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
-@SpringBootTest(properties = { "hawkbit.dmf.rabbitmq.enabled=false" })
+@SpringBootTest
@ExtendWith(SharedSqlTestDatabaseExtension.class)
-public abstract class AbstractSecurityTest {
+abstract class AbstractSecurityTest {
protected MockMvc mvc;
@Autowired
diff --git a/hawkbit-mgmt/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/AllowedHostNamesTest.java b/hawkbit-mgmt/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/AllowedHostNamesTest.java
index 6a2b507e5..a29e06108 100644
--- a/hawkbit-mgmt/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/AllowedHostNamesTest.java
+++ b/hawkbit-mgmt/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/AllowedHostNamesTest.java
@@ -16,13 +16,13 @@ import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.test.context.TestPropertySource;
-@TestPropertySource(properties = {
- "hawkbit.server.security.allowedHostNames=localhost",
- "hawkbit.server.security.httpFirewallIgnoredPaths=/index.html" })
/**
* Feature: Integration Test - Security
* Story: Allowed Host Names
*/
+@TestPropertySource(properties = {
+ "hawkbit.server.security.allowedHostNames=localhost",
+ "hawkbit.server.security.httpFirewallIgnoredPaths=/index.html" })
class AllowedHostNamesTest extends AbstractSecurityTest {
/**
diff --git a/hawkbit-mgmt/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/CorsTest.java b/hawkbit-mgmt/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/CorsTest.java
index 18fd18f0c..27e0a9f42 100644
--- a/hawkbit-mgmt/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/CorsTest.java
+++ b/hawkbit-mgmt/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/CorsTest.java
@@ -22,19 +22,18 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpHeaders;
import org.springframework.test.web.servlet.ResultActions;
+/**
+ * Feature: Integration Test - Security
+ * Story: CORS
+ */
@SpringBootTest(
properties = {
- "hawkbit.dmf.rabbitmq.enabled=false",
"hawkbit.server.security.cors.enabled=true",
"hawkbit.server.security.cors.allowedOrigins=" +
CorsTest.ALLOWED_ORIGIN_FIRST + "," +
CorsTest.ALLOWED_ORIGIN_SECOND,
"hawkbit.server.security.cors.exposedHeaders=" +
HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN })
-/**
- * Feature: Integration Test - Security
- * Story: CORS
- */
class CorsTest extends AbstractSecurityTest {
static final String ALLOWED_ORIGIN_FIRST = "http://test.first.origin";
diff --git a/hawkbit-monolith/hawkbit-update-server/README.md b/hawkbit-monolith/hawkbit-update-server/README.md
index cf174f5c5..85467856c 100644
--- a/hawkbit-monolith/hawkbit-update-server/README.md
+++ b/hawkbit-monolith/hawkbit-update-server/README.md
@@ -26,12 +26,6 @@ The Management API can be accessed via http://localhost:8080/rest/v1
Clustering in hawkBit is based on _Spring Cloud Bus_. It is not enabled in the example app by default.
-Add to your `application.properties` :
-
-```properties
-spring.cloud.bus.enabled=true
-```
-
Add to your `pom.xml` :
```xml
diff --git a/hawkbit-monolith/hawkbit-update-server/src/main/resources/application.properties b/hawkbit-monolith/hawkbit-update-server/src/main/resources/application.properties
index fe9431aec..406594630 100644
--- a/hawkbit-monolith/hawkbit-update-server/src/main/resources/application.properties
+++ b/hawkbit-monolith/hawkbit-update-server/src/main/resources/application.properties
@@ -45,12 +45,14 @@ hawkbit.cache.global.ttl=0
# Optional events
hawkbit.server.repository.publish-target-poll-event=false
-## Configuration for DMF/RabbitMQ integration
-spring.rabbitmq.username=guest
-spring.rabbitmq.password=guest
-spring.rabbitmq.virtual-host=/
-spring.rabbitmq.host=localhost
-spring.rabbitmq.port=5672
+## Disable RabbitMQ auto configuration. Comment it to enable RabbitMQ support.
+spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration
+## Configuration Spring Bus (disabled by default) - no cluster support. To enable it, enable RabbitMQ (see above)
+## and comment the line (spring.cloud.bus.enabled=false) or set spring.cloud.bus.enabled=true
+spring.cloud.bus.enabled=false
+## Disable DMF (by default) - no DMF support. To enable it, enable RabbitMQ (see above) and comment the line
+## (hawkbit.dmf.rabbitmq.enabled=false) set hawkbit.dmf.rabbitmq.enabled=true
+hawkbit.dmf.enabled=false
# Enable CORS and specify the allowed origins:
#hawkbit.server.security.cors.enabled=true
diff --git a/hawkbit-monolith/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/AbstractSecurityTest.java b/hawkbit-monolith/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/AbstractSecurityTest.java
index 808e5a2f6..5bba9eaaa 100644
--- a/hawkbit-monolith/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/AbstractSecurityTest.java
+++ b/hawkbit-monolith/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/AbstractSecurityTest.java
@@ -20,9 +20,9 @@ import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
-@SpringBootTest(properties = { "hawkbit.dmf.rabbitmq.enabled=false" })
+@SpringBootTest
@ExtendWith(SharedSqlTestDatabaseExtension.class)
-public abstract class AbstractSecurityTest {
+abstract class AbstractSecurityTest {
protected MockMvc mvc;
@Autowired
diff --git a/hawkbit-monolith/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/AllowedHostNamesTest.java b/hawkbit-monolith/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/AllowedHostNamesTest.java
index 0eb48d7c8..d48512b60 100644
--- a/hawkbit-monolith/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/AllowedHostNamesTest.java
+++ b/hawkbit-monolith/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/AllowedHostNamesTest.java
@@ -16,14 +16,14 @@ import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.test.context.TestPropertySource;
-@TestPropertySource(properties = {
- "hawkbit.server.security.allowedHostNames=localhost",
- "hawkbit.server.security.httpFirewallIgnoredPaths=/index.html"
-})
/**
* Feature: Integration Test - Security
* Story: Allowed Host Names
*/
+@TestPropertySource(properties = {
+ "hawkbit.server.security.allowedHostNames=localhost",
+ "hawkbit.server.security.httpFirewallIgnoredPaths=/index.html"
+})
class AllowedHostNamesTest extends AbstractSecurityTest {
/**
diff --git a/hawkbit-monolith/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/CorsTest.java b/hawkbit-monolith/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/CorsTest.java
index 36bf3ab03..c64da218f 100644
--- a/hawkbit-monolith/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/CorsTest.java
+++ b/hawkbit-monolith/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/CorsTest.java
@@ -22,19 +22,18 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpHeaders;
import org.springframework.test.web.servlet.ResultActions;
+/**
+ * Feature: Integration Test - Security
+ * Story: CORS
+ */
@SpringBootTest(
properties = {
- "hawkbit.dmf.rabbitmq.enabled=false",
"hawkbit.server.security.cors.enabled=true",
"hawkbit.server.security.cors.allowedOrigins=" +
CorsTest.ALLOWED_ORIGIN_FIRST + "," +
CorsTest.ALLOWED_ORIGIN_SECOND,
"hawkbit.server.security.cors.exposedHeaders=" +
HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN })
-/**
- * Feature: Integration Test - Security
- * Story: CORS
- */
class CorsTest extends AbstractSecurityTest {
static final String ALLOWED_ORIGIN_FIRST = "http://test.first.origin";
diff --git a/hawkbit-repository/hawkbit-repository-core/src/main/resources/hawkbit-eventbus-defaults.properties b/hawkbit-repository/hawkbit-repository-core/src/main/resources/hawkbit-eventbus-defaults.properties
index 29c90ada8..4c7bf9e96 100644
--- a/hawkbit-repository/hawkbit-repository-core/src/main/resources/hawkbit-eventbus-defaults.properties
+++ b/hawkbit-repository/hawkbit-repository-core/src/main/resources/hawkbit-eventbus-defaults.properties
@@ -9,7 +9,7 @@
#
# Spring cloud bus and stream
-spring.cloud.bus.enabled=false
+spring.cloud.bus.enabled=true
# Disable Cloud Bus default events
spring.cloud.bus.env.enabled=false
spring.cloud.bus.ack.enabled=false
diff --git a/hawkbit-sdk/hawkbit-sdk-dmf/src/main/resources/application.properties b/hawkbit-sdk/hawkbit-sdk-dmf/src/main/resources/application.properties
index ff70a9e8d..314497070 100644
--- a/hawkbit-sdk/hawkbit-sdk-dmf/src/main/resources/application.properties
+++ b/hawkbit-sdk/hawkbit-sdk-dmf/src/main/resources/application.properties
@@ -8,16 +8,7 @@
# SPDX-License-Identifier: EPL-2.0
#
-## Configuration for local RabbitMQ integration
-spring.rabbitmq.username=guest
-spring.rabbitmq.password=guest
-spring.rabbitmq.virtualHost=/
-spring.rabbitmq.host=localhost
-spring.rabbitmq.port=5672
-spring.rabbitmq.dynamic=true
-
-## Configuration for sdk dmf
-hawkbit.sdk.dmf.enabled=true
+## Configuration for SDK DMF
hawkbit.sdk.dmf.amqp.receiverConnectorQueueFromSp=sdk_receiver
hawkbit.sdk.dmf.amqp.senderForSpExchange=sdk.replyTo
diff --git a/site/content/guides/runhawkbit.md b/site/content/guides/runhawkbit.md
index 7ed8d951f..52e8ceb68 100644
--- a/site/content/guides/runhawkbit.md
+++ b/site/content/guides/runhawkbit.md
@@ -4,7 +4,7 @@ parent: Guides
weight: 31
---
-In this guide we describe how to run a full featured hawkBit setup based on a production ready infrastructure. It is
+In this guide we describe how to run a full-featured hawkBit setup based on a production ready infrastructure. It is
based on the hawkBit example modules and update server.
@@ -20,7 +20,7 @@ This guide describes a target architecture that you will probably expect in a pr
- hawkBit [Update Server](https://github.com/eclipse-hawkbit/hawkbit/tree/master/hawkbit-monolith/hawkbit-update-server).
- [MariaDB](https://mariadb.org) for the repository.
-- [RabbitMQ](https://www.rabbitmq.com) for DMF communication.
+- [RabbitMQ](https://www.rabbitmq.com) for DMF communication (optional for monolith / single host deployment).
For testing, demonstration or integrations purposes you could also use hawkBit SDK:
- [hawkBit SDK Management API client](https://github.com/eclipse-hawkbit/hawkbit/blob/master/hawkbit-sdk/hawkbit-sdk-commons/src/main/java/org/eclipse/hawkbit/sdk/HawkbitClient.java).
@@ -50,17 +50,17 @@ spring.datasource.username=
spring.datasource.password=
```
-### Configure RabbitMQ connection settings for update server and device simulator (optional).
+### Configure RabbitMQ connection settings for services (optional for monolith single host deployments).
-We provide already defaults that should work with a standard Rabbit installation. Otherwise configure the following in
-the `application.properties` of the two services:
+We provide already defaults that should work with a standard Rabbit installation (spring boot RabbitProperties defaults).
+Otherwise, configure the following in the `application.properties` of the services:
```properties
-spring.rabbitmq.host=localhost
-spring.rabbitmq.port=5672
-spring.rabbitmq.virtualHost=/
-spring.rabbitmq.username=guest
-spring.rabbitmq.password=guest
+spring.rabbitmq.host=
+spring.rabbitmq.port=
+spring.rabbitmq.virtualHost=
+spring.rabbitmq.username=
+spring.rabbitmq.password=
```
### Adapt hostnames of demo simulator
diff --git a/site/pom.xml b/site/pom.xml
index d2842c594..fb7f0996a 100644
--- a/site/pom.xml
+++ b/site/pom.xml
@@ -129,7 +129,12 @@
org.eclipse.hawkbit
- hawkbit-starter
+ hawkbit-ddi-starter
+ ${project.version}
+
+
+ org.eclipse.hawkbit
+ hawkbit-mgmt-starter
${project.version}
diff --git a/site/src/main/resources/application.properties b/site/src/main/resources/application.properties
index 25d6a627b..908e33338 100644
--- a/site/src/main/resources/application.properties
+++ b/site/src/main/resources/application.properties
@@ -27,9 +27,6 @@ hawkbit.server.ddi.security.authentication.gatewaytoken.enabled=false
# Optional events
hawkbit.server.repository.publish-target-poll-event=false
-## Configuration for DMF/RabbitMQ integration
-hawkbit.dmf.rabbitmq.enabled=false
-
# Swagger Configuration
springdoc.api-docs.version=openapi_3_0
springdoc.show-oauth2-endpoints=true