Merge remote-tracking branch 'origin/master' into

fix_Consitent_table_muliselect

Signed-off-by: Melanie Retter <melanie.retter@bosch-si.com>
This commit is contained in:
Melanie Retter
2016-06-14 11:12:09 +02:00
20 changed files with 329 additions and 65 deletions

View File

@@ -8,6 +8,7 @@
*/ */
package org.eclipse.hawkbit.simulator.amqp; package org.eclipse.hawkbit.simulator.amqp;
import java.time.Duration;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@@ -15,6 +16,7 @@ import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange; import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue; import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
@@ -65,9 +67,12 @@ public class AmqpConfiguration {
* @return the queue * @return the queue
*/ */
@Bean @Bean
public Queue receiverConnectorQueueFromSp() { public Queue receiverConnectorQueueFromHawkBit() {
return new Queue(amqpProperties.getReceiverConnectorQueueFromSp(), true, false, false, final Map<String, Object> arguments = getDeadLetterExchangeArgs();
getDeadLetterExchangeArgs()); arguments.putAll(getTTLMaxArgs());
return QueueBuilder.nonDurable(amqpProperties.getReceiverConnectorQueueFromSp()).withArguments(arguments)
.build();
} }
/** /**
@@ -89,7 +94,7 @@ public class AmqpConfiguration {
*/ */
@Bean @Bean
public Binding bindReceiverQueueToSpExchange() { public Binding bindReceiverQueueToSpExchange() {
return BindingBuilder.bind(receiverConnectorQueueFromSp()).to(exchangeQueueToConnector()); return BindingBuilder.bind(receiverConnectorQueueFromHawkBit()).to(exchangeQueueToConnector());
} }
/** /**
@@ -99,7 +104,7 @@ public class AmqpConfiguration {
*/ */
@Bean @Bean
public Queue deadLetterQueue() { public Queue deadLetterQueue() {
return new Queue(amqpProperties.getDeadLetterQueue(), true, false, true); return QueueBuilder.nonDurable(amqpProperties.getDeadLetterQueue()).withArguments(getTTLMaxArgs()).build();
} }
/** /**
@@ -145,4 +150,11 @@ public class AmqpConfiguration {
return args; return args;
} }
private static Map<String, Object> getTTLMaxArgs() {
final Map<String, Object> args = new HashMap<>();
args.put("x-message-ttl", Duration.ofDays(1).toMillis());
args.put("x-max-length", 100_000);
return args;
}
} }

View File

@@ -82,21 +82,4 @@ public class MessageService {
clazz.getTypeName()); clazz.getTypeName());
return (T) rabbitTemplate.getMessageConverter().fromMessage(message); return (T) rabbitTemplate.getMessageConverter().fromMessage(message);
} }
/**
* Method to verify if lwm2m header is set.
*
* @param message
* the message with the header
* @param header
* the header to verify
*/
public void checkIfLwm2mHeaderEmpty(final Message message, final String header) {
final Object headerObject = message.getMessageProperties().getHeaders().get(header);
if (null == headerObject) {
logAndThrowMessageError(message, "Header of " + header + "empty.");
}
}
} }

View File

@@ -19,7 +19,6 @@
<Logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN" /> <Logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN" />
<Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN" /> <Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN" />
<Logger name="org.apache.tomcat.jdbc.pool.ConnectionPool" level="DEBUG" />
<Logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR" /> <Logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR" />
<!-- Security Log with hints on potential attacks --> <!-- Security Log with hints on potential attacks -->

View File

@@ -23,7 +23,6 @@ import org.springframework.scheduling.annotation.EnableAsync;
* *
* *
*/ */
@Configuration @Configuration
@EnableAsync @EnableAsync
@ConditionalOnMissingBean(AsyncConfigurer.class) @ConditionalOnMissingBean(AsyncConfigurer.class)

View File

@@ -20,7 +20,7 @@ public class AsyncConfigurerThreadpoolProperties {
/** /**
* Max queue size for central event executor. * Max queue size for central event executor.
*/ */
private Integer queuesize = 250; private Integer queuesize = 5_000;
/** /**
* Core processing threads for central event executor. * Core processing threads for central event executor.
@@ -30,7 +30,7 @@ public class AsyncConfigurerThreadpoolProperties {
/** /**
* Maximum thread pool size for central event executor. * Maximum thread pool size for central event executor.
*/ */
private Integer maxthreads = 50; private Integer maxthreads = 20;
/** /**
* When the number of threads is greater than the core, this is the maximum * When the number of threads is greater than the core, this is the maximum

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.autoconfigure.scheduling;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -21,6 +22,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
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.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.security.concurrent.DelegatingSecurityContextExecutor; import org.springframework.security.concurrent.DelegatingSecurityContextExecutor;
import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.common.util.concurrent.ThreadFactoryBuilder;
@@ -39,11 +43,21 @@ public class ExecutorAutoConfiguration {
private AsyncConfigurerThreadpoolProperties asyncConfigurerProperties; private AsyncConfigurerThreadpoolProperties asyncConfigurerProperties;
/** /**
* @return ExecutorService for general purpose multi threaded operations * @return ExecutorService with security context availability in thread
* execution..
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public Executor asyncExecutor() { public Executor asyncExecutor() {
return new DelegatingSecurityContextExecutor(threadPoolExecutor());
}
/**
* @return central ThreadPoolExecutor for general purpose multi threaded
* operations. Tries an orderly shutdown when destroyed.
*/
@Bean(destroyMethod = "shutdown")
public ThreadPoolExecutor threadPoolExecutor() {
final BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>( final BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(
asyncConfigurerProperties.getQueuesize()); asyncConfigurerProperties.getQueuesize());
final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(asyncConfigurerProperties.getCorethreads(), final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(asyncConfigurerProperties.getCorethreads(),
@@ -53,7 +67,8 @@ public class ExecutorAutoConfiguration {
threadPoolExecutor.setRejectedExecutionHandler((r, executor) -> LOGGER.warn( threadPoolExecutor.setRejectedExecutionHandler((r, executor) -> LOGGER.warn(
"Reject runnable for centralExecutorService, reached limit of queue size {}", "Reject runnable for centralExecutorService, reached limit of queue size {}",
executor.getQueue().size())); executor.getQueue().size()));
return new DelegatingSecurityContextExecutor(threadPoolExecutor);
return threadPoolExecutor;
} }
/** /**
@@ -69,4 +84,32 @@ public class ExecutorAutoConfiguration {
return new DelegatingSecurityContextExecutor(threadPoolExecutor); return new DelegatingSecurityContextExecutor(threadPoolExecutor);
} }
/**
* @return {@link TaskExecutor} for task execution
*/
@Bean
@ConditionalOnMissingBean
public TaskExecutor taskExecutor() {
return new ConcurrentTaskExecutor(asyncExecutor());
}
/**
* @return {@link ScheduledExecutorService} based on
* {@link #threadPoolTaskScheduler()}.
*/
@Bean
@ConditionalOnMissingBean
public ScheduledExecutorService scheduledExecutorService() {
return threadPoolTaskScheduler().getScheduledExecutor();
}
/**
* @return {@link ThreadPoolTaskScheduler} for scheduled operations.
*/
@Bean
@ConditionalOnMissingBean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
return new ThreadPoolTaskScheduler();
}
} }

View File

@@ -27,12 +27,6 @@ vaadin.servlet.urlMapping=/UI/*
vaadin.servlet.heartbeatInterval=60 vaadin.servlet.heartbeatInterval=60
vaadin.servlet.closeIdleSessions=false vaadin.servlet.closeIdleSessions=false
# Defines the thread pool executor
hawkbit.threadpool.corethreads=5
hawkbit.threadpool.maxthreads=20
hawkbit.threadpool.idletimeout=10000
hawkbit.threadpool.queuesize=20000
# Defines the polling time for the controllers in HH:MM:SS notation # Defines the polling time for the controllers in HH:MM:SS notation
hawkbit.controller.pollingTime=00:05:00 hawkbit.controller.pollingTime=00:05:00
hawkbit.controller.pollingOverdueTime=00:05:00 hawkbit.controller.pollingOverdueTime=00:05:00

View File

@@ -8,21 +8,32 @@
*/ */
package org.eclipse.hawkbit.amqp; package org.eclipse.hawkbit.amqp;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings; import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange; import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue; import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin; import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.support.RetryTemplate;;
/** /**
* The spring AMQP configuration which is enabled by using the profile * The spring AMQP configuration which is enabled by using the profile
@@ -32,14 +43,68 @@ import org.springframework.context.annotation.Bean;
@EnableConfigurationProperties({ AmqpProperties.class, AmqpDeadletterProperties.class }) @EnableConfigurationProperties({ AmqpProperties.class, AmqpDeadletterProperties.class })
public class AmqpConfiguration { public class AmqpConfiguration {
@Autowired private static final Logger LOGGER = LoggerFactory.getLogger(AmqpConfiguration.class);
protected AmqpProperties amqpProperties;
@Autowired @Autowired
protected AmqpDeadletterProperties amqpDeadletterProperties; private AmqpProperties amqpProperties;
@Autowired @Autowired
private ConnectionFactory connectionFactory; private AmqpDeadletterProperties amqpDeadletterProperties;
@Autowired
@Qualifier("threadPoolExecutor")
private ThreadPoolExecutor threadPoolExecutor;
@Autowired
private ConnectionFactory rabbitConnectionFactory;
@Configuration
protected static class RabbitConnectionFactoryCreator {
@Autowired
private AmqpProperties amqpProperties;
@Autowired
@Qualifier("threadPoolExecutor")
private ThreadPoolExecutor threadPoolExecutor;
@Autowired
private ScheduledExecutorService scheduledExecutorService;
/**
* {@link ConnectionFactory} with enabled publisher confirms and
* heartbeat.
*
* @param config
* with standard {@link RabbitProperties}
* @return {@link ConnectionFactory}
*/
@Bean
public ConnectionFactory rabbitConnectionFactory(final RabbitProperties config) {
final CachingConnectionFactory factory = new CachingConnectionFactory();
factory.setRequestedHeartBeat(amqpProperties.getRequestedHeartBeat());
factory.setExecutor(threadPoolExecutor);
factory.getRabbitConnectionFactory().setHeartbeatExecutor(scheduledExecutorService);
factory.setPublisherConfirms(true);
final String addresses = config.getAddresses();
factory.setAddresses(addresses);
if (config.getHost() != null) {
factory.setHost(config.getHost());
factory.setPort(config.getPort());
}
if (config.getUsername() != null) {
factory.setUsername(config.getUsername());
}
if (config.getPassword() != null) {
factory.setPassword(config.getPassword());
}
if (config.getVirtualHost() != null) {
factory.setVirtualHost(config.getVirtualHost());
}
return factory;
}
}
/** /**
* Create a {@link RabbitAdmin} and ignore declaration exceptions. * Create a {@link RabbitAdmin} and ignore declaration exceptions.
@@ -49,20 +114,34 @@ public class AmqpConfiguration {
*/ */
@Bean @Bean
public RabbitAdmin rabbitAdmin() { public RabbitAdmin rabbitAdmin() {
final RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory); final RabbitAdmin rabbitAdmin = new RabbitAdmin(rabbitConnectionFactory);
rabbitAdmin.setIgnoreDeclarationExceptions(true); rabbitAdmin.setIgnoreDeclarationExceptions(true);
return rabbitAdmin; return rabbitAdmin;
} }
/** /**
* Method to set the Jackson2JsonMessageConverter. * @return {@link RabbitTemplate} with automatic retry, published confirms
* * and {@link Jackson2JsonMessageConverter}.
* @return the Jackson2JsonMessageConverter
*/ */
@Bean @Bean
public RabbitTemplate rabbitTemplate() { public RabbitTemplate rabbitTemplate() {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); final RabbitTemplate rabbitTemplate = new RabbitTemplate(rabbitConnectionFactory);
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter()); rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
final RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setBackOffPolicy(new ExponentialBackOffPolicy());
rabbitTemplate.setRetryTemplate(retryTemplate);
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
if (ack) {
LOGGER.debug("Message with correlation ID {} confirmed by broker.", correlationData.getId());
} else {
LOGGER.error("Broker is unable to handle message with correlation ID {} : {}", correlationData.getId(),
cause);
}
});
return rabbitTemplate; return rabbitTemplate;
} }
@@ -159,7 +238,7 @@ public class AmqpConfiguration {
public SimpleRabbitListenerContainerFactory listenerContainerFactory() { public SimpleRabbitListenerContainerFactory listenerContainerFactory() {
final SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory(); final SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
containerFactory.setDefaultRequeueRejected(false); containerFactory.setDefaultRequeueRejected(false);
containerFactory.setConnectionFactory(connectionFactory); containerFactory.setConnectionFactory(rabbitConnectionFactory);
containerFactory.setMissingQueuesFatal(amqpProperties.isMissingQueuesFatal()); containerFactory.setMissingQueuesFatal(amqpProperties.isMissingQueuesFatal());
return containerFactory; return containerFactory;
} }

View File

@@ -8,6 +8,8 @@
*/ */
package org.eclipse.hawkbit.amqp; package org.eclipse.hawkbit.amqp;
import java.util.concurrent.TimeUnit;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -38,6 +40,11 @@ public class AmqpProperties {
*/ */
private boolean missingQueuesFatal = false; private boolean missingQueuesFatal = false;
/**
* Requested heartbeat interval from broker in {@link TimeUnit#SECONDS}.
*/
private int requestedHeartBeat = (int) TimeUnit.SECONDS.toSeconds(60);
/** /**
* Is missingQueuesFatal enabled * Is missingQueuesFatal enabled
* *
@@ -102,4 +109,13 @@ public class AmqpProperties {
public void setReceiverQueue(final String receiverQueue) { public void setReceiverQueue(final String receiverQueue) {
this.receiverQueue = receiverQueue; this.receiverQueue = receiverQueue;
} }
public int getRequestedHeartBeat() {
return requestedHeartBeat;
}
public void setRequestedHeartBeat(final int requestedHeartBeat) {
this.requestedHeartBeat = requestedHeartBeat;
}
} }

View File

@@ -109,7 +109,7 @@ public class BaseAmqpService {
} }
protected final void logAndThrowMessageError(final Message message, final String error) { protected final void logAndThrowMessageError(final Message message, final String error) {
LOGGER.warn("Error \"{}\" reported by message: {}", error, message); LOGGER.warn("Warning! \"{}\" reported by message: {}", error, message);
throw new IllegalArgumentException(error); throw new IllegalArgumentException(error);
} }

View File

@@ -9,10 +9,14 @@
package org.eclipse.hawkbit.amqp; package org.eclipse.hawkbit.amqp;
import java.net.URI; import java.net.URI;
import java.util.UUID;
import org.eclipse.hawkbit.util.IpUtil; import org.eclipse.hawkbit.util.IpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message; import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
/** /**
* A default implementation for the sender service. The service sends all amqp * A default implementation for the sender service. The service sends all amqp
@@ -20,6 +24,7 @@ import org.springframework.amqp.rabbit.core.RabbitTemplate;
* extracted from the uri. * extracted from the uri.
*/ */
public class DefaultAmqpSenderService implements AmqpSenderService { public class DefaultAmqpSenderService implements AmqpSenderService {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultAmqpSenderService.class);
private final RabbitTemplate internalAmqpTemplate; private final RabbitTemplate internalAmqpTemplate;
@@ -39,7 +44,16 @@ public class DefaultAmqpSenderService implements AmqpSenderService {
return; return;
} }
internalAmqpTemplate.send(extractExchange(replyTo), null, message); final String correlationId = UUID.randomUUID().toString();
final String exchange = extractExchange(replyTo);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Sending message {} to exchange {} with correlationId {}", message, exchange, correlationId);
} else {
LOGGER.debug("Sending message to exchange {} with correlationId {}", exchange, correlationId);
}
internalAmqpTemplate.send(exchange, null, message, new CorrelationData(correlationId));
} }
} }

View File

@@ -8,6 +8,14 @@
*/ */
package org.eclipse.hawkbit; package org.eclipse.hawkbit;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.eclipse.hawkbit.amqp.AmqpProperties;
import org.eclipse.hawkbit.amqp.AmqpSenderService; import org.eclipse.hawkbit.amqp.AmqpSenderService;
import org.eclipse.hawkbit.amqp.DefaultAmqpSenderService; import org.eclipse.hawkbit.amqp.DefaultAmqpSenderService;
import org.eclipse.hawkbit.repository.jpa.model.helper.SystemSecurityContextHolder; import org.eclipse.hawkbit.repository.jpa.model.helper.SystemSecurityContextHolder;
@@ -16,13 +24,22 @@ import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
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.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.security.concurrent.DelegatingSecurityContextExecutor;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
/** /**
* *
*/ */
@Configuration @Configuration
@EnableConfigurationProperties({ AmqpProperties.class })
public class AmqpTestConfiguration { public class AmqpTestConfiguration {
/** /**
* @return the {@link SystemSecurityContext} singleton bean which make it * @return the {@link SystemSecurityContext} singleton bean which make it
@@ -56,4 +73,55 @@ public class AmqpTestConfiguration {
public AmqpSenderService amqpSenderServiceBean(final RabbitTemplate rabbitTemplate) { public AmqpSenderService amqpSenderServiceBean(final RabbitTemplate rabbitTemplate) {
return new DefaultAmqpSenderService(rabbitTemplate); return new DefaultAmqpSenderService(rabbitTemplate);
} }
/**
* @return ExecutorService with security context availability in thread
* execution..
*/
@Bean
@ConditionalOnMissingBean
public Executor asyncExecutor() {
return new DelegatingSecurityContextExecutor(threadPoolExecutor());
}
/**
* @return central ThreadPoolExecutor for general purpose multi threaded
* operations. Tries an orderly shutdown when destroyed.
*/
@Bean(destroyMethod = "shutdown")
public ThreadPoolExecutor threadPoolExecutor() {
final BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(10);
final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 10, 1000, TimeUnit.MILLISECONDS,
blockingQueue, new ThreadFactoryBuilder().setNameFormat("central-executor-pool-%d").build());
return threadPoolExecutor;
}
/**
* @return {@link TaskExecutor} for task execution
*/
@Bean
@ConditionalOnMissingBean
public TaskExecutor taskExecutor() {
return new ConcurrentTaskExecutor(asyncExecutor());
}
/**
* @return {@link ScheduledExecutorService} based on
* {@link #threadPoolTaskScheduler()}.
*/
@Bean
@ConditionalOnMissingBean
public ScheduledExecutorService scheduledExecutorService() {
return threadPoolTaskScheduler().getScheduledExecutor();
}
/**
* @return {@link ThreadPoolTaskScheduler} for scheduled operations.
*/
@Bean
@ConditionalOnMissingBean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
return new ThreadPoolTaskScheduler();
}
} }

View File

@@ -33,6 +33,7 @@ import ru.yandex.qatools.allure.annotations.Stories;
@Stories("Test to generate the artifact download URL") @Stories("Test to generate the artifact download URL")
@SpringApplicationConfiguration(classes = { AmqpTestConfiguration.class, @SpringApplicationConfiguration(classes = { AmqpTestConfiguration.class,
org.eclipse.hawkbit.RepositoryApplicationConfiguration.class }) org.eclipse.hawkbit.RepositoryApplicationConfiguration.class })
public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTestWithMongoDB { public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTestWithMongoDB {
private static final String HTTPS_LOCALHOST = "https://localhost:8080/"; private static final String HTTPS_LOCALHOST = "https://localhost:8080/";

View File

@@ -33,7 +33,7 @@ public final class Constants {
* generated by repository for every new account for "Firmware/Operating * generated by repository for every new account for "Firmware/Operating
* System" . * System" .
*/ */
public static final String SMT_DEFAULT_OS_NAME = "Firmware"; public static final String SMT_DEFAULT_OS_NAME = "OS";
/** /**
* {@link SoftwareModuleType#getName()} of a {@link SoftwareModuleType} * {@link SoftwareModuleType#getName()} of a {@link SoftwareModuleType}
* generated by repository for every new account for "applications/apps". * generated by repository for every new account for "applications/apps".

View File

@@ -16,7 +16,9 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Tag; import org.eclipse.hawkbit.repository.model.Tag;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
@@ -82,15 +84,26 @@ public interface DistributionSetRepository
List<DistributionSet> findByModules(JpaSoftwareModule module); List<DistributionSet> findByModules(JpaSoftwareModule module);
/** /**
* Finds {@link DistributionSet}s based on given ID if they are not assigned * Finds {@link DistributionSet}s based on given ID that are assigned yet to
* yet to an {@link UpdateAction}, i.e. unused. * an {@link Action}, i.e. in use.
* *
* @param ids * @param ids
* to search for * to search for
* @return * @return list of {@link DistributionSet#getId()}
*/ */
@Query("select ac.distributionSet.id from JpaAction ac where ac.distributionSet.id in :ids") @Query("select ac.distributionSet.id from JpaAction ac where ac.distributionSet.id in :ids")
List<Long> findAssignedDistributionSetsById(@Param("ids") Long... ids); List<Long> findAssignedToTargetDistributionSetsById(@Param("ids") Long... ids);
/**
* Finds {@link DistributionSet}s based on given ID that are assigned yet to
* an {@link Rollout}, i.e. in use.
*
* @param ids
* to search for
* @return list of {@link DistributionSet#getId()}
*/
@Query("select ra.distributionSet.id from JpaRollout ra where ra.distributionSet.id in :ids")
List<Long> findAssignedToRolloutDistributionSetsById(@Param("ids") Long... ids);
/** /**
* Saves all given {@link DistributionSet}s. * Saves all given {@link DistributionSet}s.

View File

@@ -173,7 +173,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
public void deleteDistributionSet(final Long... distributionSetIDs) { public void deleteDistributionSet(final Long... distributionSetIDs) {
final List<Long> toHardDelete = new ArrayList<>(); final List<Long> toHardDelete = new ArrayList<>();
final List<Long> assigned = distributionSetRepository.findAssignedDistributionSetsById(distributionSetIDs); final List<Long> assigned = distributionSetRepository
.findAssignedToTargetDistributionSetsById(distributionSetIDs);
assigned.addAll(distributionSetRepository.findAssignedToRolloutDistributionSetsById(distributionSetIDs));
// soft delete assigned // soft delete assigned
if (!assigned.isEmpty()) { if (!assigned.isEmpty()) {

View File

@@ -27,6 +27,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
@@ -34,12 +35,18 @@ import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetFilter.DistributionSetFilterBuilder; import org.eclipse.hawkbit.repository.model.DistributionSetFilter.DistributionSetFilterBuilder;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata; import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DistributionSetTag; import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorAction;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorCondition;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
import org.eclipse.hawkbit.repository.model.RolloutGroupConditionBuilder;
import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.fest.assertions.core.Condition; import org.fest.assertions.core.Condition;
import org.junit.Test; import org.junit.Test;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
@@ -777,36 +784,55 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
} }
@Test @Test
@Description("Deltes a DS that is no in use. Expected behaviour is a soft delete on the database, i.e. only marked as " @Description("Deletes a DS that is in use by either target assignment or rollout. Expected behaviour is a soft delete on the database, i.e. only marked as "
+ "deleted, kept eas refernce and unavailable for future use..") + "deleted, kept as reference but unavailable for future use..")
public void deleteAssignedDistributionSet() { public void deleteAssignedDistributionSet() {
DistributionSet ds1 = testdataFactory.createDistributionSet("ds-1"); DistributionSet ds1 = testdataFactory.createDistributionSet("ds-1");
DistributionSet ds2 = testdataFactory.createDistributionSet("ds-2"); DistributionSet ds2 = testdataFactory.createDistributionSet("ds-2");
DistributionSet dsAssigned = testdataFactory.createDistributionSet("ds-3"); DistributionSet dsToTargetAssigned = testdataFactory.createDistributionSet("ds-3");
final DistributionSet dsToRolloutAssigned = testdataFactory.createDistributionSet("ds-4");
ds1 = distributionSetManagement.findDistributionSetByNameAndVersion(ds1.getName(), ds1.getVersion()); ds1 = distributionSetManagement.findDistributionSetByNameAndVersion(ds1.getName(), ds1.getVersion());
ds2 = distributionSetManagement.findDistributionSetByNameAndVersion(ds2.getName(), ds2.getVersion()); ds2 = distributionSetManagement.findDistributionSetByNameAndVersion(ds2.getName(), ds2.getVersion());
// create assigned DS // create assigned DS
dsAssigned = distributionSetManagement.findDistributionSetByNameAndVersion(dsAssigned.getName(), dsToTargetAssigned = distributionSetManagement.findDistributionSetByNameAndVersion(dsToTargetAssigned.getName(),
dsAssigned.getVersion()); dsToTargetAssigned.getVersion());
final Target target = new JpaTarget("4712"); final Target target = new JpaTarget("4712");
final Target savedTarget = targetManagement.createTarget(target); final Target savedTarget = targetManagement.createTarget(target);
final List<Target> toAssign = new ArrayList<>(); final List<Target> toAssign = new ArrayList<>();
toAssign.add(savedTarget); toAssign.add(savedTarget);
deploymentManagement.assignDistributionSet(dsAssigned, toAssign); deploymentManagement.assignDistributionSet(dsToTargetAssigned, toAssign);
// delete a ds // create assigned rollout
assertThat(distributionSetRepository.findAll()).hasSize(3); createRolloutByVariables("test", "test", 5, "name==*", dsToRolloutAssigned, "50", "5");
distributionSetManagement.deleteDistributionSet(dsAssigned.getId());
// delete assigned ds
assertThat(distributionSetRepository.findAll()).hasSize(4);
distributionSetManagement.deleteDistributionSet(dsToTargetAssigned.getId(), dsToRolloutAssigned.getId());
// not assigned so not marked as deleted // not assigned so not marked as deleted
assertThat(distributionSetRepository.findAll()).hasSize(3); assertThat(distributionSetRepository.findAll()).hasSize(4);
assertThat(distributionSetManagement assertThat(distributionSetManagement
.findDistributionSetsByDeletedAndOrCompleted(pageReq, Boolean.FALSE, Boolean.TRUE).getTotalElements()) .findDistributionSetsByDeletedAndOrCompleted(pageReq, Boolean.FALSE, Boolean.TRUE).getTotalElements())
.isEqualTo(2); .isEqualTo(2);
} }
private Rollout createRolloutByVariables(final String rolloutName, final String rolloutDescription,
final int groupSize, final String filterQuery, final DistributionSet distributionSet,
final String successCondition, final String errorCondition) {
final RolloutGroupConditions conditions = new RolloutGroupConditionBuilder()
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, successCondition)
.errorCondition(RolloutGroupErrorCondition.THRESHOLD, errorCondition)
.errorAction(RolloutGroupErrorAction.PAUSE, null).build();
final Rollout rolloutToCreate = new JpaRollout();
rolloutToCreate.setName(rolloutName);
rolloutToCreate.setDescription(rolloutDescription);
rolloutToCreate.setTargetFilterQuery(filterQuery);
rolloutToCreate.setDistributionSet(distributionSet);
return rolloutManagement.createRollout(rolloutToCreate, groupSize, conditions);
}
private Target sendUpdateActionStatusToTarget(final Status status, final Action updActA, final Target t, private Target sendUpdateActionStatusToTarget(final Status status, final Action updActA, final Target t,
final String... msgs) { final String... msgs) {
updActA.setStatus(status); updActA.setStatus(status);

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa.rsql;
import static org.fest.assertions.api.Assertions.assertThat; import static org.fest.assertions.api.Assertions.assertThat;
import org.eclipse.hawkbit.repository.Constants;
import org.eclipse.hawkbit.repository.SoftwareModuleTypeFields; import org.eclipse.hawkbit.repository.SoftwareModuleTypeFields;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest; import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
@@ -34,7 +35,7 @@ public class RSQLSoftwareModuleTypeFieldsTest extends AbstractJpaIntegrationTest
@Test @Test
@Description("Test filter software module test type by name") @Description("Test filter software module test type by name")
public void testFilterByParameterName() { public void testFilterByParameterName() {
assertRSQLQuery(SoftwareModuleTypeFields.NAME.name() + "==Firmware", 1); assertRSQLQuery(SoftwareModuleTypeFields.NAME.name() + "==" + Constants.SMT_DEFAULT_OS_NAME, 1);
} }
@Test @Test

View File

@@ -26,11 +26,16 @@ public class DistributionSetTypeEvent {
private final DistributionSetTypeEnum distributionSetTypeEnum; private final DistributionSetTypeEnum distributionSetTypeEnum;
private String distributionSetTypeName;
/** /**
* @param distributionSetTypeEnum * @param distributionSetTypeEnum
* @param distributionSetTypeName
*/ */
public DistributionSetTypeEvent(final DistributionSetTypeEnum distributionSetTypeEnum) { public DistributionSetTypeEvent(final DistributionSetTypeEnum distributionSetTypeEnum,
final String distributionSetTypeName) {
this.distributionSetTypeEnum = distributionSetTypeEnum; this.distributionSetTypeEnum = distributionSetTypeEnum;
this.distributionSetTypeName = distributionSetTypeName;
} }
/** /**
@@ -43,6 +48,14 @@ public class DistributionSetTypeEvent {
this.distributionSetType = distributionSetType; this.distributionSetType = distributionSetType;
} }
public String getDistributionSetTypeName() {
return distributionSetTypeName;
}
public void setDistributionSetTypeName(final String distributionSetTypeName) {
this.distributionSetTypeName = distributionSetTypeName;
}
public DistributionSetType getDistributionSetType() { public DistributionSetType getDistributionSetType() {
return distributionSetType; return distributionSetType;
} }

View File

@@ -69,6 +69,7 @@
<jackson.version>2.5.5</jackson.version> <jackson.version>2.5.5</jackson.version>
<hibernate-validator.version>5.2.4.Final</hibernate-validator.version> <hibernate-validator.version>5.2.4.Final</hibernate-validator.version>
<spring-cloud-connectors.version>1.2.0.RELEASE</spring-cloud-connectors.version> <spring-cloud-connectors.version>1.2.0.RELEASE</spring-cloud-connectors.version>
<spring-amqp.version>1.6.0.RELEASE</spring-amqp.version>
<spring-hateoas.version>0.18.0.RELEASE</spring-hateoas.version> <spring-hateoas.version>0.18.0.RELEASE</spring-hateoas.version>
<!-- Support for MongoDB 3 --> <!-- Support for MongoDB 3 -->
<spring-data-releasetrain.version>Fowler-SR1</spring-data-releasetrain.version> <spring-data-releasetrain.version>Fowler-SR1</spring-data-releasetrain.version>