Merge branch 'master' into feature_enable_push_in_deployment_view

Conflicts:
	hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java
This commit is contained in:
gah6kor
2016-07-19 16:09:47 +02:00
90 changed files with 1677 additions and 1538 deletions

View File

@@ -10,8 +10,6 @@ package org.eclipse.hawkbit.simulator;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.DigestOutputStream;
import java.security.KeyManagementException;
@@ -34,6 +32,7 @@ import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.eclipse.hawkbit.dmf.json.model.Artifact;
import org.eclipse.hawkbit.dmf.json.model.Artifact.UrlProtocol;
import org.eclipse.hawkbit.dmf.json.model.SoftwareModule;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
import org.eclipse.hawkbit.simulator.UpdateStatus.ResponseStatus;
@@ -59,7 +58,7 @@ import com.google.common.io.ByteStreams;
public class DeviceSimulatorUpdater {
private static final Logger LOGGER = LoggerFactory.getLogger(DeviceSimulatorUpdater.class);
private static final ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(4);
private static final ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(8);
@Autowired
private SpSenderService spSenderService;
@@ -198,18 +197,14 @@ public class DeviceSimulatorUpdater {
private static void handleArtifacts(final String targetToken, final List<UpdateStatus> status,
final Artifact artifact) {
artifact.getUrls().entrySet().forEach(entry -> {
switch (entry.getKey()) {
case HTTP:
case HTTPS:
status.add(downloadUrl(entry.getValue(), targetToken, artifact.getHashes().getSha1(),
artifact.getSize()));
break;
default:
// not supported yet
break;
}
});
if (artifact.getUrls().containsKey(UrlProtocol.HTTPS)) {
status.add(downloadUrl(artifact.getUrls().get(UrlProtocol.HTTPS), targetToken,
artifact.getHashes().getSha1(), artifact.getSize()));
} else if (artifact.getUrls().containsKey(UrlProtocol.HTTP)) {
status.add(downloadUrl(artifact.getUrls().get(UrlProtocol.HTTP), targetToken,
artifact.getHashes().getSha1(), artifact.getSize()));
}
}
private static UpdateStatus downloadUrl(final String url, final String targetToken, final String sha1Hash,
@@ -236,22 +231,15 @@ public class DeviceSimulatorUpdater {
return new UpdateStatus(ResponseStatus.ERROR, message);
}
final File tempFile = File.createTempFile("uploadFile", null);
// Exception squid:S2070 - not used for hashing sensitive
// data
@SuppressWarnings("squid:S2070")
final MessageDigest md = MessageDigest.getInstance("SHA-1");
try (final DigestOutputStream dos = new DigestOutputStream(new FileOutputStream(tempFile), md)) {
try (final BufferedOutputStream bdos = new BufferedOutputStream(dos)) {
try (BufferedInputStream bis = new BufferedInputStream(response.getEntity().getContent())) {
overallread = ByteStreams.copy(bis, bdos);
}
}
} finally {
if (tempFile != null && !tempFile.delete()) {
LOGGER.error("Could not delete temporary file: {}", tempFile);
try (final BufferedOutputStream bdos = new BufferedOutputStream(
new DigestOutputStream(ByteStreams.nullOutputStream(), md))) {
try (BufferedInputStream bis = new BufferedInputStream(response.getEntity().getContent())) {
overallread = ByteStreams.copy(bis, bdos);
}
}

View File

@@ -25,6 +25,7 @@ import org.springframework.cache.interceptor.CacheOperationInvocationContext;
import org.springframework.cache.interceptor.SimpleCacheResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
/**
* A configuration for configuring the spring {@link CacheManager} for specific
@@ -33,9 +34,6 @@ import org.springframework.context.annotation.Configuration;
*
* This is done by providing a special {@link TenantCacheResolver} which
* generates a cache name included the current tenant.
*
*
*
*/
@Configuration
@EnableCaching
@@ -51,18 +49,27 @@ public class CacheAutoConfiguration extends CachingConfigurerSupport {
@Override
@Bean
@ConditionalOnMissingBean
@Primary
public TenancyCacheManager cacheManager() {
return new TenantAwareCacheManager(new GuavaCacheManager(), tenantAware);
return new TenantAwareCacheManager(directCacheManager(), tenantAware);
}
/**
* @return the direct cache manager to access without tenant aware check,
* cause in sometimes it's necessary to access the cache directly
* without having the current tenant, e.g. initial creation of
* tenant
*/
@Bean(name = "directCacheManager")
@ConditionalOnMissingBean(name = "directCacheManager")
public CacheManager directCacheManager() {
return new GuavaCacheManager();
}
/**
* A {@link SimpleCacheResolver} implementation which includes the
* {@link TenantAware#getCurrentTenant()} into the cache name before
* resolving it.
*
*
*
*
*/
public class TenantCacheResolver extends SimpleCacheResolver {

View File

@@ -31,7 +31,7 @@ public class DownloadIdCacheAutoConfiguration {
private CacheManager cacheManager;
/**
* Bean for the downlod id cache.
* Bean for the download id cache.
*
* @return the cache
*/

View File

@@ -45,6 +45,12 @@
<!-- Test -->
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-repository-api</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>

View File

@@ -15,6 +15,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
@@ -50,8 +51,14 @@ public class RedisConfiguration {
* @return the spring redis cache manager.
*/
@Bean
@Primary
public CacheManager cacheManager() {
return new TenantAwareCacheManager(new RedisCacheManager(redisTemplate()), tenantAware);
return new TenantAwareCacheManager(directCacheManager(), tenantAware);
}
@Bean(name = "directCacheManager")
public CacheManager directCacheManager() {
return new RedisCacheManager(redisTemplate());
}
/**

View File

@@ -16,8 +16,8 @@ import static org.mockito.Mockito.verify;
import java.util.Collection;
import org.eclipse.hawkbit.eventbus.event.DownloadProgressEvent;
import org.eclipse.hawkbit.eventbus.event.EntityEvent;
import org.eclipse.hawkbit.repository.eventbus.event.DownloadProgressEvent;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -56,7 +56,7 @@ public class EventDistributorTest {
@Test
public void distributeDistributedEventSendsToRedis() {
final DownloadProgressEvent event = new DownloadProgressEvent("tenant", 123L, 10);
final DownloadProgressEvent event = new DownloadProgressEvent("tenant", 123L, 500L, 100L, 200L);
underTest.distribute(event);
// origin node ID should be set by distributing the event
@@ -67,7 +67,7 @@ public class EventDistributorTest {
@Test
public void dontDistributeDistributedEventIfSameNode() {
final String knownNodeId = EventDistributor.getNodeId();
final DownloadProgressEvent event = new DownloadProgressEvent("tenant", 123L, 10);
final DownloadProgressEvent event = new DownloadProgressEvent("tenant", 123L, 500L, 100L, 200L);
event.setNodeId(knownNodeId);
// test
@@ -79,7 +79,7 @@ public class EventDistributorTest {
@Test
public void handleDistributedMessageFromRedis() {
final DownloadProgressEvent event = new DownloadProgressEvent("tenant", 123L, 10);
final DownloadProgressEvent event = new DownloadProgressEvent("tenant", 123L, 500L, 100L, 200L);
final String knownChannel = "someChannel";
underTest.handleMessage(event, knownChannel);
@@ -90,7 +90,7 @@ public class EventDistributorTest {
@Test
public void handleDistributedMessageFilteredIfSameNodeId() {
final DownloadProgressEvent event = new DownloadProgressEvent("tenant", 123L, 10);
final DownloadProgressEvent event = new DownloadProgressEvent("tenant", 123L, 500L, 100L, 200L);
final String knownChannel = "someChannel";
event.setOriginNodeId(EventDistributor.getNodeId());

View File

@@ -1,57 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.eventbus.event;
/**
* Event that contains an updated download progress for a given Action.
*
*
*
*
*/
public class DownloadProgressEvent extends AbstractDistributedEvent {
private static final long serialVersionUID = 1L;
private final Long statusId;
private final int progressPercent;
/**
* Constructor.
*
* @param tenant
* the tenant for this event
* @param statusId
* of {@link UpdateActionStatus}
* @param progressPercent
* number (1-100)
*/
public DownloadProgressEvent(final String tenant, final Long statusId, final int progressPercent) {
// the revision of the DownloadProgressEvent is just equal the
// progressPercentage due the
// percentage is going from 0 to 100.
super(statusId, tenant);
this.statusId = statusId;
this.progressPercent = progressPercent;
}
/**
* @return the statusId
*/
public Long getStatusId() {
return statusId;
}
/**
* @return the progressPercent
*/
public int getProgressPercent() {
return progressPercent;
}
}

View File

@@ -93,12 +93,12 @@ public class DdiArtifactStoreController implements DdiDlArtifactStoreControllerR
// we set a download status only if we are aware of the
// targetid, i.e. authenticated and not anonymous
if (targetid != null && !"anonymous".equals(targetid)) {
final Action action = checkAndReportDownloadByTarget(
final ActionStatus actionStatus = checkAndReportDownloadByTarget(
requestResponseContextHolder.getHttpServletRequest(), targetid, artifact);
result = RestResourceConversionHelper.writeFileResponse(artifact,
requestResponseContextHolder.getHttpServletResponse(),
requestResponseContextHolder.getHttpServletRequest(), file, controllerManagement,
action.getId());
actionStatus.getId());
} else {
result = RestResourceConversionHelper.writeFileResponse(artifact,
requestResponseContextHolder.getHttpServletResponse(),
@@ -131,7 +131,7 @@ public class DdiArtifactStoreController implements DdiDlArtifactStoreControllerR
return new ResponseEntity<>(HttpStatus.OK);
}
private Action checkAndReportDownloadByTarget(final HttpServletRequest request, final String targetid,
private ActionStatus checkAndReportDownloadByTarget(final HttpServletRequest request, final String targetid,
final LocalArtifact artifact) {
final Target target = controllerManagement.updateLastTargetQuery(targetid,
IpUtil.getClientIpFromRequest(request, securityProperties));
@@ -152,8 +152,8 @@ public class DdiArtifactStoreController implements DdiDlArtifactStoreControllerR
actionStatus.addMessage(
RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target downloads: " + request.getRequestURI());
}
controllerManagement.addInformationalActionStatus(actionStatus);
return action;
return controllerManagement.addInformationalActionStatus(actionStatus);
}
}

View File

@@ -156,8 +156,8 @@ public class DdiRootController implements DdiRootControllerRestApi {
if (ifMatch != null && !RestResourceConversionHelper.matchesHttpHeader(ifMatch, artifact.getSha1Hash())) {
result = new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED);
} else {
final Action action = checkAndLogDownload(requestResponseContextHolder.getHttpServletRequest(), target,
module);
final ActionStatus action = checkAndLogDownload(requestResponseContextHolder.getHttpServletRequest(),
target, module);
result = RestResourceConversionHelper.writeFileResponse(artifact,
requestResponseContextHolder.getHttpServletResponse(),
requestResponseContextHolder.getHttpServletRequest(), file, controllerManagement,
@@ -167,7 +167,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
return result;
}
private Action checkAndLogDownload(final HttpServletRequest request, final Target target,
private ActionStatus checkAndLogDownload(final HttpServletRequest request, final Target target,
final SoftwareModule module) {
final Action action = controllerManagement
.getActionForDownloadByTargetAndSoftwareModule(target.getControllerId(), module);
@@ -185,8 +185,8 @@ public class DdiRootController implements DdiRootControllerRestApi {
statusMessage.addMessage(
RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target downloads " + request.getRequestURI());
}
controllerManagement.addInformationalActionStatus(statusMessage);
return action;
return controllerManagement.addInformationalActionStatus(statusMessage);
}
private static boolean checkModule(final String fileName, final SoftwareModule module) {

View File

@@ -26,14 +26,14 @@ import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.RandomUtils;
import org.eclipse.hawkbit.eventbus.event.DownloadProgressEvent;
import org.eclipse.hawkbit.repository.eventbus.event.DownloadProgressEvent;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB;
import org.junit.Test;
import org.slf4j.LoggerFactory;
@@ -59,11 +59,15 @@ import ru.yandex.qatools.allure.annotations.Stories;
@Stories("Artifact Download Resource")
public class DdiArtifactDownloadTest extends AbstractRestIntegrationTestWithMongoDB {
private static final int ARTIFACT_SIZE = 5 * 1024 * 1024;
public DdiArtifactDownloadTest() {
LOG = LoggerFactory.getLogger(DdiArtifactDownloadTest.class);
}
private volatile int downLoadProgress = 0;
private volatile long shippedBytes = 0;
private volatile long shippedBytesTotal = 0;
@Autowired
private EventBus eventBus;
@@ -236,6 +240,8 @@ public class DdiArtifactDownloadTest extends AbstractRestIntegrationTestWithMong
@Description("Tests valid downloads through the artifact resource by identifying the artifact not by ID but file name.")
public void downloadArtifactThroughFileName() throws Exception {
downLoadProgress = 1;
shippedBytes = 0;
shippedBytesTotal = 0;
eventBus.register(this);
assertThat(softwareManagement.findSoftwareModulesAll(pageReq)).hasSize(0);
@@ -249,7 +255,7 @@ public class DdiArtifactDownloadTest extends AbstractRestIntegrationTestWithMong
final DistributionSet ds = testdataFactory.createDistributionSet("");
// create artifact
final byte random[] = RandomUtils.nextBytes(5 * 1024 * 1024);
final byte random[] = RandomUtils.nextBytes(ARTIFACT_SIZE);
final LocalArtifact artifact = artifactManagement.createLocalArtifact(new ByteArrayInputStream(random),
ds.findFirstModuleByType(osType).getId(), "file1", false);
@@ -276,6 +282,7 @@ public class DdiArtifactDownloadTest extends AbstractRestIntegrationTestWithMong
// download complete
assertThat(downLoadProgress).isEqualTo(10);
assertThat(shippedBytes).isEqualTo(shippedBytesTotal).isEqualTo(ARTIFACT_SIZE);
}
@Test
@@ -313,35 +320,8 @@ public class DdiArtifactDownloadTest extends AbstractRestIntegrationTestWithMong
+ "anonymous as authorization is notpossible, e.g. chekc if the controller has the artifact assigned.")
public void downloadArtifactByNameFailsIfNotAuthenticated() throws Exception {
downLoadProgress = 1;
eventBus.register(this);
assertThat(softwareManagement.findSoftwareModulesAll(pageReq)).hasSize(0);
// create target
Target target = entityFactory.generateTarget("4712");
target = targetManagement.createTarget(target);
final List<Target> targets = new ArrayList();
targets.add(target);
// create ds
final DistributionSet ds = testdataFactory.createDistributionSet("");
// create artifact
final byte random[] = RandomUtils.nextBytes(5 * 1024);
final Artifact artifact = artifactManagement.createLocalArtifact(new ByteArrayInputStream(random),
ds.findFirstModuleByType(osType).getId(), "file1.tar.bz2", false);
// download fails as artifact is not yet assigned to target
deploymentManagement.assignDistributionSet(ds, targets);
mvc.perform(get("/controller/artifacts/v1/filename/{filename}", "file1.tar.bz2"))
.andExpect(status().isNotFound());
}
@Test
@WithUser(principal = "4712", authorities = "ROLE_CONTROLLER", allSpPermissions = true)
@Description("Ensures that an authenticated and named controller is permitted to download.")
public void downloadArtifactByNameByNamedController() throws Exception {
downLoadProgress = 1;
shippedBytes = 0;
shippedBytesTotal = 0;
eventBus.register(this);
assertThat(softwareManagement.findSoftwareModulesAll(pageReq)).hasSize(0);
@@ -356,7 +336,41 @@ public class DdiArtifactDownloadTest extends AbstractRestIntegrationTestWithMong
final DistributionSet ds = testdataFactory.createDistributionSet("");
// create artifact
final byte random[] = RandomUtils.nextBytes(5 * 1024 * 1024);
final byte random[] = RandomUtils.nextBytes(ARTIFACT_SIZE);
artifactManagement.createLocalArtifact(new ByteArrayInputStream(random),
ds.findFirstModuleByType(osType).getId(), "file1.tar.bz2", false);
// download fails as artifact is not yet assigned to target
deploymentManagement.assignDistributionSet(ds, targets);
mvc.perform(get("/controller/artifacts/v1/filename/{filename}", "file1.tar.bz2"))
.andExpect(status().isNotFound());
assertThat(downLoadProgress).isEqualTo(1);
assertThat(shippedBytes).isEqualTo(shippedBytesTotal).isEqualTo(0L);
}
@Test
@WithUser(principal = "4712", authorities = "ROLE_CONTROLLER", allSpPermissions = true)
@Description("Ensures that an authenticated and named controller is permitted to download.")
public void downloadArtifactByNameByNamedController() throws Exception {
downLoadProgress = 1;
shippedBytes = 0;
shippedBytesTotal = 0;
eventBus.register(this);
assertThat(softwareManagement.findSoftwareModulesAll(pageReq)).hasSize(0);
// create target
Target target = entityFactory.generateTarget("4712");
target = targetManagement.createTarget(target);
final List<Target> targets = new ArrayList<>();
targets.add(target);
// create ds
final DistributionSet ds = testdataFactory.createDistributionSet("");
// create artifact
final byte random[] = RandomUtils.nextBytes(ARTIFACT_SIZE);
final Artifact artifact = artifactManagement.createLocalArtifact(new ByteArrayInputStream(random),
ds.findFirstModuleByType(osType).getId(), "file1", false);
@@ -389,6 +403,7 @@ public class DdiArtifactDownloadTest extends AbstractRestIntegrationTestWithMong
// download complete
assertThat(downLoadProgress).isEqualTo(10);
assertThat(shippedBytes).isEqualTo(shippedBytesTotal).isEqualTo(ARTIFACT_SIZE);
}
@Test
@@ -550,5 +565,8 @@ public class DdiArtifactDownloadTest extends AbstractRestIntegrationTestWithMong
@Subscribe
public void listen(final DownloadProgressEvent event) {
downLoadProgress++;
shippedBytes += event.getShippedBytesSinceLast();
shippedBytesTotal = event.getShippedBytesOverall();
}
}

View File

@@ -22,11 +22,11 @@ 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.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
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.RabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
@@ -268,15 +268,8 @@ public class AmqpConfiguration {
* AMQP messages
*/
@Bean(name = { "listenerContainerFactory" })
public SimpleRabbitListenerContainerFactory listenerContainerFactory() {
final SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
containerFactory.setDefaultRequeueRejected(true);
containerFactory.setConnectionFactory(rabbitConnectionFactory);
containerFactory.setMissingQueuesFatal(amqpProperties.isMissingQueuesFatal());
containerFactory.setConcurrentConsumers(amqpProperties.getInitialConcurrentConsumers());
containerFactory.setMaxConcurrentConsumers(amqpProperties.getMaxConcurrentConsumers());
containerFactory.setPrefetchCount(amqpProperties.getPrefetchCount());
return containerFactory;
public RabbitListenerContainerFactory<SimpleMessageListenerContainer> listenerContainerFactory() {
return new ConfigurableRabbitListenerContainerFactory(amqpProperties, rabbitConnectionFactory);
}
private static Map<String, Object> getTTLMaxArgsAuthenticationQueue() {

View File

@@ -138,9 +138,18 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
}
@RabbitListener(queues = "${hawkbit.dmf.rabbitmq.authenticationReceiverQueue}", containerFactory = "listenerContainerFactory")
public Message onAuthenticationRequest(final Message message,
@Header(MessageHeaderKey.TENANT) final String tenant) {
return onAuthenticationRequest(message);
public Message onAuthenticationRequest(final Message message) {
checkContentTypeJson(message);
final SecurityContext oldContext = SecurityContextHolder.getContext();
try {
return handleAuthentifiactionMessage(message);
} catch (final IllegalArgumentException ex) {
throw new AmqpRejectAndDontRequeueException("Invalid message!", ex);
} catch (final TenantNotExistException teex) {
throw new AmqpRejectAndDontRequeueException(teex);
} finally {
SecurityContextHolder.setContext(oldContext);
}
}
public Message onMessage(final Message message, final String type, final String tenant, final String virtualHost) {
@@ -159,7 +168,6 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
final EventTopic eventTopic = EventTopic.valueOf(topicValue);
handleIncomingEvent(message, eventTopic);
break;
default:
logAndThrowMessageError(message, "No handle method was found for the given message type.");
}
@@ -173,20 +181,6 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
return null;
}
public Message onAuthenticationRequest(final Message message) {
checkContentTypeJson(message);
final SecurityContext oldContext = SecurityContextHolder.getContext();
try {
return handleAuthentifiactionMessage(message);
} catch (final IllegalArgumentException ex) {
throw new AmqpRejectAndDontRequeueException("Invalid message!", ex);
} catch (final TenantNotExistException teex) {
throw new AmqpRejectAndDontRequeueException(teex);
} finally {
SecurityContextHolder.setContext(oldContext);
}
}
private Message handleAuthentifiactionMessage(final Message message) {
final DownloadResponse authentificationResponse = new DownloadResponse();
final MessageProperties messageProperties = message.getMessageProperties();
@@ -414,7 +408,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
if (ArrayUtils.isNotEmpty(message.getMessageProperties().getCorrelationId())) {
actionStatus.addMessage(RepositoryConstants.SERVER_MESSAGE_PREFIX + "DMF message correlation-id "
+ message.getMessageProperties().getCorrelationId());
+ convertCorrelationId(message));
}
actionStatus.setAction(action);
@@ -422,6 +416,10 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
return actionStatus;
}
private static String convertCorrelationId(final Message message) {
return new String(message.getMessageProperties().getCorrelationId());
}
private Action getUpdateActionStatus(final ActionStatus actionStatus) {
if (actionStatus.getStatus().equals(Status.CANCELED)) {
return controllerManagement.addCancelActionStatus(actionStatus);
@@ -466,7 +464,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
if (messageProperties.getContentType() != null && messageProperties.getContentType().contains("json")) {
return;
}
throw new IllegalArgumentException("Content-Type is not JSON compatible");
throw new AmqpRejectAndDontRequeueException("Content-Type is not JSON compatible");
}
void setControllerManagement(final ControllerManagement controllerManagement) {

View File

@@ -68,6 +68,29 @@ public class AmqpProperties {
*/
private int initialConcurrentConsumers = 3;
/**
* The number of retry attempts when passive queue declaration fails.
* Passive queue declaration occurs when the consumer starts or, when
* consuming from multiple queues, when not all queues were available during
* initialization.
*/
private int declarationRetries = 50;
/**
* @return the declarationRetries
*/
public int getDeclarationRetries() {
return declarationRetries;
}
/**
* @param declarationRetries
* the declarationRetries to set
*/
public void setDeclarationRetries(final int declarationRetries) {
this.declarationRetries = declarationRetries;
}
public String getAuthenticationReceiverQueue() {
return authenticationReceiverQueue;
}

View File

@@ -0,0 +1,52 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.amqp;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
/**
* {@link RabbitListenerContainerFactory} that can be configured through
* hawkBit's {@link AmqpProperties}.
*
*/
public class ConfigurableRabbitListenerContainerFactory extends SimpleRabbitListenerContainerFactory {
private final AmqpProperties amqpProperties;
/**
* Constructor.
*
* @param rabbitConnectionFactory
* for the container factory
* @param amqpProperties
* to configure the container factory
*/
public ConfigurableRabbitListenerContainerFactory(final AmqpProperties amqpProperties,
final ConnectionFactory rabbitConnectionFactory) {
this.amqpProperties = amqpProperties;
setDefaultRequeueRejected(true);
setConnectionFactory(rabbitConnectionFactory);
setMissingQueuesFatal(amqpProperties.isMissingQueuesFatal());
setConcurrentConsumers(amqpProperties.getInitialConcurrentConsumers());
setMaxConcurrentConsumers(amqpProperties.getMaxConcurrentConsumers());
setPrefetchCount(amqpProperties.getPrefetchCount());
}
@Override
// Exception squid:UnusedProtectedMethod - called by
// AbstractRabbitListenerContainerFactory
@SuppressWarnings("squid:UnusedProtectedMethod")
protected void initializeContainer(final SimpleMessageListenerContainer instance) {
super.initializeContainer(instance);
instance.setDeclarationRetries(amqpProperties.getDeclarationRetries());
}
}

View File

@@ -140,8 +140,8 @@ public class AmqpMessageHandlerServiceTest {
final Message message = new Message(new byte[0], messageProperties);
try {
amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, "vHost");
fail("IllegalArgumentException was excepeted due to worng content type");
} catch (final IllegalArgumentException e) {
fail("AmqpRejectAndDontRequeueException was excepeted due to worng content type");
} catch (final AmqpRejectAndDontRequeueException e) {
}
}
@@ -175,7 +175,7 @@ public class AmqpMessageHandlerServiceTest {
try {
amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, "vHost");
fail("IllegalArgumentException was excepeted since no replyTo header was set");
fail("AmqpRejectAndDontRequeueException was excepeted since no replyTo header was set");
} catch (final AmqpRejectAndDontRequeueException exception) {
// test ok - exception was excepted
}
@@ -189,7 +189,7 @@ public class AmqpMessageHandlerServiceTest {
final Message message = messageConverter.toMessage(new byte[0], messageProperties);
try {
amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, "vHost");
fail("IllegalArgumentException was excepeted since no thingID was set");
fail("AmqpRejectAndDontRequeueException was excepeted since no thingID was set");
} catch (final AmqpRejectAndDontRequeueException exception) {
// test ok - exception was excepted
}
@@ -205,7 +205,7 @@ public class AmqpMessageHandlerServiceTest {
try {
amqpMessageHandlerService.onMessage(message, type, TENANT, "vHost");
fail("IllegalArgumentException was excepeted due to unknown message type");
fail("AmqpRejectAndDontRequeueException was excepeted due to unknown message type");
} catch (final AmqpRejectAndDontRequeueException exception) {
// test ok - exception was excepted
}
@@ -218,21 +218,21 @@ public class AmqpMessageHandlerServiceTest {
final Message message = new Message(new byte[0], messageProperties);
try {
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
fail("IllegalArgumentException was excepeted due to unknown message type");
fail("AmqpRejectAndDontRequeueException was excepeted due to unknown message type");
} catch (final AmqpRejectAndDontRequeueException e) {
}
try {
messageProperties.setHeader(MessageHeaderKey.TOPIC, "wrongTopic");
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
fail("IllegalArgumentException was excepeted due to unknown topic");
fail("AmqpRejectAndDontRequeueException was excepeted due to unknown topic");
} catch (final AmqpRejectAndDontRequeueException e) {
}
messageProperties.setHeader(MessageHeaderKey.TOPIC, EventTopic.CANCEL_DOWNLOAD.name());
try {
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
fail("IllegalArgumentException was excepeted because there was no event topic");
fail("AmqpRejectAndDontRequeueException was excepeted because there was no event topic");
} catch (final AmqpRejectAndDontRequeueException exception) {
// test ok - exception was excepted
}
@@ -251,7 +251,7 @@ public class AmqpMessageHandlerServiceTest {
try {
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
fail("IllegalArgumentException was excepeted since no action id was set");
fail("AmqpRejectAndDontRequeueException was excepeted since no action id was set");
} catch (final AmqpRejectAndDontRequeueException exception) {
// test ok - exception was excepted
}
@@ -268,7 +268,7 @@ public class AmqpMessageHandlerServiceTest {
try {
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
fail("IllegalArgumentException was excepeted since no action id was set");
fail("AmqpRejectAndDontRequeueException was excepeted since no action id was set");
} catch (final AmqpRejectAndDontRequeueException exception) {
// test ok - exception was excepted
}

View File

@@ -32,7 +32,6 @@ public class MgmtSystemTenantServiceUsage {
* @param tenantName
*/
public MgmtSystemTenantServiceUsage(final String tenantName) {
super();
this.tenantName = tenantName;
}

View File

@@ -21,6 +21,17 @@ public class MgmtTargetRequestBody {
@JsonProperty
private String address;
@JsonProperty
private String securityToken;
public String getSecurityToken() {
return securityToken;
}
public void setSecurityToken(final String securityToken) {
this.securityToken = securityToken;
}
/**
* @return the name
*/

View File

@@ -182,10 +182,11 @@ public final class MgmtTargetMapper {
}
static Target fromRequest(final EntityFactory entityFactory, final MgmtTargetRequestBody targetRest) {
final Target target = entityFactory.generateTarget(targetRest.getControllerId());
final Target target = entityFactory.generateTarget(targetRest.getControllerId(), targetRest.getSecurityToken());
target.setDescription(targetRest.getDescription());
target.setName(targetRest.getName());
target.getTargetInfo().setAddress(targetRest.getAddress());
return target;
}

View File

@@ -125,6 +125,13 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
if (targetRest.getName() != null) {
existingTarget.setName(targetRest.getName());
}
if (targetRest.getAddress() != null) {
existingTarget.getTargetInfo().setAddress(targetRest.getAddress());
}
if (targetRest.getSecurityToken() != null) {
existingTarget.setSecurityToken(targetRest.getSecurityToken());
}
final Target updateTarget = this.targetManagement.updateTarget(existingTarget);
return new ResponseEntity<>(MgmtTargetMapper.toResponse(updateTarget), HttpStatus.OK);

View File

@@ -355,6 +355,54 @@ public class MgmtTargetResourceTest extends AbstractRestIntegrationTest {
assertThat(findTargetByControllerID.getName()).isEqualTo(knownNameNotModiy);
}
@Test
@Description("Ensures that target update request is reflected by repository.")
public void updateTargetSecurityToken() throws Exception {
final String knownControllerId = "123";
final String knownNewToken = "6567576565";
final String knownNameNotModiy = "nameNotModiy";
final String body = new JSONObject().put("securityToken", knownNewToken).toString();
// prepare
final Target t = entityFactory.generateTarget(knownControllerId);
t.setName(knownNameNotModiy);
targetManagement.createTarget(t);
mvc.perform(put(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownControllerId).content(body)
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.controllerId", equalTo(knownControllerId)))
.andExpect(jsonPath("$.securityToken", equalTo(knownNewToken)))
.andExpect(jsonPath("$.name", equalTo(knownNameNotModiy)));
final Target findTargetByControllerID = targetManagement.findTargetByControllerID(knownControllerId);
assertThat(findTargetByControllerID.getSecurityToken()).isEqualTo(knownNewToken);
assertThat(findTargetByControllerID.getName()).isEqualTo(knownNameNotModiy);
}
@Test
@Description("Ensures that target update request is reflected by repository.")
public void updateTargetAddress() throws Exception {
final String knownControllerId = "123";
final String knownNewAddress = "amqp://test123/foobar";
final String knownNameNotModiy = "nameNotModiy";
final String body = new JSONObject().put("address", knownNewAddress).toString();
// prepare
final Target t = entityFactory.generateTarget(knownControllerId);
t.setName(knownNameNotModiy);
targetManagement.createTarget(t);
mvc.perform(put(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownControllerId).content(body)
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.controllerId", equalTo(knownControllerId)))
.andExpect(jsonPath("$.address", equalTo(knownNewAddress)))
.andExpect(jsonPath("$.name", equalTo(knownNameNotModiy)));
final Target findTargetByControllerID = targetManagement.findTargetByControllerID(knownControllerId);
assertThat(findTargetByControllerID.getTargetInfo().getAddress().toString()).isEqualTo(knownNewAddress);
assertThat(findTargetByControllerID.getName()).isEqualTo(knownNameNotModiy);
}
@Test
@Description("Ensures that target query returns list of targets in defined format.")
public void getTargetWithoutAddtionalRequestParameters() throws Exception {
@@ -679,7 +727,7 @@ public class MgmtTargetResourceTest extends AbstractRestIntegrationTest {
@Test
public void createTargetsListReturnsSuccessful() throws Exception {
final Target test1 = entityFactory.generateTarget("id1");
final Target test1 = entityFactory.generateTarget("id1", "token");
test1.setDescription("testid1");
test1.setName("testname1");
test1.getTargetInfo().setAddress("amqp://test123/foobar");
@@ -696,7 +744,7 @@ public class MgmtTargetResourceTest extends AbstractRestIntegrationTest {
targets.add(test3);
final MvcResult mvcResult = mvc
.perform(post("/rest/v1/targets/").content(JsonBuilder.targets(targets))
.perform(post("/rest/v1/targets/").content(JsonBuilder.targets(targets, true))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
@@ -705,6 +753,7 @@ public class MgmtTargetResourceTest extends AbstractRestIntegrationTest {
.andExpect(jsonPath("[0].description", equalTo("testid1")))
.andExpect(jsonPath("[0].createdAt", not(equalTo(0))))
.andExpect(jsonPath("[0].createdBy", equalTo("bumlux")))
.andExpect(jsonPath("[0].securityToken", equalTo("token")))
.andExpect(jsonPath("[0].address", equalTo("amqp://test123/foobar")))
.andExpect(jsonPath("[1].name", equalTo("testname2")))
.andExpect(jsonPath("[1].createdBy", equalTo("bumlux")))
@@ -731,6 +780,9 @@ public class MgmtTargetResourceTest extends AbstractRestIntegrationTest {
assertThat(targetManagement.findTargetByControllerID("id1")).isNotNull();
assertThat(targetManagement.findTargetByControllerID("id1").getName()).isEqualTo("testname1");
assertThat(targetManagement.findTargetByControllerID("id1").getDescription()).isEqualTo("testid1");
assertThat(targetManagement.findTargetByControllerID("id1").getSecurityToken()).isEqualTo("token");
assertThat(targetManagement.findTargetByControllerID("id1").getTargetInfo().getAddress().toString())
.isEqualTo("amqp://test123/foobar");
assertThat(targetManagement.findTargetByControllerID("id2")).isNotNull();
assertThat(targetManagement.findTargetByControllerID("id2").getName()).isEqualTo("testname2");
assertThat(targetManagement.findTargetByControllerID("id2").getDescription()).isEqualTo("testid2");

View File

@@ -14,8 +14,8 @@ import java.util.Map;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.eventbus.event.DownloadProgressEvent;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.eventbus.event.DownloadProgressEvent;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.ToManyAttributeEntriesException;
@@ -58,16 +58,20 @@ public interface ControllerManagement {
Action addCancelActionStatus(@NotNull ActionStatus actionStatus);
/**
* Sends the download progress in percentage and notifies the
* {@link EventBus} with a {@link DownloadProgressEvent}.
* Sends the download progress and notifies the {@link EventBus} with a
* {@link DownloadProgressEvent}.
*
* @param statusId
* the ID of the {@link ActionStatus}
* @param progressPercent
* the progress in percentage which must be between 0-100
* @param requestedBytes
* requested bytes of the request
* @param shippedBytesSinceLast
* since the last report
* @param shippedBytesOverall
* for the {@link ActionStatus}
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
void downloadProgressPercent(long statusId, int progressPercent);
void downloadProgress(Long statusId, Long requestedBytes, Long shippedBytesSinceLast, Long shippedBytesOverall);
/**
* Simple addition of a new {@link ActionStatus} entry to the {@link Action}
@@ -75,9 +79,11 @@ public interface ControllerManagement {
*
* @param statusMessage
* to add to the action
*
* @return create {@link ActionStatus} entity
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
void addInformationalActionStatus(@NotNull ActionStatus statusMessage);
ActionStatus addInformationalActionStatus(@NotNull ActionStatus statusMessage);
/**
* Adds an {@link ActionStatus} entry for an update {@link Action} including

View File

@@ -269,8 +269,7 @@ public interface DeploymentManagement {
* @return the actions referring a specific rollout and a specific parent
* rollout group in a specific status
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
List<Action> findActionsByRolloutGroupParentAndStatus(@NotNull Rollout rollout,
@NotNull RolloutGroup rolloutGroupParent, @NotNull Action.Status actionStatus);
@@ -496,8 +495,7 @@ public interface DeploymentManagement {
* the action to start now.
* @return the action which has been started
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Action startScheduledAction(@NotNull Action action);
/**

View File

@@ -287,6 +287,7 @@ public interface EntityFactory {
/**
* Generates an empty {@link Target} without persisting it.
* {@link Target#getSecurityToken()} is generated.
*
* @param controllerID
* of the {@link Target}
@@ -295,6 +296,19 @@ public interface EntityFactory {
*/
Target generateTarget(@NotEmpty String controllerID);
/**
* Generates an empty {@link Target} without persisting it.
*
* @param controllerID
* of the {@link Target}
* @param securityToken
* of the {@link Target} for authentication if enabled on tenant.
* Generates one if empty or <code>null</code>.
*
* @return {@link Target} object
*/
Target generateTarget(@NotEmpty String controllerID, @NotEmpty String securityToken);
/**
* Generates an empty {@link TargetFilterQuery} without persisting it.
*

View File

@@ -61,8 +61,7 @@ public interface RolloutManagement {
* this check. This check is only applied if the last check is
* less than (lastcheck-delay).
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
void checkRunningRollouts(long delayBetweenChecks);
/**
@@ -266,8 +265,7 @@ public interface RolloutManagement {
* if given rollout is not in {@link RolloutStatus#RUNNING}.
* Only running rollouts can be paused.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
void pauseRollout(@NotNull Rollout rollout);
/**
@@ -281,8 +279,7 @@ public interface RolloutManagement {
* if given rollout is not in {@link RolloutStatus#PAUSED}. Only
* paused rollouts can be resumed.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
void resumeRollout(@NotNull Rollout rollout);
/**
@@ -303,8 +300,7 @@ public interface RolloutManagement {
* if given rollout is not in {@link RolloutStatus#READY}. Only
* ready rollouts can be started.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
Rollout startRollout(@NotNull Rollout rollout);
/**
@@ -326,8 +322,7 @@ public interface RolloutManagement {
* if given rollout is not in {@link RolloutStatus#READY}. Only
* ready rollouts can be started.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
Rollout startRolloutAsync(@NotNull Rollout rollout);
/**

View File

@@ -39,16 +39,14 @@ public interface SystemManagement {
* @param tenant
* to delete
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN)
void deleteTenant(@NotNull String tenant);
/**
*
* @return list of all tenant names in the system.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN)
List<String> findTenants();
/**
@@ -68,8 +66,8 @@ public interface SystemManagement {
/**
* Returns {@link TenantMetaData} of given and current tenant. Creates for
* new tenants also two {@link SoftwareModuleType} (os and app) and
* {@link RepositoryConstants#DEFAULT_DS_TYPES_IN_TENANT} {@link DistributionSetType}s
* (os and os_app).
* {@link RepositoryConstants#DEFAULT_DS_TYPES_IN_TENANT}
* {@link DistributionSetType}s (os and os_app).
*
* DISCLAIMER: this variant is used during initial login (where the tenant
* is not yet in the session). Please user {@link #getTenantMetadata()} for

View File

@@ -54,8 +54,7 @@ public interface TenantConfigurationManagement {
* @return <null> if no default value is set and no database value available
* or returns the tenant configuration value
*/
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
<T> TenantConfigurationValue<T> buildTenantConfigurationValueByKey(TenantConfigurationKey configurationKey,
Class<T> propertyType, TenantConfiguration tenantConfiguration);
@@ -87,8 +86,7 @@ public interface TenantConfigurationManagement {
* if the property cannot be converted to the given
* {@code propertyType}
*/
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
<T> TenantConfigurationValue<T> getConfigurationValue(TenantConfigurationKey configurationKey);
/**
@@ -114,8 +112,7 @@ public interface TenantConfigurationManagement {
* if the property cannot be converted to the given
* {@code propertyType}
*/
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
<T> TenantConfigurationValue<T> getConfigurationValue(TenantConfigurationKey configurationKey,
Class<T> propertyType);
@@ -139,7 +136,6 @@ public interface TenantConfigurationManagement {
* if the property cannot be converted to the given
* {@code propertyType}
*/
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
<T> T getGlobalConfigurationValue(TenantConfigurationKey configurationKey, Class<T> propertyType);
}

View File

@@ -20,15 +20,14 @@ import org.springframework.security.access.prepost.PreAuthorize;
public interface TenantStatsManagement {
/**
* Service for stats of a single tenant. Opens a new transaction and as a
* result can an be used for multiple tenants, i.e. to allow in one session
* to collect data of all tenants in the system.
* Service for stats of the current tenant.
*
* @param tenant
* to collect for
* @return collected statistics
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN)
TenantUsage getStatsOfTenant(String tenant);
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE)
TenantUsage getStatsOfTenant();
}

View File

@@ -0,0 +1,67 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.eventbus.event;
import org.eclipse.hawkbit.eventbus.event.AbstractDistributedEvent;
/**
* Event that contains an updated download progress for a given ActionStatus
* that was written for a download request.
*
*/
public class DownloadProgressEvent extends AbstractDistributedEvent {
private static final long serialVersionUID = 1L;
private final Long statusId;
private final long requestedBytes;
private final long shippedBytesSinceLast;
private final long shippedBytesOverall;
/**
* Constructor.
*
* @param tenant
* the tenant for this event
* @param statusId
* of ActionStatus that was written for the download request
* @param requestedBytes
* bytes requested
* @param shippedBytesSinceLast
* bytes since last event
* @param shippedBytesOverall
* on the download request
*/
public DownloadProgressEvent(final String tenant, final Long statusId, final Long requestedBytes,
final Long shippedBytesSinceLast, final Long shippedBytesOverall) {
// the revision of the DownloadProgressEvent is just equal the
// shippedBytesOverall as this is a growing number.
super(shippedBytesOverall, tenant);
this.statusId = statusId;
this.requestedBytes = requestedBytes;
this.shippedBytesSinceLast = shippedBytesSinceLast;
this.shippedBytesOverall = shippedBytesOverall;
}
public Long getStatusId() {
return statusId;
}
public long getRequestedBytes() {
return requestedBytes;
}
public long getShippedBytesSinceLast() {
return shippedBytesSinceLast;
}
public long getShippedBytesOverall() {
return shippedBytesOverall;
}
}

View File

@@ -39,12 +39,6 @@ public interface Action extends TenantAwareBaseEntity {
return Status.CANCELING.equals(getStatus()) || Status.CANCELED.equals(getStatus());
}
/**
* @return current {@link Status#DOWNLOAD} progress if known by the update
* server.
*/
int getDownloadProgressPercent();
/**
* @return current {@link Status} of the {@link Action}.
*/

View File

@@ -43,6 +43,12 @@ public interface ActionStatus extends TenantAwareBaseEntity {
*/
void addMessage(String message);
/**
* @return current {@link Status#DOWNLOAD} progress if known by the update
* server.
*/
int getDownloadProgressPercent();
/**
* @return list of message entries that can be added to the
* {@link ActionStatus}.

View File

@@ -58,4 +58,10 @@ public interface Target extends NamedEntity {
*/
String getSecurityToken();
/**
* @param token
* new securityToken
*/
void setSecurityToken(String token);
}

View File

@@ -106,7 +106,7 @@ public class TenantUsage {
}
@Override
public int hashCode() { // NOSONAR - as this is generated code
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (actions ^ (actions >>> 32));
@@ -118,15 +118,14 @@ public class TenantUsage {
}
@Override
public boolean equals(final Object obj) { // NOSONAR - as this is generated
// code
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
if (!(obj instanceof TenantUsage)) {
return false;
}
final TenantUsage other = (TenantUsage) obj;
@@ -154,7 +153,7 @@ public class TenantUsage {
@Override
public String toString() {
return "SystemUsage [tenantName=" + tenantName + ", targets=" + targets + ", artifacts=" + artifacts
return "TenantUsage [tenantName=" + tenantName + ", targets=" + targets + ", artifacts=" + artifacts
+ ", actions=" + actions + ", overallArtifactVolumeInBytes=" + overallArtifactVolumeInBytes + "]";
}

View File

@@ -54,6 +54,7 @@ import org.eclipse.hawkbit.repository.jpa.model.helper.TenantConfigurationManage
import org.eclipse.hawkbit.security.SecurityTokenGenerator;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
@@ -71,6 +72,8 @@ import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import com.google.common.eventbus.EventBus;
/**
* General configuration for hawkBit's Repository.
*
@@ -85,6 +88,9 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess
@EnableConfigurationProperties(RepositoryProperties.class)
@EnableScheduling
public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
@Autowired
private EventBus eventBus;
/**
* @return the {@link SystemSecurityContext} singleton bean which make it
* accessible in beans which cannot access the service directly,
@@ -249,7 +255,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
@Bean
@ConditionalOnMissingBean
public TenantStatsManagement tenantStatsManagement() {
return new JpaTenantStatsManagement();
final TenantStatsManagement mgmt = new JpaTenantStatsManagement();
eventBus.register(mgmt);
return mgmt;
}
/**

View File

@@ -451,8 +451,8 @@ public class JpaControllerManagement implements ControllerManagement {
@Override
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void addInformationalActionStatus(final ActionStatus statusMessage) {
actionStatusRepository.save((JpaActionStatus) statusMessage);
public ActionStatus addInformationalActionStatus(final ActionStatus statusMessage) {
return actionStatusRepository.save((JpaActionStatus) statusMessage);
}
@Override
@@ -469,8 +469,9 @@ public class JpaControllerManagement implements ControllerManagement {
}
@Override
public void downloadProgressPercent(final long statusId, final int progressPercent) {
cacheWriteNotify.downloadProgressPercent(statusId, progressPercent);
public void downloadProgress(final Long statusId, final Long requestedBytes, final Long shippedBytesSinceLast,
final Long shippedBytesOverall) {
cacheWriteNotify.downloadProgress(statusId, requestedBytes, shippedBytesSinceLast, shippedBytesOverall);
}
}

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa;
import java.util.Collection;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
@@ -87,6 +88,14 @@ public class JpaEntityFactory implements EntityFactory {
return new JpaTarget(controllerId);
}
@Override
public Target generateTarget(final String controllerId, final String securityToken) {
if (StringUtils.isEmpty(securityToken)) {
return new JpaTarget(controllerId);
}
return new JpaTarget(controllerId, securityToken);
}
@Override
public TargetTag generateTargetTag() {
return new JpaTargetTag();

View File

@@ -132,7 +132,7 @@ public class JpaSoftwareManagement implements SoftwareManagement {
final JpaSoftwareModuleType type = softwareModuleTypeRepository.findOne(sm.getId());
boolean updated = false;
if (sm.getDescription() != null && !sm.getDescription().equals(type.getDescription())) {
if (sm.getDescription() == null || !sm.getDescription().equals(type.getDescription())) {
type.setDescription(sm.getDescription());
updated = true;
}

View File

@@ -19,6 +19,7 @@ import org.eclipse.hawkbit.repository.Constants;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TenantStatsManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.configuration.MultiTenantJpaTransactionManager;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTenantMetaData;
@@ -26,6 +27,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.TenantMetaData;
import org.eclipse.hawkbit.repository.report.model.SystemUsageReport;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.springframework.beans.factory.annotation.Autowired;
@@ -33,11 +35,14 @@ import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.ApplicationContext;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.validation.annotation.Validated;
/**
@@ -108,7 +113,10 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
private SystemManagementCacheKeyGenerator currentTenantCacheKeyGenerator;
@Autowired
private ApplicationContext applicationContext;
private SystemSecurityContext systemSecurityContext;
@Autowired
private PlatformTransactionManager txManager;
@Override
public SystemUsageReport getSystemUsageStatistics() {
@@ -147,7 +155,7 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
final List<String> tenants = findTenants();
tenants.forEach(tenant -> tenantAware.runAsTenant(tenant, () -> {
report.addTenantData(systemStatsManagement.getStatsOfTenant(tenant));
report.addTenantData(systemStatsManagement.getStatsOfTenant());
return null;
}));
}
@@ -159,27 +167,48 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
}
@Override
@Cacheable(value = "tenantMetadata", key = "#tenant.toUpperCase()")
@Cacheable(value = "tenantMetadata", key = "#tenant.toUpperCase()", cacheManager = "directCacheManager")
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
public TenantMetaData getTenantMetadata(final String tenant) {
final TenantMetaData result = tenantMetaDataRepository.findByTenantIgnoreCase(tenant);
// Create if it does not exist
if (result == null) {
try {
currentTenantCacheKeyGenerator.getCreateInitialTenant().set(tenant);
cacheManager.getCache("currentTenant").evict(currentTenantKeyGenerator().generate(null, null));
applicationContext.getBean("currentTenantKeyGenerator");
return tenantMetaDataRepository.save(new JpaTenantMetaData(createStandardSoftwareDataSetup(), tenant));
return createInitialTenantMetaData(tenant);
} finally {
currentTenantCacheKeyGenerator.getCreateInitialTenant().remove();
}
}
return result;
}
/**
* Creating the initial tenant meta-data in a new transaction. Due the
* {@link MultiTenantJpaTransactionManager} is using the current tenant to
* set the necessary tenant discriminator to the query. This is not working
* if we don't have a current tenant set. Due the
* {@link #getTenantMetadata(String)} is maybe called without having a
* current tenant we need to re-open a new transaction so the
* {@link MultiTenantJpaTransactionManager} is called again and set the
* tenant for this transaction.
*
* @param tenant
* the tenant to be created
* @return the initial created {@link TenantMetaData}
*/
private TenantMetaData createInitialTenantMetaData(final String tenant) {
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("initial-tenant-creation");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
return systemSecurityContext.runAsSystemAsTenant(
() -> new TransactionTemplate(txManager, def).execute(status -> tenantMetaDataRepository
.save(new JpaTenantMetaData(createStandardSoftwareDataSetup(), tenant))),
tenant);
}
@Override
public List<String> findTenants() {
return tenantMetaDataRepository.findAll().stream().map(md -> md.getTenant()).collect(Collectors.toList());
@@ -191,7 +220,6 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
@Modifying
public void deleteTenant(final String tenant) {
cacheManager.evictCaches(tenant);
cacheManager.getCache("currentTenant").evict(currentTenantKeyGenerator().generate(null, null));
tenantAware.runAsTenant(tenant, () -> {
entityManager.setProperty(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, tenant.toUpperCase());
tenantMetaDataRepository.deleteByTenantIgnoreCase(tenant);
@@ -226,7 +254,7 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
}
@Override
@Cacheable(value = "currentTenant", keyGenerator = "currentTenantKeyGenerator")
@Cacheable(value = "currentTenant", keyGenerator = "currentTenantKeyGenerator", cacheManager = "directCacheManager")
// set transaction to not supported, due we call this in
// BaseEntity#prePersist methods
// and it seems that JPA committing the transaction when executing this

View File

@@ -12,8 +12,8 @@ import java.util.Optional;
import org.eclipse.hawkbit.repository.TenantStatsManagement;
import org.eclipse.hawkbit.repository.report.model.TenantUsage;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@@ -35,9 +35,14 @@ public class JpaTenantStatsManagement implements TenantStatsManagement {
@Autowired
private ActionRepository actionRepository;
@Autowired
private TenantAware tenantAware;
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED)
public TenantUsage getStatsOfTenant(final String tenant) {
public TenantUsage getStatsOfTenant() {
final String tenant = tenantAware.getCurrentTenant();
final TenantUsage result = new TenantUsage(tenant);
result.setTargets(targetRepository.count());

View File

@@ -8,9 +8,10 @@
*/
package org.eclipse.hawkbit.repository.jpa.cache;
import org.eclipse.hawkbit.eventbus.event.DownloadProgressEvent;
import java.math.RoundingMode;
import org.eclipse.hawkbit.repository.eventbus.event.DownloadProgressEvent;
import org.eclipse.hawkbit.repository.eventbus.event.RolloutGroupCreatedEvent;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.tenancy.TenantAware;
@@ -20,6 +21,7 @@ import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import com.google.common.eventbus.EventBus;
import com.google.common.math.DoubleMath;
/**
* An service which combines the functionality for functional use cases to write
@@ -30,10 +32,6 @@ import com.google.common.eventbus.EventBus;
*/
@Service
public class CacheWriteNotify {
/**
*
*/
private static final int DOWNLOAD_PROGRESS_MAX = 100;
@Autowired
@@ -46,20 +44,29 @@ public class CacheWriteNotify {
private TenantAware tenantAware;
/**
* writes the download progress in percentage into the cache
* writes the download progress into the cache
* {@link CacheKeys#DOWNLOAD_PROGRESS_PERCENT} and notifies the
* {@link EventBus} with a {@link DownloadProgressEvent}.
*
* @param statusId
* the ID of the {@link ActionStatus}
* @param progressPercent
* the progress in percentage which must be between 0-100
* @param requestedBytes
* requested bytes of the request
* @param shippedBytesSinceLast
* since last event
* @param shippedBytesOverall
* for the download request
*/
public void downloadProgressPercent(final long statusId, final int progressPercent) {
public void downloadProgress(final Long statusId, final Long requestedBytes, final Long shippedBytesSinceLast,
final Long shippedBytesOverall) {
final Cache cache = cacheManager.getCache(Action.class.getName());
final Cache cache = cacheManager.getCache(ActionStatus.class.getName());
final String cacheKey = CacheKeys.entitySpecificCacheKey(String.valueOf(statusId),
CacheKeys.DOWNLOAD_PROGRESS_PERCENT);
final int progressPercent = DoubleMath.roundToInt(shippedBytesOverall * 100.0 / requestedBytes,
RoundingMode.DOWN);
if (progressPercent < DOWNLOAD_PROGRESS_MAX) {
cache.put(cacheKey, progressPercent);
} else {
@@ -69,7 +76,8 @@ public class CacheWriteNotify {
cache.evict(cacheKey);
}
eventBus.post(new DownloadProgressEvent(tenantAware.getCurrentTenant(), statusId, progressPercent));
eventBus.post(new DownloadProgressEvent(tenantAware.getCurrentTenant(), statusId, requestedBytes,
shippedBytesSinceLast, shippedBytesOverall));
}
/**

View File

@@ -27,10 +27,7 @@ import javax.persistence.NamedEntityGraphs;
import javax.persistence.NamedSubgraph;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.eclipse.hawkbit.repository.jpa.cache.CacheField;
import org.eclipse.hawkbit.repository.jpa.cache.CacheKeys;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -89,13 +86,6 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@JoinColumn(name = "rollout", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_rollout"))
private JpaRollout rollout;
/**
* Note: filled only in {@link Status#DOWNLOAD}.
*/
@Transient
@CacheField(key = CacheKeys.DOWNLOAD_PROGRESS_PERCENT)
private int downloadProgressPercent;
@Override
public DistributionSet getDistributionSet() {
return distributionSet;
@@ -120,15 +110,6 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
this.status = status;
}
@Override
public int getDownloadProgressPercent() {
return downloadProgressPercent;
}
public void setDownloadProgressPercent(final int downloadProgressPercent) {
this.downloadProgressPercent = downloadProgressPercent;
}
@Override
public boolean isActive() {
return active;

View File

@@ -24,7 +24,10 @@ import javax.persistence.ManyToOne;
import javax.persistence.NamedAttributeNode;
import javax.persistence.NamedEntityGraph;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.eclipse.hawkbit.repository.jpa.cache.CacheField;
import org.eclipse.hawkbit.repository.jpa.cache.CacheKeys;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
@@ -63,6 +66,13 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements
@Column(name = "detail_message", length = 512)
private final List<String> messages = new ArrayList<>();
/**
* Note: filled only in {@link Status#DOWNLOAD}.
*/
@Transient
@CacheField(key = CacheKeys.DOWNLOAD_PROGRESS_PERCENT)
private int downloadProgressPercent;
/**
* Creates a new {@link ActionStatus} object.
*
@@ -105,6 +115,11 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements
// JPA default constructor.
}
@Override
public int getDownloadProgressPercent() {
return downloadProgressPercent;
}
@Override
public Long getOccurredAt() {
return occurredAt;

View File

@@ -115,9 +115,21 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Lon
* controller ID of the {@link Target}
*/
public JpaTarget(final String controllerId) {
this(controllerId, SecurityTokenGeneratorHolder.getInstance().generateToken());
}
/**
* Constructor.
*
* @param controllerId
* controller ID of the {@link Target}
* @param securityToken
* for target authentication if enabled
*/
public JpaTarget(final String controllerId, final String securityToken) {
this.controllerId = controllerId;
setName(controllerId);
securityToken = SecurityTokenGeneratorHolder.getInstance().generateToken();
this.securityToken = securityToken;
targetInfo = new JpaTargetInfo(this);
}

View File

@@ -17,6 +17,7 @@ import java.util.Random;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TenantMetaData;
import org.eclipse.hawkbit.repository.report.model.TenantUsage;
import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
import org.junit.Test;
@@ -29,6 +30,15 @@ import ru.yandex.qatools.allure.annotations.Stories;
@Stories("System Management")
public class SystemManagementTest extends AbstractJpaIntegrationTestWithMongoDB {
@Test
@Description("Ensures that you can create a tenant without setting the necessary security context which holds a current tenant")
public void createInitialTenantWithoutSecurityContext() {
securityRule.clear();
final String tenantToBeCreated = "newTenantToCreate";
final TenantMetaData tenantMetadata = systemManagement.getTenantMetadata(tenantToBeCreated);
assertThat(tenantMetadata).isNotNull();
}
@Test
@Description("Ensures that findTenants returns all tenants and not only restricted to the tenant which currently is logged in")
public void findTenantsReturnsAllTenantsNotOnlyWhichLoggedIn() throws Exception {

View File

@@ -38,13 +38,13 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Tag;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetIdName;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.junit.Test;
import org.springframework.data.domain.PageRequest;
@@ -61,7 +61,7 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Ensures that retrieving the target security is only permitted with the necessary permissions.")
public void getTargetSecurityTokenOnlyWithCorrectPermission() throws Exception {
final Target createdTarget = targetManagement.createTarget(new JpaTarget("targetWithSecurityToken"));
final Target createdTarget = targetManagement.createTarget(new JpaTarget("targetWithSecurityToken", "token"));
// retrieve security token only with READ_TARGET_SEC_TOKEN permission
final String securityTokenWithReadPermission = securityRule.runAs(WithSpringAuthorityRule
@@ -80,7 +80,7 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
return createdTarget.getSecurityToken();
});
assertThat(createdTarget.getSecurityToken()).isNotNull();
assertThat(createdTarget.getSecurityToken()).isEqualTo("token");
assertThat(securityTokenWithReadPermission).isNotNull();
assertThat(securityTokenAsSystemCode).isNotNull();

View File

@@ -13,8 +13,8 @@ import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.eclipse.hawkbit.eventbus.event.DownloadProgressEvent;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.eventbus.event.DownloadProgressEvent;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.junit.Before;
import org.junit.Test;
@@ -59,15 +59,14 @@ public class CacheWriteNotifyTest {
@Test
public void downloadgProgressIsCachedAndEventSent() {
final long knownStatusId = 1;
final int knownPercentage = 23;
when(cacheManagerMock.getCache(Action.class.getName())).thenReturn(cacheMock);
when(cacheManagerMock.getCache(ActionStatus.class.getName())).thenReturn(cacheMock);
when(tenantAwareMock.getCurrentTenant()).thenReturn("default");
underTest.downloadProgressPercent(knownStatusId, knownPercentage);
underTest.downloadProgress(knownStatusId, 500L, 100L, 100L);
verify(cacheManagerMock).getCache(eq(Action.class.getName()));
verify(cacheMock).put(knownStatusId + "." + CacheKeys.DOWNLOAD_PROGRESS_PERCENT, knownPercentage);
verify(cacheManagerMock).getCache(eq(ActionStatus.class.getName()));
verify(cacheMock).put(knownStatusId + "." + CacheKeys.DOWNLOAD_PROGRESS_PERCENT, 20);
verify(eventBusMock).post(any(DownloadProgressEvent.class));
}

View File

@@ -63,7 +63,7 @@ public class WithSpringAuthorityRule implements TestRule {
}
return oldContext;
}
/**
* @param annotation
*/
@@ -129,6 +129,14 @@ public class WithSpringAuthorityRule implements TestRule {
private void after(final SecurityContext oldContext) {
SecurityContextHolder.setContext(oldContext);
}
/**
* Clears the current security context.
*/
public void clear()
{
SecurityContextHolder.clearContext();
}
/**
* @param callable

View File

@@ -9,11 +9,12 @@
package org.eclipse.hawkbit.rest.exception;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.eclipse.hawkbit.exception.SpServerError;
import org.eclipse.hawkbit.exception.SpServerRtException;
import org.eclipse.hawkbit.repository.exception.MultiPartFileUploadException;
@@ -27,6 +28,8 @@ import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.multipart.MultipartException;
import com.google.common.collect.Iterables;
/**
* General controller advice for exception handling.
*/
@@ -135,13 +138,8 @@ public class ResponseExceptionHandler {
logRequest(request, ex);
Throwable responseCause = ex;
final Throwable searchForCause = searchForCause(ex, FileUploadException.class);
if (searchForCause != null) {
responseCause = searchForCause;
}
final List<Throwable> throwables = ExceptionUtils.getThrowableList(ex);
final Throwable responseCause = Iterables.getLast(throwables);
final ExceptionInfo response = createExceptionInfo(new MultiPartFileUploadException(responseCause));
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}
@@ -150,19 +148,6 @@ public class ResponseExceptionHandler {
LOG.debug("Handling exception {} of request {}", ex.getClass().getName(), request.getRequestURL());
}
private static Throwable searchForCause(final Throwable t, final Class<?> lookFor) {
if (t == null || t.getCause() == null) {
return null;
}
final Throwable cause = t.getCause();
if (cause.getClass().equals(lookFor)) {
return cause;
}
return searchForCause(cause, lookFor);
}
private ExceptionInfo createExceptionInfo(final Exception ex) {
final ExceptionInfo response = new ExceptionInfo();
response.setMessage(ex.getMessage());

View File

@@ -23,6 +23,7 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -87,7 +88,7 @@ public final class RestResourceConversionHelper {
* @param controllerManagement
* to write progress updates to
* @param statusId
* of the UpdateActionStatus
* of the {@link ActionStatus}
*
* @return http code
*
@@ -293,6 +294,7 @@ public final class RestResourceConversionHelper {
long toRead = length;
boolean toContinue = true;
long shippedSinceLastEvent = 0;
while (toContinue) {
final int r = from.read(buf);
@@ -304,9 +306,11 @@ public final class RestResourceConversionHelper {
if (toRead > 0) {
to.write(buf, 0, r);
total += r;
shippedSinceLastEvent += r;
} else {
to.write(buf, 0, (int) toRead + r);
total += toRead + r;
shippedSinceLastEvent += toRead + r;
toContinue = false;
}
@@ -316,7 +320,8 @@ public final class RestResourceConversionHelper {
// every 10 percent an event
if (newPercent == 100 || newPercent > progressPercent + 10) {
progressPercent = newPercent;
controllerManagement.downloadProgressPercent(statusId, progressPercent);
controllerManagement.downloadProgress(statusId, length, shippedSinceLastEvent, total);
shippedSinceLastEvent = 0;
}
}
}

View File

@@ -367,11 +367,7 @@ public abstract class JsonBuilder {
}
/**
* @param targets
* @return
*/
public static String targets(final List<Target> targets) {
public static String targets(final List<Target> targets, final boolean withToken) {
final StringBuilder builder = new StringBuilder();
builder.append("[");
@@ -381,10 +377,12 @@ public abstract class JsonBuilder {
final String address = target.getTargetInfo().getAddress() != null
? target.getTargetInfo().getAddress().toString() : null;
final String token = withToken ? target.getSecurityToken() : null;
builder.append(new JSONObject().put("controllerId", target.getControllerId())
.put("description", target.getDescription()).put("name", target.getName()).put("createdAt", "0")
.put("updatedAt", "0").put("createdBy", "fghdfkjghdfkjh").put("updatedBy", "fghdfkjghdfkjh")
.put("address", address).toString());
.put("address", address).put("securityToken", token).toString());
} catch (final Exception e) {
e.printStackTrace();
}

View File

@@ -224,8 +224,10 @@ public final class SpPermission {
/*
* Spring security eval expressions.
*/
private static final String HAS_AUTH_PREFIX = "hasAuthority('";
private static final String HAS_AUTH_SUFFIX = "')";
private static final String BRACKET_OPEN = "(";
private static final String BRACKET_CLOSE = ")";
private static final String HAS_AUTH_PREFIX = "hasAuthority" + BRACKET_OPEN + "'";
private static final String HAS_AUTH_SUFFIX = "'" + BRACKET_CLOSE;
private static final String HAS_AUTH_AND = " and ";
/**
@@ -257,99 +259,6 @@ public final class SpPermission {
*/
public static final String HAS_AUTH_OR = " or ";
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#UPDATE_TARGET}.
*/
public static final String HAS_AUTH_UPDATE_TARGET = HAS_AUTH_PREFIX + UPDATE_TARGET + HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#SYSTEM_ADMIN}.
*/
public static final String HAS_AUTH_SYSTEM_ADMIN = HAS_AUTH_PREFIX + SYSTEM_ADMIN + HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#READ_TARGET}.
*/
public static final String HAS_AUTH_READ_TARGET = HAS_AUTH_PREFIX + READ_TARGET + HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#CREATE_TARGET}.
*/
public static final String HAS_AUTH_CREATE_TARGET = HAS_AUTH_PREFIX + CREATE_TARGET + HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#DELETE_TARGET}.
*/
public static final String HAS_AUTH_DELETE_TARGET = HAS_AUTH_PREFIX + DELETE_TARGET + HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#READ_REPOSITORY} and
* {@link SpPermission#UPDATE_TARGET}.
*/
public static final String HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET = HAS_AUTH_PREFIX + READ_REPOSITORY
+ HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + UPDATE_TARGET + HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#CREATE_REPOSITORY}.
*/
public static final String HAS_AUTH_CREATE_REPOSITORY = HAS_AUTH_PREFIX + CREATE_REPOSITORY + HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#DELETE_REPOSITORY}.
*/
public static final String HAS_AUTH_DELETE_REPOSITORY = HAS_AUTH_PREFIX + DELETE_REPOSITORY + HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#READ_REPOSITORY}.
*/
public static final String HAS_AUTH_READ_REPOSITORY = HAS_AUTH_PREFIX + READ_REPOSITORY + HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#UPDATE_REPOSITORY}.
*/
public static final String HAS_AUTH_UPDATE_REPOSITORY = HAS_AUTH_PREFIX + UPDATE_REPOSITORY + HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#READ_REPOSITORY} and
* {@link SpPermission#READ_TARGET}.
*/
public static final String HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET = HAS_AUTH_PREFIX + READ_REPOSITORY
+ HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + READ_TARGET + HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#DOWNLOAD_REPOSITORY_ARTIFACT}.
*/
public static final String HAS_AUTH_DOWNLOAD_ARTIFACT = HAS_AUTH_PREFIX + DOWNLOAD_REPOSITORY_ARTIFACT
+ HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAnyRole expression to check if the spring
* context contains the anoynmous role or the controller specific role
* {@link SpPermission#CONTROLLER_ROLE}.
*/
public static final String IS_CONTROLLER = "hasAnyRole('" + CONTROLLER_ROLE_ANONYMOUS + "', '" + CONTROLLER_ROLE
+ "')";
/**
* Spring security eval hasAuthority expression to check if the spring
* context contains the role to allow controllers to download specific
* role {@link SpPermission#CONTROLLER_DOWNLOAD_ROLE}.
*/
public static final String HAS_CONTROLLER_DOWNLOAD = HAS_AUTH_PREFIX + CONTROLLER_DOWNLOAD_ROLE
+ HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAnyRole expression to check if the spring
* context contains system code role
@@ -359,48 +268,168 @@ public final class SpPermission {
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#CREATE_REPOSITORY} and
* {@link SpPermission#CREATE_TARGET}.
* context contains {@link SpPermission#UPDATE_TARGET} or
* {@link #IS_SYSTEM_CODE}.
*/
public static final String HAS_AUTH_CREATE_REPOSITORY_AND_CREATE_TARGET = HAS_AUTH_PREFIX + CREATE_REPOSITORY
+ HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + CREATE_TARGET + HAS_AUTH_SUFFIX;
public static final String HAS_AUTH_UPDATE_TARGET = HAS_AUTH_PREFIX + UPDATE_TARGET + HAS_AUTH_SUFFIX
+ HAS_AUTH_OR + IS_SYSTEM_CODE;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#ROLLOUT_MANAGEMENT}
* context contains {@link SpPermission#SYSTEM_ADMIN} or
* {@link #IS_SYSTEM_CODE}.
*/
public static final String HAS_AUTH_SYSTEM_ADMIN = HAS_AUTH_PREFIX + SYSTEM_ADMIN + HAS_AUTH_SUFFIX
+ HAS_AUTH_OR + IS_SYSTEM_CODE;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#READ_TARGET} or
* {@link #IS_SYSTEM_CODE}.
*/
public static final String HAS_AUTH_READ_TARGET = HAS_AUTH_PREFIX + READ_TARGET + HAS_AUTH_SUFFIX + HAS_AUTH_OR
+ IS_SYSTEM_CODE;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#CREATE_TARGET} or
* {@link #IS_SYSTEM_CODE}.
*/
public static final String HAS_AUTH_CREATE_TARGET = HAS_AUTH_PREFIX + CREATE_TARGET + HAS_AUTH_SUFFIX
+ HAS_AUTH_OR + IS_SYSTEM_CODE;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#DELETE_TARGET} or
* {@link #IS_SYSTEM_CODE}.
*/
public static final String HAS_AUTH_DELETE_TARGET = HAS_AUTH_PREFIX + DELETE_TARGET + HAS_AUTH_SUFFIX
+ HAS_AUTH_OR + IS_SYSTEM_CODE;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#READ_REPOSITORY} and
* {@link SpPermission#UPDATE_TARGET} or {@link #IS_SYSTEM_CODE}.
*/
public static final String HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET = BRACKET_OPEN + HAS_AUTH_PREFIX
+ READ_REPOSITORY + HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + UPDATE_TARGET + HAS_AUTH_SUFFIX
+ BRACKET_CLOSE + HAS_AUTH_OR + IS_SYSTEM_CODE;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#CREATE_REPOSITORY} or
* {@link #IS_SYSTEM_CODE}.
*/
public static final String HAS_AUTH_CREATE_REPOSITORY = HAS_AUTH_PREFIX + CREATE_REPOSITORY + HAS_AUTH_SUFFIX
+ HAS_AUTH_OR + IS_SYSTEM_CODE;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#DELETE_REPOSITORY} or
* {@link #IS_SYSTEM_CODE}.
*/
public static final String HAS_AUTH_DELETE_REPOSITORY = HAS_AUTH_PREFIX + DELETE_REPOSITORY + HAS_AUTH_SUFFIX
+ HAS_AUTH_OR + IS_SYSTEM_CODE;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#READ_REPOSITORY} or
* {@link #IS_SYSTEM_CODE}.
*/
public static final String HAS_AUTH_READ_REPOSITORY = HAS_AUTH_PREFIX + READ_REPOSITORY + HAS_AUTH_SUFFIX
+ HAS_AUTH_OR + IS_SYSTEM_CODE;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#UPDATE_REPOSITORY} or
* {@link #IS_SYSTEM_CODE}.
*/
public static final String HAS_AUTH_UPDATE_REPOSITORY = HAS_AUTH_PREFIX + UPDATE_REPOSITORY + HAS_AUTH_SUFFIX
+ HAS_AUTH_OR + IS_SYSTEM_CODE;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#READ_REPOSITORY} and
* {@link SpPermission#READ_TARGET} or {@link #IS_SYSTEM_CODE}.
*/
public static final String HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET = BRACKET_OPEN + HAS_AUTH_PREFIX
+ READ_REPOSITORY + HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + READ_TARGET + HAS_AUTH_SUFFIX
+ BRACKET_CLOSE + HAS_AUTH_OR + IS_SYSTEM_CODE;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#DOWNLOAD_REPOSITORY_ARTIFACT} or
* {@link #IS_SYSTEM_CODE}.
*/
public static final String HAS_AUTH_DOWNLOAD_ARTIFACT = HAS_AUTH_PREFIX + DOWNLOAD_REPOSITORY_ARTIFACT
+ HAS_AUTH_SUFFIX + HAS_AUTH_OR + IS_SYSTEM_CODE;
/**
* Spring security eval hasAnyRole expression to check if the spring
* context contains the anoynmous role or the controller specific role
* {@link SpringEvalExpressions#CONTROLLER_ROLE}.
*/
public static final String IS_CONTROLLER = "hasAnyRole('" + CONTROLLER_ROLE_ANONYMOUS + "', '" + CONTROLLER_ROLE
+ "')";
/**
* Spring security eval hasAuthority expression to check if the spring
* context contains the role to allow controllers to download specific
* role {@link SpringEvalExpressions#CONTROLLER_DOWNLOAD_ROLE}
*/
public static final String HAS_CONTROLLER_DOWNLOAD = HAS_AUTH_PREFIX + CONTROLLER_DOWNLOAD_ROLE
+ HAS_AUTH_SUFFIX;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#CREATE_REPOSITORY} and
* {@link SpPermission#CREATE_TARGET} or {@link #IS_SYSTEM_CODE}.
*/
public static final String HAS_AUTH_CREATE_REPOSITORY_AND_CREATE_TARGET = BRACKET_OPEN + HAS_AUTH_PREFIX
+ CREATE_REPOSITORY + HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + CREATE_TARGET + HAS_AUTH_SUFFIX
+ BRACKET_CLOSE + HAS_AUTH_OR + IS_SYSTEM_CODE;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#ROLLOUT_MANAGEMENT} or
* {@link #IS_SYSTEM_CODE}.
*/
public static final String HAS_AUTH_ROLLOUT_MANAGEMENT_READ = HAS_AUTH_PREFIX + ROLLOUT_MANAGEMENT
+ HAS_AUTH_SUFFIX;
+ HAS_AUTH_SUFFIX + HAS_AUTH_OR + IS_SYSTEM_CODE;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#ROLLOUT_MANAGEMENT} and
* {@link SpPermission#READ_TARGET}
* {@link SpPermission#READ_TARGET} or {@link #IS_SYSTEM_CODE}.
*/
public static final String HAS_AUTH_ROLLOUT_MANAGEMENT_READ_AND_TARGET_READ = HAS_AUTH_PREFIX
+ ROLLOUT_MANAGEMENT + HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + READ_TARGET
+ HAS_AUTH_SUFFIX;;
public static final String HAS_AUTH_ROLLOUT_MANAGEMENT_READ_AND_TARGET_READ = BRACKET_OPEN + HAS_AUTH_PREFIX
+ ROLLOUT_MANAGEMENT + HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + READ_TARGET + HAS_AUTH_SUFFIX
+ BRACKET_CLOSE + HAS_AUTH_OR + IS_SYSTEM_CODE;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#ROLLOUT_MANAGEMENT} and
* {@link SpPermission#UPDATE_TARGET}.
* {@link SpPermission#UPDATE_TARGET} or {@link #IS_SYSTEM_CODE}.
*/
public static final String HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE = HAS_AUTH_PREFIX + ROLLOUT_MANAGEMENT
+ HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + UPDATE_TARGET + HAS_AUTH_SUFFIX;
public static final String HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE = BRACKET_OPEN + HAS_AUTH_PREFIX
+ ROLLOUT_MANAGEMENT + HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + UPDATE_TARGET
+ HAS_AUTH_SUFFIX + BRACKET_CLOSE + HAS_AUTH_OR + IS_SYSTEM_CODE;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#TENANT_CONFIGURATION}
* context contains {@link SpPermission#TENANT_CONFIGURATION} or
* {@link #IS_SYSTEM_CODE}.
*/
public static final String HAS_AUTH_TENANT_CONFIGURATION = HAS_AUTH_PREFIX + TENANT_CONFIGURATION
+ HAS_AUTH_SUFFIX;
+ HAS_AUTH_SUFFIX + HAS_AUTH_OR + IS_SYSTEM_CODE;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#SYSTEM_MONITOR}
* context contains {@link SpPermission#SYSTEM_MONITOR} or
* {@link #IS_SYSTEM_CODE}.
*/
public static final String HAS_AUTH_SYSTEM_MONITOR = HAS_AUTH_PREFIX + SYSTEM_MONITOR + HAS_AUTH_SUFFIX;
public static final String HAS_AUTH_SYSTEM_MONITOR = HAS_AUTH_PREFIX + SYSTEM_MONITOR + HAS_AUTH_SUFFIX
+ HAS_AUTH_OR + IS_SYSTEM_CODE;
private SpringEvalExpressions() {
// utility class

View File

@@ -9,6 +9,7 @@
package org.eclipse.hawkbit.security;
import java.util.Collection;
import java.util.Collections;
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.tenancy.TenantAware;
@@ -80,32 +81,37 @@ public class SecurityContextTenantAware implements TenantAware {
@Override
public boolean equals(final Object another) {
return delegate.equals(another);
if (delegate != null) {
return delegate.equals(another);
} else if (another == null) {
return true;
}
return false;
}
@Override
public String toString() {
return delegate.toString();
return (delegate != null) ? delegate.toString() : null;
}
@Override
public int hashCode() {
return delegate.hashCode();
return (delegate != null) ? delegate.hashCode() : null;
}
@Override
public String getName() {
return delegate.getName();
return (delegate != null) ? delegate.getName() : null;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return delegate.getAuthorities();
return (delegate != null) ? delegate.getAuthorities() : Collections.emptyList();
}
@Override
public Object getCredentials() {
return delegate.getCredentials();
return (delegate != null) ? delegate.getCredentials() : null;
}
@Override
@@ -115,16 +121,19 @@ public class SecurityContextTenantAware implements TenantAware {
@Override
public Object getPrincipal() {
return delegate.getPrincipal();
return (delegate != null) ? delegate.getPrincipal() : null;
}
@Override
public boolean isAuthenticated() {
return delegate.isAuthenticated();
return (delegate != null) ? delegate.isAuthenticated() : null;
}
@Override
public void setAuthenticated(final boolean isAuthenticated) throws IllegalArgumentException {
public void setAuthenticated(final boolean isAuthenticated) {
if (delegate == null) {
return;
}
delegate.setAuthenticated(isAuthenticated);
}
}

View File

@@ -60,6 +60,9 @@ public class SystemSecurityContext {
* The security context will be switched to the system code and back after
* the callable is called.
*
* The system code is executed for a current tenant by using the
* {@link TenantAware#getCurrentTenant()}.
*
* @param callable
* the callable to call within the system security context
* @return the return value of the {@link Callable#call()} method.
@@ -67,12 +70,36 @@ public class SystemSecurityContext {
// Exception squid:S2221 - Callable declares Exception
@SuppressWarnings("squid:S2221")
public <T> T runAsSystem(final Callable<T> callable) {
return runAsSystemAsTenant(callable, tenantAware.getCurrentTenant());
}
/**
* Runs a given {@link Callable} within a system security context, which is
* permitted to call secured system code. Often the system needs to call
* secured methods by it's own without relying on the current security
* context e.g. if the current security context does not contain the
* necessary permission it's necessary to execute code as system code to
* execute necessary methods and functionality.
*
* The security context will be switched to the system code and back after
* the callable is called.
*
* The system code is executed for a specific given tenant by using the
* {@link TenantAware}.
*
* @param callable
* the callable to call within the system security context
* @param tenant
* the tenant to act as system code
* @return the return value of the {@link Callable#call()} method.
*/
public <T> T runAsSystemAsTenant(final Callable<T> callable, final String tenant) {
final SecurityContext oldContext = SecurityContextHolder.getContext();
try {
logger.debug("entering system code execution");
return tenantAware.runAsTenant(tenantAware.getCurrentTenant(), () -> {
return tenantAware.runAsTenant(tenant, () -> {
try {
setSystemContext(oldContext);
setSystemContext(SecurityContextHolder.getContext());
return callable.call();
} catch (final Exception e) {
throw Throwables.propagate(e);
@@ -100,6 +127,13 @@ public class SystemSecurityContext {
SecurityContextHolder.setContext(securityContextImpl);
}
/**
* An implementation of the Spring's {@link Authentication} object which is
* used within a system security code block and wraps the original
* authentication object. The wrapped object contains the necessary
* {@link SpringEvalExpressions#SYSTEM_ROLE} which is allowed to execute all
* secured methods.
*/
public static class SystemCodeAuthentication implements Authentication {
private static final long serialVersionUID = 1L;

View File

@@ -200,6 +200,10 @@
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>

View File

@@ -10,6 +10,8 @@ package org.eclipse.hawkbit.ui.artifacts.smtable;
import java.io.Serializable;
import javax.annotation.PostConstruct;
import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
@@ -18,18 +20,17 @@ import org.eclipse.hawkbit.ui.common.CommonDialogWindow;
import org.eclipse.hawkbit.ui.common.SoftwareModuleTypeBeanQuery;
import org.eclipse.hawkbit.ui.common.table.BaseEntityEventType;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.decorators.SPUIWindowDecorator;
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
import org.eclipse.hawkbit.ui.utils.I18N;
import org.eclipse.hawkbit.ui.utils.SPUIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions;
import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
import org.eclipse.hawkbit.ui.utils.UINotification;
import org.springframework.beans.factory.annotation.Autowired;
import org.vaadin.addons.lazyquerycontainer.BeanQueryFactory;
import org.vaadin.spring.events.EventBus;
import com.vaadin.event.FieldEvents.TextChangeEvent;
import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.ViewScope;
import com.vaadin.ui.ComboBox;
@@ -38,8 +39,6 @@ import com.vaadin.ui.FormLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.Window;
import com.vaadin.ui.themes.ValoTheme;
/**
@@ -66,8 +65,6 @@ public class SoftwareModuleAddUpdateWindow extends CustomComponent implements Se
@Autowired
private transient EntityFactory entityFactory;
private Label mandatoryLabel;
private TextField nameTextField;
private TextField versionTextField;
@@ -80,14 +77,20 @@ public class SoftwareModuleAddUpdateWindow extends CustomComponent implements Se
private CommonDialogWindow window;
private String oldDescriptionValue;
private String oldVendorValue;
private Boolean editSwModule = Boolean.FALSE;
private Long baseSwModuleId;
private FormLayout formLayout;
/**
* Initialize Distribution Add and Edit Window.
*/
@PostConstruct
void init() {
createRequiredComponents();
}
/**
* Create window for new software module.
*
@@ -95,11 +98,7 @@ public class SoftwareModuleAddUpdateWindow extends CustomComponent implements Se
* module.
*/
public CommonDialogWindow createAddSoftwareModuleWindow() {
editSwModule = Boolean.FALSE;
createRequiredComponents();
createWindow();
return window;
return createUpdateSoftwareModuleWindow(null);
}
/**
@@ -110,17 +109,11 @@ public class SoftwareModuleAddUpdateWindow extends CustomComponent implements Se
* @return reference of {@link com.vaadin.ui.Window} to update software
* module.
*/
public Window createUpdateSoftwareModuleWindow(final Long baseSwModuleId) {
editSwModule = Boolean.TRUE;
public CommonDialogWindow createUpdateSoftwareModuleWindow(final Long baseSwModuleId) {
this.baseSwModuleId = baseSwModuleId;
createRequiredComponents();
createWindow();
/* populate selected target values to edit. */
resetComponents();
populateValuesOfSwModule();
nameTextField.setEnabled(false);
versionTextField.setEnabled(false);
typeComboBox.setEnabled(false);
createWindow();
return window;
}
@@ -145,13 +138,6 @@ public class SoftwareModuleAddUpdateWindow extends CustomComponent implements Se
ValoTheme.TEXTAREA_TINY, false, null, i18n.get("textfield.description"),
SPUILabelDefinitions.TEXT_AREA_MAX_LENGTH);
descTextArea.setId(SPUIComponentIdProvider.ADD_SW_MODULE_DESCRIPTION);
addDescriptionTextChangeListener();
addVendorTextChangeListener();
/* Label for mandatory symbol */
mandatoryLabel = new Label(i18n.get("label.mandatory.field"));
mandatoryLabel.setStyleName(SPUIStyleDefinitions.SP_TEXTFIELD_ERROR);
mandatoryLabel.addStyleName(ValoTheme.LABEL_SMALL);
typeComboBox = SPUIComponentProvider.getComboBox(i18n.get("upload.swmodule.type"), "", "", null, null, true,
null, i18n.get("upload.swmodule.type"));
@@ -159,46 +145,34 @@ public class SoftwareModuleAddUpdateWindow extends CustomComponent implements Se
typeComboBox.setStyleName(SPUIDefinitions.COMBO_BOX_SPECIFIC_STYLE + " " + ValoTheme.COMBOBOX_TINY);
typeComboBox.setNewItemsAllowed(Boolean.FALSE);
typeComboBox.setImmediate(Boolean.TRUE);
populateTypeNameCombo();
resetOldValues();
}
/**
*
*/
private void populateTypeNameCombo() {
typeComboBox.setContainerDataSource(HawkbitCommonUtil.createLazyQueryContainer(
new BeanQueryFactory<SoftwareModuleTypeBeanQuery>(SoftwareModuleTypeBeanQuery.class)));
typeComboBox.setItemCaptionPropertyId(SPUILabelDefinitions.VAR_NAME);
}
private void resetOldValues() {
oldDescriptionValue = null;
oldVendorValue = null;
private void resetComponents() {
vendorTextField.clear();
nameTextField.clear();
versionTextField.clear();
descTextArea.clear();
typeComboBox.clear();
editSwModule = Boolean.FALSE;
}
/**
* Build the window content and get an instance of customDialogWindow
*
*/
private void createWindow() {
final Label madatoryStarLabel = new Label("*");
madatoryStarLabel.setStyleName("v-caption v-required-field-indicator");
madatoryStarLabel.setWidth(null);
/*
* The main layout of the window contains mandatory info, textboxes
* (controller Id, name & description) and action buttons layout
*/
addStyleName("lay-color");
setSizeUndefined();
final FormLayout formLayout = new FormLayout();
formLayout.addComponent(mandatoryLabel);
formLayout = new FormLayout();
formLayout.setCaption(null);
formLayout.addComponent(typeComboBox);
formLayout.addComponent(nameTextField);
formLayout.addComponent(versionTextField);
@@ -207,24 +181,17 @@ public class SoftwareModuleAddUpdateWindow extends CustomComponent implements Se
setCompositionRoot(formLayout);
/* add main layout to the window */
window = SPUIComponentProvider.getWindow(i18n.get("upload.caption.add.new.swmodule"), null,
SPUIDefinitions.CREATE_UPDATE_WINDOW, this, event -> saveOrUpdate(), event -> closeThisWindow(), null);
window = SPUIWindowDecorator.getWindow(i18n.get("upload.caption.add.new.swmodule"), null,
SPUIDefinitions.CREATE_UPDATE_WINDOW, this, event -> saveOrUpdate(), null, null, formLayout, i18n);
window.getButtonsLayout().removeStyleName("actionButtonsMargin");
nameTextField.focus();
nameTextField.setEnabled(!editSwModule);
versionTextField.setEnabled(!editSwModule);
typeComboBox.setEnabled(!editSwModule);
typeComboBox.focus();
}
private void addDescriptionTextChangeListener() {
descTextArea.addTextChangeListener(event -> window.setSaveButtonEnabled(hasDescriptionChanged(event)));
}
private void addVendorTextChangeListener() {
vendorTextField.addTextChangeListener(event -> window.setSaveButtonEnabled(hasVendorChanged(event)));
}
/**
* Add new SW module.
*/
private void addNewBaseSoftware() {
final String name = HawkbitCommonUtil.trimAndNullIfEmpty(nameTextField.getValue());
final String version = HawkbitCommonUtil.trimAndNullIfEmpty(versionTextField.getValue());
@@ -232,10 +199,6 @@ public class SoftwareModuleAddUpdateWindow extends CustomComponent implements Se
final String description = HawkbitCommonUtil.trimAndNullIfEmpty(descTextArea.getValue());
final String type = typeComboBox.getValue() != null ? typeComboBox.getValue().toString() : null;
if (!mandatoryCheck(name, version, type)) {
return;
}
if (HawkbitCommonUtil.isDuplicate(name, version, type)) {
uiNotifcation.displayValidationError(
i18n.get("message.duplicate.softwaremodule", new Object[] { name, version }));
@@ -248,8 +211,6 @@ public class SoftwareModuleAddUpdateWindow extends CustomComponent implements Se
new Object[] { newBaseSoftwareModule.getName() + ":" + newBaseSoftwareModule.getVersion() }));
eventBus.publish(this, new SoftwareModuleEvent(BaseEntityEventType.NEW_ENTITY, newBaseSoftwareModule));
}
// close the window
closeThisWindow();
}
}
@@ -269,13 +230,16 @@ public class SoftwareModuleAddUpdateWindow extends CustomComponent implements Se
eventBus.publish(this, new SoftwareModuleEvent(BaseEntityEventType.UPDATED_ENTITY, newSWModule));
}
closeThisWindow();
}
/**
* fill the data of a softwareModule in the content of the window
*/
private void populateValuesOfSwModule() {
if (baseSwModuleId == null) {
return;
}
editSwModule = Boolean.TRUE;
final SoftwareModule swModle = softwareManagement.findSoftwareModuleById(baseSwModuleId);
nameTextField.setValue(swModle.getName());
versionTextField.setValue(swModle.getVersion());
@@ -283,49 +247,10 @@ public class SoftwareModuleAddUpdateWindow extends CustomComponent implements Se
: HawkbitCommonUtil.trimAndNullIfEmpty(swModle.getVendor()));
descTextArea.setValue(swModle.getDescription() == null ? HawkbitCommonUtil.SP_STRING_EMPTY
: HawkbitCommonUtil.trimAndNullIfEmpty(swModle.getDescription()));
oldDescriptionValue = descTextArea.getValue();
oldVendorValue = vendorTextField.getValue();
if (swModle.getType().isDeleted()) {
typeComboBox.addItem(swModle.getType().getName());
}
typeComboBox.setValue(swModle.getType().getName());
window.setSaveButtonEnabled(Boolean.FALSE);
}
/**
* Method to close window.
*/
private void closeThisWindow() {
window.close();
UI.getCurrent().removeWindow(window);
}
/**
* Validation check - Mandatory.
*
* @param name
* as String
* @param version
* as version
* @return boolena as flag
*/
private boolean mandatoryCheck(final String name, final String version, final String type) {
boolean isValid = true;
if (name == null || version == null || type == null) {
if (name == null) {
nameTextField.addStyleName(SPUIStyleDefinitions.SP_TEXTFIELD_ERROR);
}
if (version == null) {
versionTextField.addStyleName(SPUIStyleDefinitions.SP_TEXTFIELD_ERROR);
}
if (type == null) {
typeComboBox.addStyleName(SPUIStyleDefinitions.SP_COMBOFIELD_ERROR);
}
uiNotifcation.displayValidationError(i18n.get("message.mandatory.check"));
isValid = false;
}
return isValid;
}
private void saveOrUpdate() {
@@ -336,12 +261,8 @@ public class SoftwareModuleAddUpdateWindow extends CustomComponent implements Se
}
}
private boolean hasDescriptionChanged(final TextChangeEvent event) {
return !(event.getText().equals(oldDescriptionValue) && vendorTextField.getValue().equals(oldVendorValue));
}
private boolean hasVendorChanged(final TextChangeEvent event) {
return !(event.getText().equals(oldVendorValue) && descTextArea.getValue().equals(oldDescriptionValue));
public FormLayout getFormLayout() {
return formLayout;
}
}

View File

@@ -44,7 +44,6 @@ import com.vaadin.ui.UI;
/**
* Header of Software module table.
*
*/
@SpringComponent
@ViewScope

View File

@@ -11,13 +11,11 @@ package org.eclipse.hawkbit.ui.artifacts.smtype;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleTypeEvent;
import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleTypeEvent.SoftwareModuleTypeEnum;
import org.eclipse.hawkbit.ui.colorpicker.ColorPickerConstants;
import org.eclipse.hawkbit.ui.colorpicker.ColorPickerHelper;
import org.eclipse.hawkbit.ui.common.SoftwareModuleTypeBeanQuery;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
@@ -34,12 +32,10 @@ import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.shared.ui.colorpicker.Color;
import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.ViewScope;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Label;
import com.vaadin.ui.OptionGroup;
import com.vaadin.ui.components.colorpicker.ColorChangeListener;
import com.vaadin.ui.components.colorpicker.ColorSelector;
import com.vaadin.ui.themes.ValoTheme;
/**
@@ -48,8 +44,7 @@ import com.vaadin.ui.themes.ValoTheme;
*/
@SpringComponent
@ViewScope
public class CreateUpdateSoftwareTypeLayout extends CreateUpdateTypeLayout
implements ColorChangeListener, ColorSelector {
public class CreateUpdateSoftwareTypeLayout extends CreateUpdateTypeLayout {
private static final long serialVersionUID = -5169398523815919367L;
private static final Logger LOG = LoggerFactory.getLogger(CreateUpdateSoftwareTypeLayout.class);
@@ -69,7 +64,7 @@ public class CreateUpdateSoftwareTypeLayout extends CreateUpdateTypeLayout
@Override
protected void addListeners() {
super.addListeners();
optiongroup.addValueChangeListener(this::createOptionValueChanged);
optiongroup.addValueChangeListener(this::optionValueChanged);
}
@Override
@@ -95,7 +90,6 @@ public class CreateUpdateSoftwareTypeLayout extends CreateUpdateTypeLayout
tagDesc = SPUIComponentProvider.getTextArea(i18n.get("textfield.description"), "",
ValoTheme.TEXTFIELD_TINY + " " + SPUIDefinitions.TYPE_DESC, false, "",
i18n.get("textfield.description"), SPUILabelDefinitions.TEXT_AREA_MAX_LENGTH);
tagDesc.setId(SPUIDefinitions.NEW_SOFTWARE_TYPE_DESC);
tagDesc.setImmediate(true);
tagDesc.setNullRepresentation("");
@@ -113,10 +107,8 @@ public class CreateUpdateSoftwareTypeLayout extends CreateUpdateTypeLayout
}
@Override
public void createWindow() {
reset();
window = SPUIComponentProvider.getWindow(i18n.get("caption.add.type"), null,
SPUIDefinitions.CREATE_UPDATE_WINDOW, this, this::save, this::discard, null);
protected String getWindowCaption() {
return i18n.get("caption.add.type");
}
/**
@@ -126,15 +118,16 @@ public class CreateUpdateSoftwareTypeLayout extends CreateUpdateTypeLayout
* ValueChangeEvent
*/
@Override
protected void createOptionValueChanged(final ValueChangeEvent event) {
protected void optionValueChanged(final ValueChangeEvent event) {
super.createOptionValueChanged(event);
super.optionValueChanged(event);
if (updateTypeStr.equals(event.getProperty().getValue())) {
assignOptiongroup.setEnabled(false);
} else {
assignOptiongroup.setEnabled(true);
}
assignOptiongroup.select(singleAssignStr);
}
/**
@@ -171,12 +164,11 @@ public class CreateUpdateSoftwareTypeLayout extends CreateUpdateTypeLayout
if (null != selectedTypeTag) {
tagDesc.setValue(selectedTypeTag.getDescription());
typeKey.setValue(selectedTypeTag.getKey());
if (selectedTypeTag.getMaxAssignments() == Integer.MAX_VALUE) {
assignOptiongroup.setValue(multiAssignStr);
} else {
if (selectedTypeTag.getMaxAssignments() == 1) {
assignOptiongroup.setValue(singleAssignStr);
} else {
assignOptiongroup.setValue(multiAssignStr);
}
setColorPickerComponentsColor(selectedTypeTag.getColour());
}
}
@@ -198,10 +190,6 @@ public class CreateUpdateSoftwareTypeLayout extends CreateUpdateTypeLayout
@Override
protected void save(final ClickEvent event) {
if (!mandatoryValuesPresent()) {
return;
}
final SoftwareModuleType existingSMTypeByKey = swTypeManagementService
.findSoftwareModuleTypeByKey(typeKey.getValue());
final SoftwareModuleType existingSMTypeByName = swTypeManagementService
@@ -211,7 +199,6 @@ public class CreateUpdateSoftwareTypeLayout extends CreateUpdateTypeLayout
createNewSWModuleType();
}
} else {
updateSWModuleType(existingSMTypeByName);
}
}
@@ -233,22 +220,14 @@ public class CreateUpdateSoftwareTypeLayout extends CreateUpdateTypeLayout
SoftwareModuleType newSWType = entityFactory.generateSoftwareModuleType(typeKeyValue, typeNameValue,
typeDescValue, assignNumber);
newSWType.setColour(colorPicked);
if (null != typeDescValue) {
newSWType.setDescription(typeDescValue);
}
newSWType.setDescription(typeDescValue);
newSWType.setColour(colorPicked);
newSWType = swTypeManagementService.createSoftwareModuleType(newSWType);
uiNotification.displaySuccess(i18n.get("message.save.success", new Object[] { newSWType.getName() }));
closeWindow();
eventBus.publish(this,
new SoftwareModuleTypeEvent(SoftwareModuleTypeEnum.ADD_SOFTWARE_MODULE_TYPE, newSWType));
} else {
uiNotification.displayValidationError(i18n.get("message.error.missing.typenameorkey"));
}
}
@@ -258,51 +237,15 @@ public class CreateUpdateSoftwareTypeLayout extends CreateUpdateTypeLayout
final String typeDescValue = HawkbitCommonUtil.trimAndNullIfEmpty(tagDesc.getValue());
if (null != typeNameValue) {
existingType.setName(typeNameValue);
existingType.setDescription(null != typeDescValue ? typeDescValue : null);
existingType.setDescription(typeDescValue);
existingType.setColour(ColorPickerHelper.getColorPickedString(getColorPickerLayout().getSelPreview()));
swTypeManagementService.updateSoftwareModuleType(existingType);
uiNotification.displaySuccess(i18n.get("message.update.success", new Object[] { existingType.getName() }));
closeWindow();
eventBus.publish(this,
new SoftwareModuleTypeEvent(SoftwareModuleTypeEnum.UPDATE_SOFTWARE_MODULE_TYPE, existingType));
} else {
uiNotification.displayValidationError(i18n.get("message.tag.update.mandatory"));
}
}
/**
* Open color picker on click of preview button. Auto select the color based
* on target tag if already selected.
*/
@Override
protected void previewButtonClicked() {
if (!tagPreviewBtnClicked) {
final String selectedOption = (String) optiongroup.getValue();
if (StringUtils.isNotEmpty(selectedOption) && selectedOption.equalsIgnoreCase(updateTypeStr)) {
if (null != tagNameComboBox.getValue()) {
final SoftwareModuleType typeSelected = swTypeManagementService
.findSoftwareModuleTypeByName(tagNameComboBox.getValue().toString());
if (null != typeSelected) {
getColorPickerLayout().setSelectedColor(typeSelected.getColour() != null
? ColorPickerHelper.rgbToColorConverter(typeSelected.getColour())
: ColorPickerHelper.rgbToColorConverter(ColorPickerConstants.DEFAULT_COLOR));
}
} else {
getColorPickerLayout().setSelectedColor(
ColorPickerHelper.rgbToColorConverter(ColorPickerConstants.DEFAULT_COLOR));
}
}
getColorPickerLayout().getSelPreview().setColor(getColorPickerLayout().getSelectedColor());
mainLayout.addComponent(colorPickerLayout, 1, 0);
mainLayout.setComponentAlignment(colorPickerLayout, Alignment.MIDDLE_CENTER);
} else {
mainLayout.removeComponent(colorPickerLayout);
}
tagPreviewBtnClicked = !tagPreviewBtnClicked;
}
@Override

View File

@@ -13,6 +13,7 @@ import java.util.Set;
import org.eclipse.hawkbit.ui.common.CoordinatesToColor;
import org.eclipse.hawkbit.ui.management.tag.SpColorPickerPreview;
import org.eclipse.hawkbit.ui.utils.SPUIComponentIdProvider;
import com.vaadin.shared.ui.colorpicker.Color;
import com.vaadin.ui.AbstractColorPicker.Coordinates2Color;
@@ -47,6 +48,7 @@ public class ColorPickerLayout extends GridLayout {
setColumns(2);
setRows(4);
setId(SPUIComponentIdProvider.COLOR_PICKER_LAYOUT);
init();
@@ -71,6 +73,7 @@ public class ColorPickerLayout extends GridLayout {
colorSelect.setWidth("220px");
redSlider = createRGBSlider("", "red");
redSlider.setId(SPUIComponentIdProvider.COLOR_PICKER_RED_SLIDER);
greenSlider = createRGBSlider("", "green");
blueSlider = createRGBSlider("", "blue");

View File

@@ -10,32 +10,69 @@ package org.eclipse.hawkbit.ui.common;
import static com.google.common.base.Preconditions.checkNotNull;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleBorderWithIcon;
import org.eclipse.hawkbit.ui.utils.SPUIComponentIdProvider;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.hawkbit.ui.artifacts.smtable.SoftwareModuleAddUpdateWindow;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleNoBorderWithIcon;
import org.eclipse.hawkbit.ui.layouts.AbstractCreateUpdateTagLayout;
import org.eclipse.hawkbit.ui.management.targettable.TargetAddUpdateWindowLayout;
import org.eclipse.hawkbit.ui.utils.I18N;
import org.eclipse.hawkbit.ui.utils.SPUIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
import org.vaadin.hene.flexibleoptiongroup.FlexibleOptionGroupItemComponent;
import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import com.vaadin.data.Container.ItemSetChangeEvent;
import com.vaadin.data.Container.ItemSetChangeListener;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.data.Validator;
import com.vaadin.data.validator.NullValidator;
import com.vaadin.event.FieldEvents.TextChangeEvent;
import com.vaadin.event.FieldEvents.TextChangeListener;
import com.vaadin.event.FieldEvents.TextChangeNotifier;
import com.vaadin.server.FontAwesome;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.AbstractField;
import com.vaadin.ui.AbstractLayout;
import com.vaadin.ui.AbstractOrderedLayout;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.Component;
import com.vaadin.ui.Field;
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Link;
import com.vaadin.ui.Table;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import com.vaadin.ui.themes.ValoTheme;
/**
*
* Superclass for pop-up-windows including a minimize and close icon in the
* upper right corner and a save and cancel button at the bottom.
*
* Table pop-up-windows including a minimize and close icon in the upper right
* corner and a save and cancel button at the bottom. Is not intended to reuse.
*
*/
public class CommonDialogWindow extends Window {
public class CommonDialogWindow extends Window implements Serializable {
private static final long serialVersionUID = -1321949234316858703L;
private static final long serialVersionUID = 1L;
private final VerticalLayout mainLayout = new VerticalLayout();
@@ -57,6 +94,14 @@ public class CommonDialogWindow extends Window {
private final ClickListener cancelButtonClickListener;
private final ClickListener close = event -> close();
private final transient Map<Component, Object> orginalValues;
private final List<AbstractField<?>> allComponents;
private final I18N i18n;
/**
* Constructor.
*
@@ -72,28 +117,83 @@ public class CommonDialogWindow extends Window {
* the cancelButtonClickListener
*/
public CommonDialogWindow(final String caption, final Component content, final String helpLink,
final ClickListener saveButtonClickListener, final ClickListener cancelButtonClickListener) {
final ClickListener saveButtonClickListener, final ClickListener cancelButtonClickListener,
final AbstractLayout layout, final I18N i18n) {
checkNotNull(saveButtonClickListener);
checkNotNull(cancelButtonClickListener);
this.caption = caption;
this.content = content;
this.helpLink = helpLink;
this.saveButtonClickListener = saveButtonClickListener;
this.cancelButtonClickListener = cancelButtonClickListener;
this.orginalValues = new HashMap<>();
this.allComponents = getAllComponents(layout);
this.i18n = i18n;
init();
}
@Override
public void close() {
super.close();
orginalValues.clear();
removeListeners();
allComponents.clear();
this.saveButton.setEnabled(false);
}
private void removeListeners() {
for (final AbstractField<?> field : allComponents) {
removeTextListener(field);
removeValueChangeListener(field);
removeItemSetChangeistener(field);
}
}
private void removeItemSetChangeistener(final AbstractField<?> field) {
if (!(field instanceof Table)) {
return;
}
for (final Object listener : field.getListeners(ItemSetChangeEvent.class)) {
if (listener instanceof ChangeListener) {
((Table) field).removeItemSetChangeListener((ChangeListener) listener);
}
}
}
private void removeTextListener(final AbstractField<?> field) {
if (!(field instanceof TextChangeNotifier)) {
return;
}
for (final Object listener : field.getListeners(TextChangeEvent.class)) {
if (listener instanceof ChangeListener) {
((TextChangeNotifier) field).removeTextChangeListener((ChangeListener) listener);
}
}
}
private void removeValueChangeListener(final AbstractField<?> field) {
for (final Object listener : field.getListeners(ValueChangeEvent.class)) {
if (listener instanceof ChangeListener) {
field.removeValueChangeListener((ChangeListener) listener);
}
}
}
private final void init() {
if (content instanceof AbstractOrderedLayout) {
((AbstractOrderedLayout) content).setSpacing(true);
((AbstractOrderedLayout) content).setMargin(true);
}
if (content instanceof GridLayout) {
addStyleName("marginTop");
}
if (null != content) {
mainLayout.addComponent(content);
}
createMandatoryLabel();
final HorizontalLayout buttonLayout = createActionButtonsLayout();
mainLayout.addComponent(buttonLayout);
mainLayout.setComponentAlignment(buttonLayout, Alignment.TOP_CENTER);
@@ -104,6 +204,173 @@ public class CommonDialogWindow extends Window {
center();
setModal(true);
addStyleName("fontsize");
setOrginaleValues();
addListeners();
}
/**
* saves the original values in a Map so we can use them for detecting
* changes
*/
public final void setOrginaleValues() {
for (final AbstractField<?> field : allComponents) {
Object value = field.getValue();
if (field instanceof Table) {
value = Sets.newHashSet(((Table) field).getContainerDataSource().getItemIds());
}
orginalValues.put(field, value);
}
saveButton.setEnabled(isSaveButtonEnabledAfterValueChange(null, null));
}
private final void addListeners() {
for (final AbstractField<?> field : allComponents) {
if (field instanceof TextChangeNotifier) {
((TextChangeNotifier) field).addTextChangeListener(new ChangeListener(field));
}
if (field instanceof Table) {
((Table) field).addItemSetChangeListener(new ChangeListener(field));
} else {
field.addValueChangeListener(new ChangeListener(field));
}
}
saveButton.addClickListener(close);
cancelButton.addClickListener(close);
}
private boolean isSaveButtonEnabledAfterValueChange(final Component currentChangedComponent,
final Object newValue) {
return isMandatoryFieldNotEmptyAndValid(currentChangedComponent, newValue)
&& isValuesChanged(currentChangedComponent, newValue);
}
private boolean isValuesChanged(final Component currentChangedComponent, final Object newValue) {
for (final AbstractField<?> field : allComponents) {
Object originalValue = orginalValues.get(field);
if (field instanceof CheckBox && originalValue == null) {
originalValue = Boolean.FALSE;
}
final Object currentValue = getCurrentVaue(currentChangedComponent, newValue, field);
if (!isValueEquals(field, originalValue, currentValue)) {
return true;
}
}
return false;
}
private boolean isValueEquals(final AbstractField<?> field, final Object orginalValue, final Object currentValue) {
if (Set.class.equals(field.getType())) {
return CollectionUtils.isEqualCollection(CollectionUtils.emptyIfNull((Collection<?>) orginalValue),
CollectionUtils.emptyIfNull((Collection<?>) currentValue));
}
if (String.class.equals(field.getType())) {
return Objects.equals(Strings.emptyToNull((String) orginalValue),
Strings.emptyToNull((String) currentValue));
}
return Objects.equals(orginalValue, currentValue);
}
private Object getCurrentVaue(final Component currentChangedComponent, final Object newValue,
final AbstractField<?> field) {
Object currentValue = field.getValue();
if (field instanceof Table) {
currentValue = ((Table) field).getContainerDataSource().getItemIds();
}
if (field.equals(currentChangedComponent)) {
currentValue = newValue;
}
return currentValue;
}
private boolean shouldMandatoryLabelShown() {
for (final AbstractField<?> field : allComponents) {
if (field.isRequired()) {
return true;
}
}
return false;
}
private boolean isMandatoryFieldNotEmptyAndValid(final Component currentChangedComponent, final Object newValue) {
boolean valid = true;
final List<AbstractField<?>> requiredComponents = allComponents.stream().filter(field -> field.isRequired())
.collect(Collectors.toList());
requiredComponents.addAll(allComponents.stream().filter(this::hasNullValidator).collect(Collectors.toList()));
for (final AbstractField field : requiredComponents) {
Object value = getCurrentVaue(currentChangedComponent, newValue, field);
if (String.class.equals(field.getType())) {
value = Strings.emptyToNull((String) value);
}
if (Set.class.equals(field.getType())) {
value = emptyToNull((Collection<?>) value);
}
if (value == null) {
return false;
}
// We need to loop through the entire loop for validity testing.
// Otherwise the UI will only mark the
// first field with errors and then stop. If there are several
// fields with errors, this is bad.
field.setValue(value);
if (!field.isValid()) {
valid = false;
}
}
return valid;
}
private static Object emptyToNull(final Collection<?> c) {
return (c == null || c.isEmpty()) ? null : c;
}
private boolean hasNullValidator(final Component component) {
if (component instanceof AbstractField<?>) {
final AbstractField<?> fieldComponent = (AbstractField<?>) component;
for (final Validator validator : fieldComponent.getValidators()) {
if (validator instanceof NullValidator) {
return true;
}
}
}
return false;
}
private List<AbstractField<?>> getAllComponents(final AbstractLayout abstractLayout) {
final List<AbstractField<?>> components = new ArrayList<>();
final Iterator<Component> iterate = abstractLayout.iterator();
while (iterate.hasNext()) {
final Component c = iterate.next();
if (c instanceof AbstractLayout) {
components.addAll(getAllComponents((AbstractLayout) c));
}
if (c instanceof AbstractField) {
components.add((AbstractField<?>) c);
}
if (c instanceof FlexibleOptionGroupItemComponent) {
components.add(((FlexibleOptionGroupItemComponent) c).getOwner());
}
}
return components;
}
private HorizontalLayout createActionButtonsLayout() {
@@ -111,23 +378,43 @@ public class CommonDialogWindow extends Window {
buttonsLayout = new HorizontalLayout();
buttonsLayout.setSizeFull();
buttonsLayout.setSpacing(true);
buttonsLayout.addStyleName("actionButtonsMargin");
createSaveButton();
createCancelButton();
buttonsLayout.addStyleName("actionButtonsMargin");
addHelpLink();
return buttonsLayout;
}
private void createMandatoryLabel() {
if (!shouldMandatoryLabelShown()) {
return;
}
final Label mandatoryLabel = new Label(i18n.get("label.mandatory.field"));
mandatoryLabel.addStyleName(SPUIStyleDefinitions.SP_TEXTFIELD_ERROR + " " + ValoTheme.LABEL_TINY);
if (content instanceof TargetAddUpdateWindowLayout) {
((TargetAddUpdateWindowLayout) content).getFormLayout().addComponent(mandatoryLabel);
} else if (content instanceof SoftwareModuleAddUpdateWindow) {
((SoftwareModuleAddUpdateWindow) content).getFormLayout().addComponent(mandatoryLabel);
} else if (content instanceof AbstractCreateUpdateTagLayout) {
((AbstractCreateUpdateTagLayout) content).getMainLayout().addComponent(mandatoryLabel);
}
mainLayout.addComponent(mandatoryLabel);
}
private void createCancelButton() {
cancelButton = SPUIComponentProvider.getButton(SPUIComponentIdProvider.CANCEL_BUTTON, "Cancel", "", "", true,
FontAwesome.TIMES, SPUIButtonStyleBorderWithIcon.class);
FontAwesome.TIMES, SPUIButtonStyleNoBorderWithIcon.class);
cancelButton.setSizeUndefined();
cancelButton.addStyleName("default-color");
cancelButton.addClickListener(cancelButtonClickListener);
if (cancelButtonClickListener != null) {
cancelButton.addClickListener(cancelButtonClickListener);
}
buttonsLayout.addComponent(cancelButton);
buttonsLayout.setComponentAlignment(cancelButton, Alignment.MIDDLE_LEFT);
@@ -136,10 +423,10 @@ public class CommonDialogWindow extends Window {
private void createSaveButton() {
saveButton = SPUIComponentProvider.getButton(SPUIComponentIdProvider.SAVE_BUTTON, "Save", "", "", true,
FontAwesome.SAVE, SPUIButtonStyleBorderWithIcon.class);
FontAwesome.SAVE, SPUIButtonStyleNoBorderWithIcon.class);
saveButton.setSizeUndefined();
saveButton.addStyleName("default-color");
saveButton.addClickListener(saveButtonClickListener);
saveButton.setEnabled(false);
buttonsLayout.addComponent(saveButton);
buttonsLayout.setComponentAlignment(saveButton, Alignment.MIDDLE_RIGHT);
buttonsLayout.setExpandRatio(saveButton, 1.0F);
@@ -155,16 +442,52 @@ public class CommonDialogWindow extends Window {
buttonsLayout.setComponentAlignment(helpLinkComponent, Alignment.MIDDLE_RIGHT);
}
public void setSaveButtonEnabled(final boolean enabled) {
saveButton.setEnabled(enabled);
public AbstractComponent getButtonsLayout() {
return this.buttonsLayout;
}
public void setCancelButtonEnabled(final boolean enabled) {
cancelButton.setEnabled(enabled);
private class ChangeListener implements ValueChangeListener, TextChangeListener, ItemSetChangeListener {
private final Field<?> field;
public ChangeListener(final Field<?> field) {
super();
this.field = field;
}
@Override
public void textChange(final TextChangeEvent event) {
saveButton.setEnabled(isSaveButtonEnabledAfterValueChange(field, event.getText()));
}
@Override
public void valueChange(final ValueChangeEvent event) {
saveButton.setEnabled(isSaveButtonEnabledAfterValueChange(field, field.getValue()));
}
@Override
public void containerItemSetChange(final ItemSetChangeEvent event) {
if (!(field instanceof Table)) {
return;
}
final Table table = (Table) field;
saveButton.setEnabled(
isSaveButtonEnabledAfterValueChange(table, table.getContainerDataSource().getItemIds()));
}
}
public HorizontalLayout getButtonsLayout() {
return buttonsLayout;
/**
* Adds the component manually to the allComponents-List and adds a
* ValueChangeListener to it. Necessary in Update Distribution Type as the
* CheckBox concerned is an ItemProperty...
*
* @param component
* AbstractField
*/
public void updateAllComponents(final AbstractField component) {
allComponents.add(component);
component.addValueChangeListener(new ChangeListener(component));
}
}

View File

@@ -22,7 +22,8 @@ import com.vaadin.ui.Button.ClickEvent;
*/
public abstract class AbstractFilterMultiButtonClick extends AbstractFilterButtonClickBehaviour {
protected final Set<Button> alreadyClickedButtons = new HashSet<>();
private static final long serialVersionUID = 1L;
protected final transient Set<Button> alreadyClickedButtons = new HashSet<>();
/*
* (non-Javadoc)

View File

@@ -95,9 +95,6 @@ public abstract class AbstractTable<E extends NamedEntity, I> extends Table {
if (values == null) {
values = Collections.emptySet();
}
if (values.contains(null)) {
LOG.warn("Null values in table content. How could this happen?");
}
return values;
}

View File

@@ -43,10 +43,6 @@ import com.vaadin.ui.themes.ValoTheme;
/**
* Abstract class for target/ds tag token layout.
*
*
*
*
*/
public abstract class AbstractTagToken<T extends BaseEntity> implements Serializable {
@@ -58,9 +54,9 @@ public abstract class AbstractTagToken<T extends BaseEntity> implements Serializ
protected IndexedContainer container;
protected final Map<Long, TagData> tagDetails = new HashMap<>();
protected final transient Map<Long, TagData> tagDetails = new HashMap<>();
protected final Map<Long, TagData> tokensAdded = new HashMap<>();
protected final transient Map<Long, TagData> tokensAdded = new HashMap<>();
protected CssLayout tokenLayout = new CssLayout();

View File

@@ -14,7 +14,6 @@ import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.hawkbit.repository.model.BaseEntity;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.ui.common.CommonDialogWindow;
import org.eclipse.hawkbit.ui.common.UserDetailsFormatter;
import org.eclipse.hawkbit.ui.decorators.SPUIButtonDecorator;
import org.eclipse.hawkbit.ui.decorators.SPUIComboBoxDecorator;
@@ -33,10 +32,8 @@ import com.vaadin.server.FontAwesome;
import com.vaadin.server.Resource;
import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.Component;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Link;
@@ -140,24 +137,6 @@ public final class SPUIComponentProvider {
return SPUILabelDecorator.getDeocratedLabel(name, type);
}
/**
* Get window component.
*
* @param caption
* window caption
* @param id
* window id
* @param type
* type of window
* @return Window
*/
public static CommonDialogWindow getWindow(final String caption, final String id, final String type,
final Component content, final ClickListener saveButtonClickListener,
final ClickListener cancelButtonClickListener, final String helpLink) {
return SPUIWindowDecorator.getDeocratedWindow(caption, id, type, content, saveButtonClickListener,
cancelButtonClickListener, helpLink);
}
/**
* Get window component.
*
@@ -204,6 +183,8 @@ public final class SPUIComponentProvider {
/**
* Get Label UI component. *
*
* @param caption
* set the caption of the textArea
* @param style
* set style
* @param styleName

View File

@@ -8,15 +8,16 @@
*/
package org.eclipse.hawkbit.ui.decorators;
import org.apache.commons.lang3.StringUtils;
import com.vaadin.server.Resource;
import com.vaadin.ui.Button;
import com.vaadin.ui.themes.ValoTheme;
/**
* Button with icon decorator.
*
* Decorator class for a borderless Button with an icon.
*/
public class SPUIButtonStyleBorderWithIcon implements SPUIButtonDecorator {
public class SPUIButtonStyleNoBorderWithIcon implements SPUIButtonDecorator {
private Button button;
@@ -24,23 +25,25 @@ public class SPUIButtonStyleBorderWithIcon implements SPUIButtonDecorator {
public Button decorate(final Button button, final String style, final boolean setStyle, final Resource icon) {
this.button = button;
setButtonStyle(style, setStyle);
setButtonIcon(icon);
button.setSizeFull();
button.addStyleName(ValoTheme.LABEL_SMALL);
button.setSizeFull();
button.addStyleName(ValoTheme.BUTTON_BORDERLESS_COLORED);
setOrAddButtonStyle(style, setStyle);
setButtonIcon(icon);
return button;
}
private void setButtonStyle(final String style, final boolean setStyle) {
private void setOrAddButtonStyle(final String style, final boolean setStyle) {
if (style == null) {
if (StringUtils.isEmpty(style)) {
return;
}
if (setStyle) {
// overwrite all other styles
button.setStyleName(style);
} else {
button.addStyleName(style);

View File

@@ -9,9 +9,11 @@
package org.eclipse.hawkbit.ui.decorators;
import org.eclipse.hawkbit.ui.common.CommonDialogWindow;
import org.eclipse.hawkbit.ui.utils.I18N;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
import com.vaadin.ui.AbstractLayout;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.Component;
import com.vaadin.ui.Window;
@@ -42,12 +44,13 @@ public final class SPUIWindowDecorator {
* window type
* @return Window
*/
public static CommonDialogWindow getDeocratedWindow(final String caption, final String id, final String type,
public static CommonDialogWindow getWindow(final String caption, final String id, final String type,
final Component content, final ClickListener saveButtonClickListener,
final ClickListener cancelButtonClickListener, final String helpLink) {
final ClickListener cancelButtonClickListener, final String helpLink, final AbstractLayout layout,
final I18N i18n) {
final CommonDialogWindow window = new CommonDialogWindow(caption, content, helpLink, saveButtonClickListener,
cancelButtonClickListener);
cancelButtonClickListener, layout, i18n);
if (null != id) {
window.setId(id);
}

View File

@@ -9,6 +9,7 @@
package org.eclipse.hawkbit.ui.distributions.disttype;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
@@ -16,7 +17,6 @@ import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.ui.colorpicker.ColorPickerConstants;
import org.eclipse.hawkbit.ui.colorpicker.ColorPickerHelper;
import org.eclipse.hawkbit.ui.common.DistributionSetTypeBeanQuery;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
@@ -49,8 +49,6 @@ import com.vaadin.ui.Component;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Table;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.components.colorpicker.ColorChangeListener;
import com.vaadin.ui.components.colorpicker.ColorSelector;
import com.vaadin.ui.themes.ValoTheme;
/**
@@ -58,8 +56,7 @@ import com.vaadin.ui.themes.ValoTheme;
*/
@SpringComponent
@ViewScope
public class CreateUpdateDistSetTypeLayout extends CreateUpdateTypeLayout
implements ColorChangeListener, ColorSelector {
public class CreateUpdateDistSetTypeLayout extends CreateUpdateTypeLayout {
private static final long serialVersionUID = -5169398523815877767L;
private static final Logger LOG = LoggerFactory.getLogger(CreateUpdateDistSetTypeLayout.class);
@@ -82,8 +79,12 @@ public class CreateUpdateDistSetTypeLayout extends CreateUpdateTypeLayout
private Table sourceTable;
private Table selectedTable;
private IndexedContainer selectedTablecontainer;
private IndexedContainer sourceTablecontainer;
private IndexedContainer selectedTableContainer;
private IndexedContainer sourceTableContainer;
private IndexedContainer originalSelectedTableContainer;
private Map<CheckBox, Boolean> mandatoryCheckboxMap;
@Override
protected void createRequiredComponents() {
@@ -103,7 +104,6 @@ public class CreateUpdateDistSetTypeLayout extends CreateUpdateTypeLayout
tagDesc = SPUIComponentProvider.getTextArea(i18n.get("textfield.description"), "",
ValoTheme.TEXTFIELD_TINY + " " + SPUIDefinitions.DIST_SET_TYPE_DESC, false, "",
i18n.get("textfield.description"), SPUILabelDefinitions.TEXT_AREA_MAX_LENGTH);
tagDesc.setId(SPUIDefinitions.NEW_DISTRIBUTION_TYPE_DESC);
tagDesc.setImmediate(true);
tagDesc.setNullRepresentation("");
@@ -159,9 +159,6 @@ public class CreateUpdateDistSetTypeLayout extends CreateUpdateTypeLayout
return twinColumnLayout;
}
/**
*
*/
private void buildSelectedTable() {
selectedTable = new Table();
@@ -176,13 +173,14 @@ public class CreateUpdateDistSetTypeLayout extends CreateUpdateTypeLayout
selectedTable.addStyleName("dist_type_twin-table");
selectedTable.setSizeFull();
createSelectedTableContainer();
selectedTable.setContainerDataSource(selectedTablecontainer);
selectedTable.setContainerDataSource(selectedTableContainer);
addTooltTipToSelectedTable();
selectedTable.setImmediate(true);
selectedTable.setVisibleColumns(DIST_TYPE_NAME, DIST_TYPE_MANDATORY);
selectedTable.setColumnHeaders(i18n.get("header.dist.twintable.selected"), STAR);
selectedTable.setColumnExpandRatio(DIST_TYPE_NAME, 0.75f);
selectedTable.setColumnExpandRatio(DIST_TYPE_MANDATORY, 0.25f);
selectedTable.setColumnExpandRatio(DIST_TYPE_NAME, 0.75F);
selectedTable.setColumnExpandRatio(DIST_TYPE_MANDATORY, 0.25F);
selectedTable.setRequired(true);
}
private void addTooltTipToSelectedTable() {
@@ -218,14 +216,13 @@ public class CreateUpdateDistSetTypeLayout extends CreateUpdateTypeLayout
sourceTable.addStyleName(ValoTheme.TABLE_NO_VERTICAL_LINES);
sourceTable.addStyleName(ValoTheme.TABLE_SMALL);
sourceTable.setImmediate(true);
// sourceTable
sourceTable.setSizeFull();
sourceTable.addStyleName("dist_type_twin-table");
sourceTable.setSortEnabled(false);
sourceTablecontainer = new IndexedContainer();
sourceTablecontainer.addContainerProperty(DIST_TYPE_NAME, String.class, "");
sourceTablecontainer.addContainerProperty(DIST_TYPE_DESCRIPTION, String.class, "");
sourceTable.setContainerDataSource(sourceTablecontainer);
sourceTableContainer = new IndexedContainer();
sourceTableContainer.addContainerProperty(DIST_TYPE_NAME, String.class, "");
sourceTableContainer.addContainerProperty(DIST_TYPE_DESCRIPTION, String.class, "");
sourceTable.setContainerDataSource(sourceTableContainer);
sourceTable.setVisibleColumns(new Object[] { DIST_TYPE_NAME });
sourceTable.setColumnHeaders(i18n.get("header.dist.twintable.available"));
@@ -237,20 +234,29 @@ public class CreateUpdateDistSetTypeLayout extends CreateUpdateTypeLayout
private void createSelectedTableContainer() {
selectedTablecontainer = new IndexedContainer();
selectedTablecontainer.addContainerProperty(DIST_TYPE_NAME, String.class, "");
selectedTablecontainer.addContainerProperty(DIST_TYPE_DESCRIPTION, String.class, "");
selectedTablecontainer.addContainerProperty(DIST_TYPE_MANDATORY, CheckBox.class, null);
selectedTableContainer = new IndexedContainer();
selectedTableContainer.addContainerProperty(DIST_TYPE_NAME, String.class, "");
selectedTableContainer.addContainerProperty(DIST_TYPE_DESCRIPTION, String.class, "");
selectedTableContainer.addContainerProperty(DIST_TYPE_MANDATORY, CheckBox.class, null);
}
private void createOriginalSelectedTableContainer() {
originalSelectedTableContainer = new IndexedContainer();
originalSelectedTableContainer.addContainerProperty(DIST_TYPE_NAME, String.class, "");
originalSelectedTableContainer.addContainerProperty(DIST_TYPE_DESCRIPTION, String.class, "");
originalSelectedTableContainer.addContainerProperty(DIST_TYPE_MANDATORY, CheckBox.class, null);
}
@SuppressWarnings("unchecked")
private void addSMType() {
final Set<Long> selectedIds = (Set<Long>) sourceTable.getValue();
if (null != selectedIds && !selectedIds.isEmpty()) {
for (final Long id : selectedIds) {
addTargetTableData(id);
}
if (selectedIds == null) {
return;
}
for (final Long id : selectedIds) {
addTargetTableData(id);
}
}
@@ -258,23 +264,24 @@ public class CreateUpdateDistSetTypeLayout extends CreateUpdateTypeLayout
@SuppressWarnings("unchecked")
final Set<Long> selectedIds = (Set<Long>) selectedTable.getValue();
if (null != selectedIds && !selectedIds.isEmpty()) {
for (final Long id : selectedIds) {
addSourceTableData(id);
selectedTable.removeItem(id);
}
if (selectedIds == null) {
return;
}
for (final Long id : selectedIds) {
addSourceTableData(id);
selectedTable.removeItem(id);
}
}
@SuppressWarnings("unchecked")
private void getSourceTableData() {
sourceTablecontainer.removeAllItems();
sourceTableContainer.removeAllItems();
final Iterable<SoftwareModuleType> moduleTypeBeans = softwareManagement
.findSoftwareModuleTypesAll(new PageRequest(0, 1_000));
Item saveTblitem;
for (final SoftwareModuleType swTypeTag : moduleTypeBeans) {
saveTblitem = sourceTablecontainer.addItem(swTypeTag.getId());
saveTblitem = sourceTableContainer.addItem(swTypeTag.getId());
saveTblitem.getItemProperty(DIST_TYPE_NAME).setValue(swTypeTag.getName());
saveTblitem.getItemProperty(DIST_TYPE_DESCRIPTION).setValue(swTypeTag.getDescription());
}
@@ -307,11 +314,13 @@ public class CreateUpdateDistSetTypeLayout extends CreateUpdateTypeLayout
private void getSelectedTableItemData(final Long id) {
Item saveTblitem;
if (null != selectedTablecontainer) {
saveTblitem = selectedTablecontainer.addItem(id);
if (selectedTableContainer != null) {
saveTblitem = selectedTableContainer.addItem(id);
saveTblitem.getItemProperty(DIST_TYPE_NAME).setValue(
sourceTable.getContainerDataSource().getItem(id).getItemProperty(DIST_TYPE_NAME).getValue());
saveTblitem.getItemProperty(DIST_TYPE_MANDATORY).setValue(new CheckBox());
final CheckBox mandatoryCheckBox = new CheckBox();
window.updateAllComponents(mandatoryCheckBox);
saveTblitem.getItemProperty(DIST_TYPE_MANDATORY).setValue(mandatoryCheckBox);
saveTblitem.getItemProperty(DIST_TYPE_DESCRIPTION).setValue(
sourceTable.getContainerDataSource().getItem(id).getItemProperty(DIST_TYPE_DESCRIPTION).getValue());
}
@@ -320,10 +329,9 @@ public class CreateUpdateDistSetTypeLayout extends CreateUpdateTypeLayout
@SuppressWarnings("unchecked")
private void addSourceTableData(final Long selectedId) {
if (null != sourceTablecontainer) {
if (sourceTableContainer != null) {
Item saveTblitem;
saveTblitem = sourceTablecontainer.addItem(selectedId);
selectedTable.getContainerDataSource().getItem(selectedId).getItemProperty(DIST_TYPE_NAME);
saveTblitem = sourceTableContainer.addItem(selectedId);
saveTblitem.getItemProperty(DIST_TYPE_NAME).setValue(selectedTable.getContainerDataSource()
.getItem(selectedId).getItemProperty(DIST_TYPE_NAME).getValue());
saveTblitem.getItemProperty(DIST_TYPE_DESCRIPTION).setValue(selectedTable.getContainerDataSource()
@@ -353,20 +361,14 @@ public class CreateUpdateDistSetTypeLayout extends CreateUpdateTypeLayout
final SoftwareModuleType swModuleType = softwareManagement.findSoftwareModuleTypeByName(distTypeName);
checkMandatoryAndAddMandatoryModuleType(newDistType, isMandatory, swModuleType);
}
if (null != typeDescValue) {
newDistType.setDescription(typeDescValue);
}
newDistType.setDescription(typeDescValue);
newDistType.setColour(colorPicked);
newDistType = distributionSetManagement.createDistributionSetType(newDistType);
uiNotification.displaySuccess(i18n.get("message.save.success", new Object[] { newDistType.getName() }));
closeWindow();
eventBus.publish(this,
new DistributionSetTypeEvent(DistributionSetTypeEnum.ADD_DIST_SET_TYPE, newDistType));
} else {
uiNotification.displayValidationError(i18n.get("message.error.missing.typenameorkey"));
}
}
@@ -386,7 +388,7 @@ public class CreateUpdateDistSetTypeLayout extends CreateUpdateTypeLayout
if (null != typeNameValue) {
updateDistSetType.setName(typeNameValue);
updateDistSetType.setKey(typeKeyValue);
updateDistSetType.setDescription(null != typeDescValue ? typeDescValue : null);
updateDistSetType.setDescription(typeDescValue);
if (distributionSetManagement.countDistributionSetsByType(existingType) <= 0 && null != itemIds
&& !itemIds.isEmpty()) {
@@ -404,21 +406,17 @@ public class CreateUpdateDistSetTypeLayout extends CreateUpdateTypeLayout
distributionSetManagement.updateDistributionSetType(updateDistSetType);
uiNotification
.displaySuccess(i18n.get("message.update.success", new Object[] { updateDistSetType.getName() }));
closeWindow();
eventBus.publish(this,
new DistributionSetTypeEvent(DistributionSetTypeEnum.UPDATE_DIST_SET_TYPE, updateDistSetType));
} else {
uiNotification.displayValidationError(i18n.get("message.tag.update.mandatory"));
}
}
private void checkMandatoryAndAddMandatoryModuleType(final DistributionSetType updateDistSetType,
final Boolean isMandatory, final SoftwareModuleType swModuleType) {
if (isMandatory) {
updateDistSetType.addMandatoryModuleType(swModuleType);
} else {
updateDistSetType.addOptionalModuleType(swModuleType);
}
@@ -440,32 +438,6 @@ public class CreateUpdateDistSetTypeLayout extends CreateUpdateTypeLayout
return distSetType;
}
/*
* (non-Javadoc)
*
* @see com.vaadin.ui.components.colorpicker.HasColorChangeListener#
* addColorChangeListener(com.vaadin
* .ui.components.colorpicker.ColorChangeListener)
*/
@Override
public void addColorChangeListener(final ColorChangeListener listener) {
LOG.info("in side addColorChangeListener() ");
}
/*
* (non-Javadoc)
*
* @see com.vaadin.ui.components.colorpicker.HasColorChangeListener#
* removeColorChangeListener(com.
* vaadin.ui.components.colorpicker.ColorChangeListener)
*/
@Override
public void removeColorChangeListener(final ColorChangeListener listener) {
LOG.info("in side removeColorChangeListener() ");
}
/**
* reset the components.
*/
@@ -484,9 +456,9 @@ public class CreateUpdateDistSetTypeLayout extends CreateUpdateTypeLayout
* ValueChangeEvent
*/
@Override
protected void createOptionValueChanged(final ValueChangeEvent event) {
protected void optionValueChanged(final ValueChangeEvent event) {
super.createOptionValueChanged(event);
super.optionValueChanged(event);
if (updateTypeStr.equals(event.getProperty().getValue())) {
selectedTable.getContainerDataSource().removeAllItems();
@@ -552,18 +524,17 @@ public class CreateUpdateDistSetTypeLayout extends CreateUpdateTypeLayout
if (null != selectedTypeTag) {
tagDesc.setValue(selectedTypeTag.getDescription());
typeKey.setValue(selectedTypeTag.getKey());
if (distributionSetManagement.countDistributionSetsByType(selectedTypeTag) <= 0) {
distTypeSelectLayout.setEnabled(true);
selectedTable.setEnabled(true);
window.setSaveButtonEnabled(true);
} else {
uiNotification.displayValidationError(
selectedTypeTag.getName() + " " + i18n.get("message.error.dist.set.type.update"));
distTypeSelectLayout.setEnabled(false);
selectedTable.setEnabled(false);
window.setSaveButtonEnabled(false);
}
createOriginalSelectedTableContainer();
for (final SoftwareModuleType swModuleType : selectedTypeTag.getOptionalModuleTypes()) {
addTargetTableforUpdate(swModuleType, false);
}
@@ -583,65 +554,41 @@ public class CreateUpdateDistSetTypeLayout extends CreateUpdateTypeLayout
@SuppressWarnings("unchecked")
private void addTargetTableforUpdate(final SoftwareModuleType swModuleType, final boolean mandatory) {
Item saveTblitem;
if (null != selectedTablecontainer) {
saveTblitem = selectedTablecontainer.addItem(swModuleType.getId());
sourceTable.removeItem(swModuleType.getId());
saveTblitem.getItemProperty(DIST_TYPE_NAME).setValue(swModuleType.getName());
saveTblitem.getItemProperty(DIST_TYPE_MANDATORY).setValue(new CheckBox("", mandatory));
if (selectedTableContainer == null) {
return;
}
final Item saveTblitem = selectedTableContainer.addItem(swModuleType.getId());
sourceTable.removeItem(swModuleType.getId());
saveTblitem.getItemProperty(DIST_TYPE_NAME).setValue(swModuleType.getName());
final CheckBox mandatoryCheckbox = new CheckBox("", mandatory);
mandatoryCheckbox.setId(swModuleType.getName());
saveTblitem.getItemProperty(DIST_TYPE_MANDATORY).setValue(mandatoryCheckbox);
final Item originalItem = originalSelectedTableContainer.addItem(swModuleType.getId());
originalItem.getItemProperty(DIST_TYPE_NAME).setValue(swModuleType.getName());
originalItem.getItemProperty(DIST_TYPE_MANDATORY).setValue(mandatoryCheckbox);
window.updateAllComponents(mandatoryCheckbox);
}
@Override
protected void save(final ClickEvent event) {
if (mandatoryValuesPresent()) {
final DistributionSetType existingDistTypeByKey = distributionSetManagement
.findDistributionSetTypeByKey(typeKey.getValue());
final DistributionSetType existingDistTypeByName = distributionSetManagement
.findDistributionSetTypeByName(tagName.getValue());
if (optiongroup.getValue().equals(createTypeStr)) {
if (!checkIsDuplicateByKey(existingDistTypeByKey) && !checkIsDuplicate(existingDistTypeByName)) {
createNewDistributionSetType();
}
} else {
updateDistributionSetType(existingDistTypeByKey);
final DistributionSetType existingDistTypeByKey = distributionSetManagement
.findDistributionSetTypeByKey(typeKey.getValue());
final DistributionSetType existingDistTypeByName = distributionSetManagement
.findDistributionSetTypeByName(tagName.getValue());
if (optiongroup.getValue().equals(createTypeStr)) {
if (!checkIsDuplicateByKey(existingDistTypeByKey) && !checkIsDuplicate(existingDistTypeByName)) {
createNewDistributionSetType();
}
}
}
@Override
public void createWindow() {
reset();
window = SPUIComponentProvider.getWindow(i18n.get("caption.add.type"), null,
SPUIDefinitions.CREATE_UPDATE_WINDOW, this, this::save, this::discard, null);
}
@Override
protected void previewButtonClicked() {
if (!tagPreviewBtnClicked) {
final String selectedOption = (String) optiongroup.getValue();
if (null != selectedOption && selectedOption.equalsIgnoreCase(updateTypeStr)
&& null != tagNameComboBox.getValue()) {
final DistributionSetType existedDistType = distributionSetManagement
.findDistributionSetTypeByKey(tagNameComboBox.getValue().toString());
if (null != existedDistType) {
getColorPickerLayout().setSelectedColor(existedDistType.getColour() != null
? ColorPickerHelper.rgbToColorConverter(existedDistType.getColour())
: ColorPickerHelper.rgbToColorConverter(ColorPickerConstants.DEFAULT_COLOR));
} else {
getColorPickerLayout().setSelectedColor(
ColorPickerHelper.rgbToColorConverter(ColorPickerConstants.DEFAULT_COLOR));
}
}
getColorPickerLayout().getSelPreview().setColor(getColorPickerLayout().getSelectedColor());
mainLayout.addComponent(colorPickerLayout, 1, 0);
mainLayout.setComponentAlignment(colorPickerLayout, Alignment.MIDDLE_CENTER);
} else {
mainLayout.removeComponent(colorPickerLayout);
updateDistributionSetType(existingDistTypeByKey);
}
tagPreviewBtnClicked = !tagPreviewBtnClicked;
}
@Override
protected String getWindowCaption() {
return i18n.get("caption.add.type");
}
@Override

View File

@@ -84,7 +84,7 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet
private VerticalLayout tagsLayout;
Map<String, StringBuilder> assignedSWModule = new HashMap<>();
private final Map<String, StringBuilder> assignedSWModule = new HashMap<>();
/**
* softwareLayout Initialize the component.
@@ -259,8 +259,7 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet
@Override
protected void onEdit(final ClickEvent event) {
final Window newDistWindow = distributionAddUpdateWindowLayout.getWindow();
distributionAddUpdateWindowLayout.populateValuesOfDistribution(getSelectedBaseEntityId());
final Window newDistWindow = distributionAddUpdateWindowLayout.getWindow(getSelectedBaseEntityId());
newDistWindow.setCaption(getI18n().get("caption.update.dist"));
UI.getCurrent().addWindow(newDistWindow);
newDistWindow.setVisible(Boolean.TRUE);

View File

@@ -152,7 +152,7 @@ public class DistributionSetTableHeader extends AbstractTableHeader {
@Override
protected void addNewItem(final ClickEvent event) {
final Window newDistWindow = addUpdateWindowLayout.getWindow();
final Window newDistWindow = addUpdateWindowLayout.getWindow(null);
newDistWindow.setCaption(i18n.get("caption.add.new.dist"));
UI.getCurrent().addWindow(newDistWindow);
newDistWindow.setVisible(Boolean.TRUE);

View File

@@ -20,17 +20,16 @@ import org.eclipse.hawkbit.ui.colorpicker.ColorPickerHelper;
import org.eclipse.hawkbit.ui.colorpicker.ColorPickerLayout;
import org.eclipse.hawkbit.ui.common.CommonDialogWindow;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.decorators.SPUIWindowDecorator;
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
import org.eclipse.hawkbit.ui.utils.I18N;
import org.eclipse.hawkbit.ui.utils.SPUIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions;
import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions;
import org.eclipse.hawkbit.ui.utils.UINotification;
import org.springframework.beans.factory.annotation.Autowired;
import org.vaadin.spring.events.EventBus;
import com.google.common.base.Strings;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.server.Page;
@@ -87,7 +86,6 @@ public abstract class AbstractCreateUpdateTagLayout extends CustomComponent
protected CommonDialogWindow window;
protected Label colorLabel;
protected Label madatoryLabel;
protected TextField tagName;
protected TextArea tagDesc;
protected Button tagColorPreviewBtn;
@@ -99,17 +97,13 @@ public abstract class AbstractCreateUpdateTagLayout extends CustomComponent
protected GridLayout mainLayout;
protected VerticalLayout contentLayout;
protected boolean tagPreviewBtnClicked = false;
protected boolean tagPreviewBtnClicked;
private String colorPicked;
protected String tagNameValue;
protected String tagDescValue;
protected void createWindow() {
reset();
setWindow(SPUIComponentProvider.getWindow(i18n.get("caption.add.tag"), null,
SPUIDefinitions.CREATE_UPDATE_WINDOW, this, this::save, this::discard, null));
}
protected abstract String getWindowCaption();
/**
* Save new tag / update new tag.
@@ -142,7 +136,6 @@ public abstract class AbstractCreateUpdateTagLayout extends CustomComponent
setSizeUndefined();
createRequiredComponents();
buildLayout();
createWindow();
addListeners();
eventBus.subscribe(this);
}
@@ -157,7 +150,6 @@ public abstract class AbstractCreateUpdateTagLayout extends CustomComponent
createTagStr = i18n.get("label.create.tag");
updateTagStr = i18n.get("label.update.tag");
comboLabel = SPUIComponentProvider.getLabel(i18n.get("label.choose.tag"), null);
madatoryLabel = getMandatoryLabel();
colorLabel = SPUIComponentProvider.getLabel(i18n.get("label.choose.tag.color"), null);
colorLabel.addStyleName(SPUIDefinitions.COLOR_LABEL_STYLE);
@@ -169,7 +161,6 @@ public abstract class AbstractCreateUpdateTagLayout extends CustomComponent
tagDesc = SPUIComponentProvider.getTextArea(i18n.get("textfield.description"), "",
ValoTheme.TEXTFIELD_TINY + " " + SPUIDefinitions.TAG_DESC, false, "", i18n.get("textfield.description"),
SPUILabelDefinitions.TEXT_AREA_MAX_LENGTH);
tagDesc.setId(SPUIDefinitions.NEW_TARGET_TAG_DESC);
tagDesc.setImmediate(true);
tagDesc.setNullRepresentation("");
@@ -178,6 +169,7 @@ public abstract class AbstractCreateUpdateTagLayout extends CustomComponent
i18n.get("label.combobox.tag"));
tagNameComboBox.addStyleName(SPUIDefinitions.FILTER_TYPE_COMBO_STYLE);
tagNameComboBox.setImmediate(true);
tagNameComboBox.setId(SPUIComponentIdProvider.DIST_TAG_COMBO);
tagColorPreviewBtn = new Button();
tagColorPreviewBtn.setId(SPUIComponentIdProvider.TAG_COLOR_PREVIEW_ID);
@@ -200,7 +192,6 @@ public abstract class AbstractCreateUpdateTagLayout extends CustomComponent
formLayout.addComponent(optiongroup);
formLayout.addComponent(comboLayout);
formLayout.addComponent(madatoryLabel);
formLayout.addComponent(tagName);
formLayout.addComponent(tagDesc);
formLayout.addStyleName("form-lastrow");
@@ -215,6 +206,10 @@ public abstract class AbstractCreateUpdateTagLayout extends CustomComponent
mainLayout.setSizeFull();
mainLayout.addComponent(contentLayout, 0, 0);
colorPickerLayout.setVisible(false);
mainLayout.addComponent(colorPickerLayout, 1, 0);
mainLayout.setComponentAlignment(colorPickerLayout, Alignment.MIDDLE_CENTER);
setCompositionRoot(mainLayout);
tagName.focus();
}
@@ -234,13 +229,10 @@ public abstract class AbstractCreateUpdateTagLayout extends CustomComponent
protected void previewButtonClicked() {
if (!tagPreviewBtnClicked) {
setColor();
mainLayout.getComponent(1, 0);
mainLayout.addComponent(colorPickerLayout, 1, 0);
mainLayout.setComponentAlignment(colorPickerLayout, Alignment.MIDDLE_CENTER);
} else {
mainLayout.removeComponent(colorPickerLayout);
}
tagPreviewBtnClicked = !tagPreviewBtnClicked;
colorPickerLayout.setVisible(tagPreviewBtnClicked);
}
private void setColor() {
@@ -270,12 +262,6 @@ public abstract class AbstractCreateUpdateTagLayout extends CustomComponent
}
}
protected Label getMandatoryLabel() {
final Label label = new Label(i18n.get("label.mandatory.field"));
label.setStyleName(SPUIStyleDefinitions.SP_TEXTFIELD_ERROR + " " + ValoTheme.LABEL_SMALL);
return label;
}
private void tagNameChosen(final ValueChangeEvent event) {
final String tagSelected = (String) event.getProperty().getValue();
if (null != tagSelected) {
@@ -283,6 +269,7 @@ public abstract class AbstractCreateUpdateTagLayout extends CustomComponent
} else {
resetTagNameField();
}
window.setOrginaleValues();
}
protected void resetTagNameField() {
@@ -290,7 +277,6 @@ public abstract class AbstractCreateUpdateTagLayout extends CustomComponent
tagName.clear();
tagDesc.clear();
restoreComponentStyles();
mainLayout.removeComponent(colorPickerLayout);
colorPickerLayout.setSelectedColor(colorPickerLayout.getDefaultColor());
colorPickerLayout.getSelPreview().setColor(colorPickerLayout.getSelectedColor());
tagPreviewBtnClicked = false;
@@ -327,7 +313,7 @@ public abstract class AbstractCreateUpdateTagLayout extends CustomComponent
getPreviewButtonColor(ColorPickerConstants.DEFAULT_COLOR);
colorPickerLayout.getSelPreview()
.setColor(ColorPickerHelper.rgbToColorConverter(ColorPickerConstants.DEFAULT_COLOR));
mainLayout.removeComponent(colorPickerLayout);
window.setOrginaleValues();
}
/**
@@ -342,9 +328,9 @@ public abstract class AbstractCreateUpdateTagLayout extends CustomComponent
// hide target name combo
comboLayout.removeComponent(comboLabel);
comboLayout.removeComponent(tagNameComboBox);
mainLayout.removeComponent(colorPickerLayout);
// Default green color
colorPickerLayout.setVisible(false);
colorPickerLayout.setSelectedColor(colorPickerLayout.getDefaultColor());
colorPickerLayout.getSelPreview().setColor(colorPickerLayout.getSelectedColor());
tagPreviewBtnClicked = false;
@@ -389,6 +375,7 @@ public abstract class AbstractCreateUpdateTagLayout extends CustomComponent
*/
protected void createDynamicStyleForComponents(final TextField tagName, final TextArea tagDesc,
final String taregtTagColor) {
tagName.removeStyleName(SPUIDefinitions.TAG_NAME);
tagDesc.removeStyleName(SPUIDefinitions.TAG_DESC);
getTargetDynamicStyles(taregtTagColor);
@@ -435,11 +422,7 @@ public abstract class AbstractCreateUpdateTagLayout extends CustomComponent
createDynamicStyleForComponents(tagName, tagDesc, colorPickedPreview);
colorPickerLayout.getColorSelect().setColor(colorPickerLayout.getSelPreview().getColor());
}
}
protected void closeWindow() {
window.close();
UI.getCurrent().removeWindow(window);
}
/**
@@ -448,6 +431,7 @@ public abstract class AbstractCreateUpdateTagLayout extends CustomComponent
protected void createOptionGroup(final boolean hasCreatePermission, final boolean hasUpdatePermission) {
optiongroup = new OptionGroup("Select Action");
optiongroup.setId(SPUIComponentIdProvider.OPTION_GROUP);
optiongroup.addStyleName(ValoTheme.OPTIONGROUP_SMALL);
optiongroup.addStyleName("custom-option-group");
optiongroup.setNullSelectionAllowed(false);
@@ -472,27 +456,17 @@ public abstract class AbstractCreateUpdateTagLayout extends CustomComponent
}
}
@Override
public void addColorChangeListener(final ColorChangeListener listener) {
}
@Override
public void removeColorChangeListener(final ColorChangeListener listener) {
}
public ColorPickerLayout getColorPickerLayout() {
return colorPickerLayout;
}
public CommonDialogWindow getWindow() {
reset();
window = SPUIWindowDecorator.getWindow(getWindowCaption(), null, SPUIDefinitions.CREATE_UPDATE_WINDOW, this,
this::save, this::discard, null, mainLayout, i18n);
return window;
}
public void setWindow(final CommonDialogWindow window) {
this.window = window;
}
/**
* Value change listeners implementations of sliders.
*/
@@ -565,7 +539,6 @@ public abstract class AbstractCreateUpdateTagLayout extends CustomComponent
tagManagement.updateDistributionSetTag((DistributionSetTag) targetObj);
}
uiNotification.displaySuccess(i18n.get("message.update.success", new Object[] { targetObj.getName() }));
closeWindow();
} else {
uiNotification.displayValidationError(i18n.get("message.tag.update.mandatory"));
}
@@ -587,27 +560,6 @@ public abstract class AbstractCreateUpdateTagLayout extends CustomComponent
getPreviewButtonColor(previewColor);
}
/**
*
* @return
*/
protected Boolean mandatoryValuesPresent() {
if (Strings.isNullOrEmpty(tagName.getValue())) {
if (optiongroup.getValue().equals(createTagStr)) {
displayValidationError(SPUILabelDefinitions.MISSING_TAG_NAME);
}
if (optiongroup.getValue().equals(updateTagStr)) {
if (null == tagNameComboBox.getValue()) {
displayValidationError(i18n.get(MESSAGE_ERROR_MISSING_TAGNAME));
} else {
displayValidationError(SPUILabelDefinitions.MISSING_TAG_NAME);
}
}
return Boolean.FALSE;
}
return Boolean.TRUE;
}
protected Boolean checkIsDuplicate(final Tag existingTag) {
if (existingTag != null) {
displayValidationError(i18n.get("message.tag.duplicate.check", new Object[] { existingTag.getName() }));
@@ -644,4 +596,16 @@ public abstract class AbstractCreateUpdateTagLayout extends CustomComponent
return formLayout;
}
public GridLayout getMainLayout() {
return mainLayout;
}
@Override
public void addColorChangeListener(final ColorChangeListener listener) {
}
@Override
public void removeColorChangeListener(final ColorChangeListener listener) {
}
}

View File

@@ -17,9 +17,7 @@ import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
import org.eclipse.hawkbit.ui.utils.SPUIComponentIdProvider;
import org.eclipse.hawkbit.ui.utils.SPUIDefinitions;
import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions;
import com.google.common.base.Strings;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.server.Page;
import com.vaadin.shared.ui.colorpicker.Color;
@@ -33,12 +31,10 @@ import com.vaadin.ui.components.colorpicker.ColorSelector;
import com.vaadin.ui.themes.ValoTheme;
/**
*
* Superclass defining common properties and methods for creating/updating
* types.
*
*/
public class CreateUpdateTypeLayout extends AbstractCreateUpdateTagLayout {
public abstract class CreateUpdateTypeLayout extends AbstractCreateUpdateTagLayout {
private static final long serialVersionUID = 5732904956185988397L;
@@ -52,7 +48,7 @@ public class CreateUpdateTypeLayout extends AbstractCreateUpdateTagLayout {
@Override
protected void addListeners() {
super.addListeners();
optiongroup.addValueChangeListener(this::createOptionValueChanged);
optiongroup.addValueChangeListener(this::optionValueChanged);
}
@Override
@@ -61,7 +57,6 @@ public class CreateUpdateTypeLayout extends AbstractCreateUpdateTagLayout {
createTypeStr = i18n.get("label.create.type");
updateTypeStr = i18n.get("label.update.type");
comboLabel = SPUIComponentProvider.getLabel(i18n.get("label.choose.type"), null);
madatoryLabel = getMandatoryLabel();
colorLabel = SPUIComponentProvider.getLabel(i18n.get("label.choose.type.color"), null);
colorLabel.addStyleName(SPUIDefinitions.COLOR_LABEL_STYLE);
@@ -137,7 +132,8 @@ public class CreateUpdateTypeLayout extends AbstractCreateUpdateTagLayout {
* @param event
* ValueChangeEvent
*/
protected void createOptionValueChanged(final ValueChangeEvent event) {
@Override
protected void optionValueChanged(final ValueChangeEvent event) {
if (updateTypeStr.equals(event.getProperty().getValue())) {
tagName.clear();
@@ -151,7 +147,6 @@ public class CreateUpdateTypeLayout extends AbstractCreateUpdateTagLayout {
} else {
typeKey.setEnabled(true);
tagName.setEnabled(true);
window.setSaveButtonEnabled(true);
tagName.clear();
tagDesc.clear();
typeKey.clear();
@@ -203,6 +198,7 @@ public class CreateUpdateTypeLayout extends AbstractCreateUpdateTagLayout {
protected void createOptionGroup(final boolean hasCreatePermission, final boolean hasUpdatePermission) {
optiongroup = new OptionGroup("Select Action");
optiongroup.setId(SPUIComponentIdProvider.OPTION_GROUP);
optiongroup.addStyleName(ValoTheme.OPTIONGROUP_SMALL);
optiongroup.addStyleName("custom-option-group");
optiongroup.setNullSelectionAllowed(false);
@@ -285,24 +281,6 @@ public class CreateUpdateTypeLayout extends AbstractCreateUpdateTagLayout {
return Boolean.FALSE;
}
@Override
protected Boolean mandatoryValuesPresent() {
if (Strings.isNullOrEmpty(tagName.getValue()) || Strings.isNullOrEmpty(typeKey.getValue())) {
if (optiongroup.getValue().equals(createTypeStr)) {
displayValidationError(SPUILabelDefinitions.MISSING_TYPE_NAME_KEY);
}
if (optiongroup.getValue().equals(updateTypeStr)) {
if (null == tagNameComboBox.getValue()) {
displayValidationError(i18n.get("message.error.missing.tagName"));
} else {
displayValidationError(SPUILabelDefinitions.MISSING_TAG_NAME);
}
}
return Boolean.FALSE;
}
return Boolean.TRUE;
}
@Override
protected void save(final ClickEvent event) {
// is implemented in the inherited class

View File

@@ -8,10 +8,8 @@
*/
package org.eclipse.hawkbit.ui.management.dstable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -27,8 +25,11 @@ import org.eclipse.hawkbit.repository.model.TenantMetaData;
import org.eclipse.hawkbit.ui.common.CommonDialogWindow;
import org.eclipse.hawkbit.ui.common.DistributionSetIdName;
import org.eclipse.hawkbit.ui.common.DistributionSetTypeBeanQuery;
import org.eclipse.hawkbit.ui.common.table.BaseEntityEventType;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.decorators.SPUIWindowDecorator;
import org.eclipse.hawkbit.ui.distributions.dstable.DistributionSetTable;
import org.eclipse.hawkbit.ui.management.event.DistributionTableEvent;
import org.eclipse.hawkbit.ui.management.event.DragEvent;
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
import org.eclipse.hawkbit.ui.utils.I18N;
@@ -45,26 +46,18 @@ import org.vaadin.addons.lazyquerycontainer.LazyQueryContainer;
import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition;
import org.vaadin.spring.events.EventBus;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.event.FieldEvents.TextChangeEvent;
import com.vaadin.event.FieldEvents.TextChangeListener;
import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.ViewScope;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.Component;
import com.vaadin.ui.CustomComponent;
import com.vaadin.ui.FormLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.themes.ValoTheme;
/**
*
*
* WindowContent for adding/editing a Distribution
*/
@SpringComponent
@ViewScope
@@ -97,24 +90,12 @@ public class DistributionAddUpdateWindowLayout extends CustomComponent {
private TextField distNameTextField;
private TextField distVersionTextField;
private Label madatoryLabel;
private TextArea descTextArea;
private CheckBox reqMigStepCheckbox;
private ComboBox distsetTypeNameComboBox;
private boolean editDistribution = Boolean.FALSE;
private Long editDistId;
private CommonDialogWindow addDistributionWindow;
private String originalDistName;
private String originalDistVersion;
private String originalDistDescription;
private Boolean originalReqMigStep;
private String originalDistSetType;
private final List<Component> changedComponents = new ArrayList<>();
private ValueChangeListener reqMigStepCheckboxListerner;
private TextChangeListener descTextAreaListener;
private TextChangeListener distNameTextFieldListener;
private TextChangeListener distVersionTextFieldListener;
private ValueChangeListener distsetTypeNameComboBoxListener;
private CommonDialogWindow window;
private FormLayout formLayout;
@@ -128,16 +109,10 @@ public class DistributionAddUpdateWindowLayout extends CustomComponent {
}
private void buildLayout() {
/*
* The main layout of the window contains mandatory info, textboxes
* (controller Id, name & description) and action buttons layout
*/
addStyleName("lay-color");
setSizeUndefined();
formLayout = new FormLayout();
formLayout.addComponent(madatoryLabel);
formLayout.addComponent(distsetTypeNameComboBox);
formLayout.addComponent(distNameTextField);
formLayout.addComponent(distVersionTextField);
@@ -145,7 +120,6 @@ public class DistributionAddUpdateWindowLayout extends CustomComponent {
formLayout.addComponent(reqMigStepCheckbox);
setCompositionRoot(formLayout);
distNameTextField.focus();
}
@@ -169,6 +143,7 @@ public class DistributionAddUpdateWindowLayout extends CustomComponent {
distsetTypeNameComboBox.setImmediate(true);
distsetTypeNameComboBox.setNullSelectionAllowed(false);
distsetTypeNameComboBox.setId(SPUIComponentIdProvider.DIST_ADD_DISTSETTYPE);
populateDistSetTypeNameCombo();
descTextArea = SPUIComponentProvider.getTextArea(i18n.get("textfield.description"), "text-area-style",
ValoTheme.TEXTAREA_TINY, false, null, i18n.get("textfield.description"),
@@ -176,10 +151,6 @@ public class DistributionAddUpdateWindowLayout extends CustomComponent {
descTextArea.setId(SPUIComponentIdProvider.DIST_ADD_DESC);
descTextArea.setNullRepresentation("");
/* Label for mandatory symbol */
madatoryLabel = new Label(i18n.get("label.mandatory.field"));
madatoryLabel.setStyleName(SPUIStyleDefinitions.SP_TEXTFIELD_ERROR + " " + ValoTheme.LABEL_SMALL);
reqMigStepCheckbox = SPUIComponentProvider.getCheckBox(i18n.get("checkbox.dist.required.migration.step"),
"dist-checkbox-style", null, false, "");
reqMigStepCheckbox.addStyleName(ValoTheme.CHECKBOX_SMALL);
@@ -205,27 +176,17 @@ public class DistributionAddUpdateWindowLayout extends CustomComponent {
return disttypeContainer;
}
private void enableSaveButton() {
addDistributionWindow.setSaveButtonEnabled(true);
}
private DistributionSetType getDefaultDistributionSetType() {
final TenantMetaData tenantMetaData = systemManagement.getTenantMetadata();
return tenantMetaData.getDefaultDsType();
}
private void disableSaveButton() {
addDistributionWindow.setSaveButtonEnabled(false);
}
private void saveDistribution() {
/* add new or update target */
if (editDistribution) {
updateDistribution();
} else {
addNewDistribution();
}
}
/**
@@ -237,7 +198,7 @@ public class DistributionAddUpdateWindowLayout extends CustomComponent {
final String distSetTypeName = HawkbitCommonUtil
.trimAndNullIfEmpty((String) distsetTypeNameComboBox.getValue());
if (mandatoryCheck(name, version, distSetTypeName) && duplicateCheck(name, version)) {
if (duplicateCheck(name, version)) {
final DistributionSet currentDS = distributionSetManagement.findDistributionSetByIdWithDetails(editDistId);
final String desc = HawkbitCommonUtil.trimAndNullIfEmpty(descTextArea.getValue());
final boolean isMigStepReq = reqMigStepCheckbox.getValue();
@@ -248,28 +209,16 @@ public class DistributionAddUpdateWindowLayout extends CustomComponent {
distributionSetManagement.updateDistributionSet(currentDS);
notificationMessage.displaySuccess(i18n.get("message.new.dist.save.success",
new Object[] { currentDS.getName(), currentDS.getVersion() }));
// update table row+details layout
eventBus.publish(this, new DistributionTableEvent(BaseEntityEventType.UPDATED_ENTITY, currentDS));
} catch (final EntityAlreadyExistsException entityAlreadyExistsException) {
LOG.error("Update distribution failed {}", entityAlreadyExistsException);
notificationMessage.displayValidationError(
i18n.get("message.distribution.no.update", currentDS.getName() + ":" + currentDS.getVersion()));
}
closeThisWindow();
}
}
private void addListeners() {
reqMigStepCheckboxListerner = event -> checkValueChanged(originalReqMigStep, event);
descTextAreaListener = event -> checkValueChanged(originalDistDescription, event);
distNameTextFieldListener = event -> checkValueChanged(originalDistName, event);
distVersionTextFieldListener = event -> checkValueChanged(originalDistVersion, event);
distsetTypeNameComboBoxListener = event -> checkValueChanged(originalDistSetType, event);
reqMigStepCheckbox.addValueChangeListener(reqMigStepCheckboxListerner);
descTextArea.addTextChangeListener(descTextAreaListener);
distNameTextField.addTextChangeListener(distNameTextFieldListener);
distVersionTextField.addTextChangeListener(distVersionTextFieldListener);
distsetTypeNameComboBox.addValueChangeListener(distsetTypeNameComboBoxListener);
}
/**
* Add new Distribution set.
*/
@@ -280,7 +229,7 @@ public class DistributionAddUpdateWindowLayout extends CustomComponent {
final String distSetTypeName = HawkbitCommonUtil
.trimAndNullIfEmpty((String) distsetTypeNameComboBox.getValue());
if (mandatoryCheck(name, version, distSetTypeName) && duplicateCheck(name, version)) {
if (duplicateCheck(name, version)) {
final String desc = HawkbitCommonUtil.trimAndNullIfEmpty(descTextArea.getValue());
final boolean isMigStepReq = reqMigStepCheckbox.getValue();
DistributionSet newDist = entityFactory.generateDistributionSet();
@@ -290,8 +239,6 @@ public class DistributionAddUpdateWindowLayout extends CustomComponent {
notificationMessage.displaySuccess(i18n.get("message.new.dist.save.success",
new Object[] { newDist.getName(), newDist.getVersion() }));
/* close the window */
closeThisWindow();
final Set<DistributionSetIdName> s = new HashSet<>();
s.add(new DistributionSetIdName(newDist.getId(),newDist.getName(),newDist.getVersion()));
@@ -299,14 +246,6 @@ public class DistributionAddUpdateWindowLayout extends CustomComponent {
}
}
/**
* Close window.
*/
private void closeThisWindow() {
addDistributionWindow.close();
UI.getCurrent().removeWindow(addDistributionWindow);
}
/**
* Set Values for Distribution set.
*
@@ -350,47 +289,6 @@ public class DistributionAddUpdateWindowLayout extends CustomComponent {
}
}
/**
* Mandatory Check.
*
* @param name
* as String
* @param version
* as String
* @param selectedJVM
* as String
* @param selectedAgentHub
* as String
* @param selectedOs
* as String
* @return boolean as flag
*/
private boolean mandatoryCheck(final String name, final String version, final String distSetTypeName) {
if (name == null || version == null || distSetTypeName == null) {
if (name == null) {
distNameTextField.addStyleName(SPUIStyleDefinitions.SP_TEXTFIELD_ERROR);
}
if (version == null) {
distVersionTextField.addStyleName(SPUIStyleDefinitions.SP_TEXTFIELD_ERROR);
}
if (distSetTypeName == null) {
distsetTypeNameComboBox.addStyleName(SPUIStyleDefinitions.SP_COMBOFIELD_ERROR);
}
notificationMessage.displayValidationError(i18n.get("message.mandatory.check"));
return false;
}
return true;
}
private void discardDistribution() {
/* Just close this window */
distsetTypeNameComboBox.removeValueChangeListener(distsetTypeNameComboBoxListener);
closeThisWindow();
}
/**
* clear all the fields.
*/
@@ -403,139 +301,52 @@ public class DistributionAddUpdateWindowLayout extends CustomComponent {
distsetTypeNameComboBox.removeStyleName(SPUIStyleDefinitions.SP_COMBOFIELD_ERROR);
descTextArea.clear();
reqMigStepCheckbox.clear();
if (addDistributionWindow != null) {
addDistributionWindow.setSaveButtonEnabled(true);
}
removeListeners();
changedComponents.clear();
}
private void populateRequiredComponents() {
populateDistSetTypeNameCombo();
}
private void removeListeners() {
reqMigStepCheckbox.removeValueChangeListener(reqMigStepCheckboxListerner);
descTextArea.removeTextChangeListener(descTextAreaListener);
distNameTextField.removeTextChangeListener(distNameTextFieldListener);
distVersionTextField.removeTextChangeListener(distVersionTextFieldListener);
}
public void setOriginalDistName(final String originalDistName) {
this.originalDistName = originalDistName;
}
public void setOriginalDistVersion(final String originalDistVersion) {
this.originalDistVersion = originalDistVersion;
}
public void setOriginalDistDescription(final String originalDistDescription) {
this.originalDistDescription = originalDistDescription;
}
private void checkValueChanged(final String originalValue, final TextChangeEvent event) {
if (editDistribution) {
final String newValue = event.getText();
if (!originalValue.equalsIgnoreCase(newValue)) {
changedComponents.add(event.getComponent());
} else {
changedComponents.remove(event.getComponent());
}
enableDisableSaveButton();
}
}
private void checkValueChanged(final Boolean originalValue, final ValueChangeEvent event) {
if (editDistribution) {
if (!originalValue.equals(event.getProperty().getValue())) {
changedComponents.add(reqMigStepCheckbox);
} else {
changedComponents.remove(reqMigStepCheckbox);
}
enableDisableSaveButton();
}
}
private void checkValueChanged(final String originalValue, final ValueChangeEvent event) {
if (editDistribution) {
if (!originalValue.equals(event.getProperty().getValue())) {
changedComponents.add(distsetTypeNameComboBox);
} else {
changedComponents.remove(distsetTypeNameComboBox);
}
enableDisableSaveButton();
}
}
private void enableDisableSaveButton() {
if (changedComponents.isEmpty()) {
disableSaveButton();
} else {
enableSaveButton();
}
}
private void setOriginalReqMigStep(final Boolean originalReqMigStep) {
this.originalReqMigStep = originalReqMigStep;
}
/**
* populate data.
*
* @param editDistId
*/
public void populateValuesOfDistribution(final Long editDistId) {
private void populateValuesOfDistribution(final Long editDistId) {
this.editDistId = editDistId;
editDistribution = Boolean.TRUE;
addDistributionWindow.setSaveButtonEnabled(false);
if (editDistId == null) {
return;
}
final DistributionSet distSet = distributionSetManagement.findDistributionSetByIdWithDetails(editDistId);
if (distSet != null) {
distNameTextField.setValue(distSet.getName());
distVersionTextField.setValue(distSet.getVersion());
if (distSet.getType().isDeleted()) {
distsetTypeNameComboBox.addItem(distSet.getType().getName());
}
distsetTypeNameComboBox.setValue(distSet.getType().getName());
reqMigStepCheckbox.setValue(distSet.isRequiredMigrationStep());
if (distSet.getDescription() != null) {
descTextArea.setValue(distSet.getDescription());
}
setOriginalDistName(distSet.getName());
setOriginalDistVersion(distSet.getVersion());
setOriginalDistDescription(distSet.getDescription());
setOriginalReqMigStep(distSet.isRequiredMigrationStep());
setOriginalDistSetTYpe(distSet.getType().getName());
addListeners();
if (distSet == null) {
return;
}
editDistribution = Boolean.TRUE;
distNameTextField.setValue(distSet.getName());
distVersionTextField.setValue(distSet.getVersion());
if (distSet.getType().isDeleted()) {
distsetTypeNameComboBox.addItem(distSet.getType().getName());
}
distsetTypeNameComboBox.setValue(distSet.getType().getName());
reqMigStepCheckbox.setValue(distSet.isRequiredMigrationStep());
if (distSet.getDescription() != null) {
descTextArea.setValue(distSet.getDescription());
}
}
public CommonDialogWindow getWindow() {
public CommonDialogWindow getWindow(final Long editDistId) {
eventBus.publish(this, DragEvent.HIDE_DROP_HINT);
populateRequiredComponents();
resetComponents();
addDistributionWindow = SPUIComponentProvider.getWindow(i18n.get("caption.add.new.dist"), null,
SPUIDefinitions.CREATE_UPDATE_WINDOW, this, event -> saveDistribution(), event -> discardDistribution(),
null);
addDistributionWindow.getButtonsLayout().removeStyleName("actionButtonsMargin");
return addDistributionWindow;
populateDistSetTypeNameCombo();
populateValuesOfDistribution(editDistId);
window = SPUIWindowDecorator.getWindow(i18n.get("caption.add.new.dist"), null,
SPUIDefinitions.CREATE_UPDATE_WINDOW, this, event -> saveDistribution(), null, null, formLayout, i18n);
window.getButtonsLayout().removeStyleName("actionButtonsMargin");
return window;
}
/**
* Populate DistributionSet Type name combo.
*/
public void populateDistSetTypeNameCombo() {
private void populateDistSetTypeNameCombo() {
distsetTypeNameComboBox.setContainerDataSource(getDistSetTypeLazyQueryContainer());
distsetTypeNameComboBox.setItemCaptionPropertyId(SPUILabelDefinitions.VAR_NAME);
distsetTypeNameComboBox.setValue(getDefaultDistributionSetType().getName());
}
/**
* @param originalDistSetTYpe
* the originalDistSetTYpe to set
*/
public void setOriginalDistSetTYpe(final String originalDistSetType) {
this.originalDistSetType = originalDistSetType;
}
}

View File

@@ -77,8 +77,7 @@ public class DistributionDetails extends AbstractNamedVersionedEntityTableDetail
@Override
protected void onEdit(final ClickEvent event) {
final Window newDistWindow = distributionAddUpdateWindowLayout.getWindow();
distributionAddUpdateWindowLayout.populateValuesOfDistribution(getSelectedBaseEntityId());
final Window newDistWindow = distributionAddUpdateWindowLayout.getWindow(getSelectedBaseEntityId());
newDistWindow.setCaption(getI18n().get("caption.update.dist"));
UI.getCurrent().addWindow(newDistWindow);
newDistWindow.setVisible(Boolean.TRUE);

View File

@@ -154,7 +154,7 @@ public class DistributionTableHeader extends AbstractTableHeader {
@Override
protected void addNewItem(final ClickEvent event) {
final Window newDistWindow = distributionAddUpdateWindowLayout.getWindow();
final Window newDistWindow = distributionAddUpdateWindowLayout.getWindow(null);
newDistWindow.setCaption(i18n.get("caption.add.new.dist"));
UI.getCurrent().addWindow(newDistWindow);
newDistWindow.setVisible(Boolean.TRUE);

View File

@@ -32,9 +32,7 @@ import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.UI;
/**
*
* Class for Create/Update Tag Layout of distribution set
*
*/
@SpringComponent
@ViewScope
@@ -87,16 +85,13 @@ public class CreateUpdateDistributionTagLayoutWindow extends AbstractCreateUpdat
*/
@Override
public void save(final ClickEvent event) {
if (mandatoryValuesPresent()) {
final DistributionSetTag existingDistTag = tagManagement.findDistributionSetTag(tagName.getValue());
if (optiongroup.getValue().equals(createTagStr)) {
if (!checkIsDuplicate(existingDistTag)) {
createNewTag();
}
} else {
updateExistingTag(existingDistTag);
final DistributionSetTag existingDistTag = tagManagement.findDistributionSetTag(tagName.getValue());
if (optiongroup.getValue().equals(createTagStr)) {
if (!checkIsDuplicate(existingDistTag)) {
createNewTag();
}
} else {
updateExistingTag(existingDistTag);
}
}
@@ -181,4 +176,9 @@ public class CreateUpdateDistributionTagLayoutWindow extends AbstractCreateUpdat
setOptionGroupDefaultValue(permChecker.hasCreateDistributionPermission(),
permChecker.hasUpdateDistributionPermission());
}
@Override
protected String getWindowCaption() {
return i18n.get("caption.add.tag");
}
}

View File

@@ -17,6 +17,7 @@ import javax.annotation.PostConstruct;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.ui.utils.I18N;
import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil;
import org.eclipse.hawkbit.ui.utils.SPUIComponentIdProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.vaadin.hene.flexibleoptiongroup.FlexibleOptionGroup;
import org.vaadin.hene.flexibleoptiongroup.FlexibleOptionGroupItemComponent;
@@ -78,6 +79,7 @@ public class ActionTypeOptionGroupLayout extends HorizontalLayout {
private void createOptionGroup() {
actionTypeOptionGroup = new FlexibleOptionGroup();
actionTypeOptionGroup.setId(SPUIComponentIdProvider.ROLLOUT_ACTION_BUTTON_ID);
actionTypeOptionGroup.addItem(ActionTypeOption.SOFT);
actionTypeOptionGroup.addItem(ActionTypeOption.FORCED);
actionTypeOptionGroup.addItem(ActionTypeOption.AUTO_FORCED);

View File

@@ -10,6 +10,8 @@ package org.eclipse.hawkbit.ui.management.tag;
import java.lang.reflect.Field;
import org.eclipse.hawkbit.ui.utils.SPUIComponentIdProvider;
import com.google.common.base.Throwables;
import com.vaadin.data.Property;
import com.vaadin.data.Property.ValueChangeEvent;
@@ -44,7 +46,7 @@ public final class SpColorPickerPreview extends ColorPickerPreview implements Te
try {
final Field textField = ColorPickerPreview.class.getDeclaredField("field");
textField.setAccessible(true);
((TextField) textField.get(this)).setId("color-preview-field");
((TextField) textField.get(this)).setId(SPUIComponentIdProvider.COLOR_PREVIEW_FIELD);
((TextField) textField.get(this)).addTextChangeListener(this);
} catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
Throwables.propagate(e);

View File

@@ -18,6 +18,7 @@ import org.eclipse.hawkbit.repository.model.TargetIdName;
import org.eclipse.hawkbit.ui.common.CommonDialogWindow;
import org.eclipse.hawkbit.ui.common.table.BaseEntityEventType;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.decorators.SPUIWindowDecorator;
import org.eclipse.hawkbit.ui.management.event.DragEvent;
import org.eclipse.hawkbit.ui.management.event.TargetTableEvent;
import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil;
@@ -31,30 +32,23 @@ import org.eclipse.hawkbit.ui.utils.UINotification;
import org.springframework.beans.factory.annotation.Autowired;
import org.vaadin.spring.events.EventBus;
import com.vaadin.event.FieldEvents.TextChangeEvent;
import com.vaadin.event.FieldEvents.TextChangeListener;
import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.VaadinSessionScope;
import com.vaadin.ui.CustomComponent;
import com.vaadin.ui.FormLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.Window;
import com.vaadin.ui.themes.ValoTheme;
/**
* Add and Update Target.
*
*
*
*/
@SpringComponent
@VaadinSessionScope
public class TargetAddUpdateWindowLayout extends CustomComponent {
private static final long serialVersionUID = -6659290471705262389L;
@Autowired
private I18N i18n;
@@ -66,122 +60,61 @@ public class TargetAddUpdateWindowLayout extends CustomComponent {
@Autowired
private transient UINotification uINotification;
@Autowired
private transient EntityFactory entityFactory;
private TextField controllerIDTextField;
private TextField nameTextField;
private TextArea descTextArea;
private Label madatoryLabel;
private boolean editTarget = Boolean.FALSE;
private String controllerId;
private FormLayout formLayout;
private CommonDialogWindow window;
private String oldTargetName;
private String oldTargetDesc;
/**
* Initialize the Add Update Window Component for Target.
*/
public void init() {
/* create components */
createRequiredComponents();
/* display components in layout */
buildLayout();
/* register all listeners related to the Window */
addListeners();
setCompositionRoot(formLayout);
}
private void createRequiredComponents() {
/* Textfield for controller Id */
controllerIDTextField = SPUIComponentProvider.getTextField( i18n.get("prompt.target.id"), "", ValoTheme.TEXTFIELD_TINY, true, null,
i18n.get("prompt.target.id"), true, SPUILabelDefinitions.TEXT_FIELD_MAX_LENGTH);
controllerIDTextField = SPUIComponentProvider.getTextField(i18n.get("prompt.target.id"), "",
ValoTheme.TEXTFIELD_TINY, true, null, i18n.get("prompt.target.id"), true,
SPUILabelDefinitions.TEXT_FIELD_MAX_LENGTH);
controllerIDTextField.setId(SPUIComponentIdProvider.TARGET_ADD_CONTROLLER_ID);
/* Textfield for target name */
nameTextField = SPUIComponentProvider.getTextField( i18n.get("textfield.name"), "", ValoTheme.TEXTFIELD_TINY, false, null,
i18n.get("textfield.name"), true, SPUILabelDefinitions.TEXT_FIELD_MAX_LENGTH);
nameTextField = SPUIComponentProvider.getTextField(i18n.get("textfield.name"), "", ValoTheme.TEXTFIELD_TINY,
false, null, i18n.get("textfield.name"), true, SPUILabelDefinitions.TEXT_FIELD_MAX_LENGTH);
nameTextField.setId(SPUIComponentIdProvider.TARGET_ADD_NAME);
/* Textarea for target description */
descTextArea = SPUIComponentProvider.getTextArea( i18n.get("textfield.description"), "text-area-style", ValoTheme.TEXTFIELD_TINY, false, null,
i18n.get("textfield.description"), SPUILabelDefinitions.TEXT_AREA_MAX_LENGTH);
descTextArea = SPUIComponentProvider.getTextArea(i18n.get("textfield.description"), "text-area-style",
ValoTheme.TEXTFIELD_TINY, false, null, i18n.get("textfield.description"),
SPUILabelDefinitions.TEXT_AREA_MAX_LENGTH);
descTextArea.setId(SPUIComponentIdProvider.TARGET_ADD_DESC);
descTextArea.setNullRepresentation(HawkbitCommonUtil.SP_STRING_EMPTY);
/* Label for mandatory symbol */
madatoryLabel = new Label(i18n.get("label.mandatory.field"));
madatoryLabel.setStyleName(SPUIStyleDefinitions.SP_TEXTFIELD_ERROR + " " + ValoTheme.LABEL_SMALL);
}
private void buildLayout() {
/*
* The main layout of the window contains mandatory info, textboxes
* (controller Id, name & description) and action buttons layout
*/
setSizeUndefined();
formLayout = new FormLayout();
formLayout.addComponent(madatoryLabel);
formLayout.addComponent(controllerIDTextField);
formLayout.addComponent(nameTextField);
formLayout.addComponent(descTextArea);
if (Boolean.TRUE.equals(editTarget)) {
madatoryLabel.setVisible(Boolean.FALSE);
}
controllerIDTextField.focus();
}
private void addListeners() {
addTargetNameChangeListner();
addTargetDescChangeListner();
}
private void addTargetNameChangeListner() {
nameTextField.addTextChangeListener(new TextChangeListener() {
/**
*
*/
private static final long serialVersionUID = 1761855781481115921L;
@Override
public void textChange(final TextChangeEvent event) {
if (event.getText().equals(oldTargetName) && descTextArea.getValue().equals(oldTargetDesc)) {
window.setSaveButtonEnabled(false);
} else {
window.setSaveButtonEnabled(true);
}
}
});
}
private void addTargetDescChangeListner() {
descTextArea.addTextChangeListener(new TextChangeListener() {
/**
*
*/
private static final long serialVersionUID = 5770734934988115068L;
@Override
public void textChange(final TextChangeEvent event) {
if (event.getText().equals(oldTargetDesc) && nameTextField.getValue().equals(oldTargetName)) {
window.setSaveButtonEnabled(false);
} else {
window.setSaveButtonEnabled(true);
}
}
});
}
/**
* Update the Target if modified.
*/
@@ -199,10 +132,6 @@ public class TargetAddUpdateWindowLayout extends CustomComponent {
uINotification.displaySuccess(i18n.get("message.update.success", new Object[] { latestTarget.getName() }));
// publishing through event bus
eventBus.publish(this, new TargetTableEvent(BaseEntityEventType.UPDATED_ENTITY, latestTarget));
/* close the window */
closeThisWindow();
/* update details in table */
}
private void saveTargetListner() {
@@ -213,13 +142,9 @@ public class TargetAddUpdateWindowLayout extends CustomComponent {
}
}
private void discardTargetListner() {
closeThisWindow();
}
private void addNewTarget() {
final String newControlllerId = HawkbitCommonUtil.trimAndNullIfEmpty(controllerIDTextField.getValue());
if (mandatoryCheck(newControlllerId) && duplicateCheck(newControlllerId)) {
if (duplicateCheck(newControlllerId)) {
final String newName = HawkbitCommonUtil.trimAndNullIfEmpty(nameTextField.getValue());
final String newDesc = HawkbitCommonUtil.trimAndNullIfEmpty(descTextArea.getValue());
@@ -236,15 +161,20 @@ public class TargetAddUpdateWindowLayout extends CustomComponent {
/* display success msg */
uINotification.displaySuccess(i18n.get("message.save.success", new Object[] { newTarget.getName() }));
/* close the window */
closeThisWindow();
}
}
public Window getWindow() {
eventBus.publish(this, DragEvent.HIDE_DROP_HINT);
window = SPUIComponentProvider.getWindow(i18n.get("caption.add.new.target"), null,
SPUIDefinitions.CREATE_UPDATE_WINDOW, this, event -> saveTargetListner(), event -> discardTargetListner(), null);
window = SPUIWindowDecorator.getWindow(i18n.get("caption.add.new.target"), null,
SPUIDefinitions.CREATE_UPDATE_WINDOW, this, event -> saveTargetListner(), null, null, formLayout, i18n);
return window;
}
public Window getWindow(final String entityId) {
populateValuesOfTarget(entityId);
getWindow();
window.addStyleName("target-update-window");
return window;
}
@@ -254,39 +184,23 @@ public class TargetAddUpdateWindowLayout extends CustomComponent {
public void resetComponents() {
nameTextField.clear();
nameTextField.removeStyleName(SPUIStyleDefinitions.SP_TEXTFIELD_ERROR);
controllerIDTextField.setEnabled(true);
controllerIDTextField.setReadOnly(false);
controllerIDTextField.setEnabled(Boolean.TRUE);
controllerIDTextField.removeStyleName(SPUIStyleDefinitions.SP_TEXTFIELD_ERROR);
controllerIDTextField.clear();
descTextArea.clear();
editTarget = Boolean.FALSE;
}
private void closeThisWindow() {
editTarget = Boolean.FALSE;
window.close();
UI.getCurrent().removeWindow(window);
}
private void setTargetValues(final Target target, final String name, final String description) {
target.setName(name == null ? target.getControllerId() : name);
target.setDescription(description);
}
private boolean mandatoryCheck(final String newControlllerId) {
if (newControlllerId == null) {
controllerIDTextField.addStyleName(SPUIStyleDefinitions.SP_TEXTFIELD_ERROR);
uINotification.displayValidationError("Mandatory details are missing");
return false;
} else {
return true;
}
}
private boolean duplicateCheck(final String newControlllerId) {
final Target existingTarget = targetManagement.findTargetByControllerID(newControlllerId.trim());
if (existingTarget != null) {
uINotification.displayValidationError(i18n.get("message.target.duplicate.check"));
uINotification.displayValidationError(
i18n.get("message.target.duplicate.check", new Object[] { newControlllerId }));
return false;
} else {
return true;
@@ -296,22 +210,21 @@ public class TargetAddUpdateWindowLayout extends CustomComponent {
/**
* @param controllerId
*/
public void populateValuesOfTarget(final String controllerId) {
private void populateValuesOfTarget(final String controllerId) {
resetComponents();
this.controllerId = controllerId;
editTarget = Boolean.TRUE;
final Target target = targetManagement.findTargetByControllerID(controllerId);
controllerIDTextField.setValue(target.getControllerId());
controllerIDTextField.setReadOnly(Boolean.TRUE);
controllerIDTextField.setEnabled(Boolean.FALSE);
nameTextField.setValue(target.getName());
if (target.getDescription() != null) {
descTextArea.setValue(target.getDescription());
}
window.setSaveButtonEnabled(Boolean.FALSE);
}
oldTargetDesc = descTextArea.getValue();
oldTargetName = nameTextField.getValue();
window.addStyleName("target-update-window");
public FormLayout getFormLayout() {
return formLayout;
}
}

View File

@@ -44,7 +44,7 @@ import com.google.common.base.Strings;
public class TargetBeanQuery extends AbstractBeanQuery<ProxyTarget> {
private static final long serialVersionUID = -5645680058303167558L;
private Sort sort = new Sort(SPUIDefinitions.TARGET_TABLE_CREATE_AT_SORT_ORDER, "createdAt");
private Collection<TargetUpdateStatus> status = null;
private transient Collection<TargetUpdateStatus> status = null;
private String[] targetTags = null;
private Long distributionId = null;
private String searchText = null;

View File

@@ -106,8 +106,13 @@ public class TargetDetails extends AbstractTableDetailsLayout<Target> {
if (getSelectedBaseEntity() == null) {
return;
}
targetAddUpdateWindowLayout.getWindow(getSelectedBaseEntity().getControllerId());
// targetAddUpdateWindowLayout.populateValuesOfTarget(getSelectedBaseEntity().getControllerId());
openWindow();
}
private void openWindow() {
final Window newDistWindow = targetAddUpdateWindowLayout.getWindow();
targetAddUpdateWindowLayout.populateValuesOfTarget(getSelectedBaseEntity().getControllerId());
newDistWindow.setCaption(getI18n().get("caption.update.dist"));
UI.getCurrent().addWindow(newDistWindow);
newDistWindow.setVisible(Boolean.TRUE);

View File

@@ -102,15 +102,13 @@ public class CreateUpdateTargetTagLayoutWindow extends AbstractCreateUpdateTagLa
@Override
public void save(final ClickEvent event) {
if (mandatoryValuesPresent()) {
final TargetTag existingTag = tagManagement.findTargetTag(tagName.getValue());
if (optiongroup.getValue().equals(createTagStr)) {
if (!checkIsDuplicate(existingTag)) {
createNewTag();
}
} else {
updateExistingTag(existingTag);
final TargetTag existingTag = tagManagement.findTargetTag(tagName.getValue());
if (optiongroup.getValue().equals(createTagStr)) {
if (!checkIsDuplicate(existingTag)) {
createNewTag();
}
} else {
updateExistingTag(existingTag);
}
}
@@ -131,7 +129,6 @@ public class CreateUpdateTargetTagLayoutWindow extends AbstractCreateUpdateTagLa
}
newTargetTag = tagManagement.createTargetTag(newTargetTag);
displaySuccess(newTargetTag.getName());
closeWindow();
} else {
displayValidationError(i18n.get(MESSAGE_ERROR_MISSING_TAGNAME));
}
@@ -150,4 +147,9 @@ public class CreateUpdateTargetTagLayoutWindow extends AbstractCreateUpdateTagLa
setOptionGroupDefaultValue(permChecker.hasCreateTargetPermission(), permChecker.hasUpdateTargetPermission());
}
@Override
protected String getWindowCaption() {
return i18n.get("caption.add.tag");
}
}

View File

@@ -31,6 +31,7 @@ import org.eclipse.hawkbit.ui.UiProperties;
import org.eclipse.hawkbit.ui.common.CommonDialogWindow;
import org.eclipse.hawkbit.ui.common.DistributionSetIdName;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.decorators.SPUIWindowDecorator;
import org.eclipse.hawkbit.ui.filtermanagement.TargetFilterBeanQuery;
import org.eclipse.hawkbit.ui.management.footer.ActionTypeOptionGroupLayout;
import org.eclipse.hawkbit.ui.management.footer.ActionTypeOptionGroupLayout.ActionTypeOption;
@@ -56,8 +57,10 @@ import com.vaadin.data.Container;
import com.vaadin.data.Item;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Validator;
import com.vaadin.data.util.converter.StringToIntegerConverter;
import com.vaadin.data.validator.IntegerRangeValidator;
import com.vaadin.data.validator.RegexpValidator;
import com.vaadin.data.validator.NullValidator;
import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.ViewScope;
import com.vaadin.ui.ComboBox;
@@ -66,13 +69,10 @@ import com.vaadin.ui.Label;
import com.vaadin.ui.OptionGroup;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.themes.ValoTheme;
/**
*
* Rollout add or update popup layout.
*
*/
@SpringComponent
@ViewScope
@@ -86,8 +86,6 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
private static final String MESSAGE_ENTER_NUMBER = "message.enter.number";
private static final String NUMBER_REGEXP = "[-]?[0-9]*\\.?,?[0-9]+";
@Autowired
private ActionTypeOptionGroupLayout actionTypeOptionGroupLayout;
@@ -115,8 +113,6 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
@Autowired
private transient EventBus.SessionEventBus eventBus;
private Label mandatoryLabel;
private TextField rolloutName;
private ComboBox distributionSet;
@@ -135,7 +131,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
private OptionGroup errorThresholdOptionGroup;
private CommonDialogWindow addUpdateRolloutWindow;
private CommonDialogWindow window;
private Boolean editRolloutEnabled;
@@ -147,22 +143,28 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
private TextArea targetFilterQuery;
private final NullValidator nullValidator = new NullValidator(null, false);
/**
* Create components and layout.
*/
public void init() {
setSizeUndefined();
createRequiredComponents();
buildLayout();
}
public CommonDialogWindow getWindow() {
public CommonDialogWindow getWindow(final Long rolloutId) {
window = getWindow();
populateData(rolloutId);
return window;
}
addUpdateRolloutWindow = SPUIComponentProvider.getWindow(i18n.get("caption.configure.rollout"), null,
SPUIDefinitions.CREATE_UPDATE_WINDOW, this, event -> onRolloutSave(), event -> onDiscard(),
uiProperties.getLinks().getDocumentation().getRolloutView());
return addUpdateRolloutWindow;
public CommonDialogWindow getWindow() {
resetComponents();
return SPUIWindowDecorator.getWindow(i18n.get("caption.configure.rollout"), null,
SPUIDefinitions.CREATE_UPDATE_WINDOW, this, event -> onRolloutSave(), null,
uiProperties.getLinks().getDocumentation().getRolloutView(), this, i18n);
}
/**
@@ -179,10 +181,8 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
setDefaultSaveStartGroupOption();
totalTargetsLabel.setVisible(false);
groupSizeLabel.setVisible(false);
removeComponent(targetFilterQuery);
if (getComponent(1, 3) == null) {
addComponent(targetFilterQueryCombo, 1, 3);
}
removeComponent(1, 2);
addComponent(targetFilterQueryCombo, 1, 2);
actionTypeOptionGroupLayout.selectDefaultOption();
totalTargetsCount = 0L;
rolloutForEdit = null;
@@ -206,37 +206,60 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
setSizeUndefined();
setRows(9);
setColumns(3);
setStyleName("marginTop");
addComponent(mandatoryLabel, 1, 0, 2, 0);
addComponent(getLabel("textfield.name"), 0, 1);
addComponent(rolloutName, 1, 1);
addComponent(getLabel("prompt.distribution.set"), 0, 2);
addComponent(distributionSet, 1, 2);
addComponent(getLabel("prompt.target.filter"), 0, 3);
addComponent(targetFilterQueryCombo, 1, 3);
addComponent(totalTargetsLabel, 2, 3);
addComponent(getLabel("prompt.number.of.groups"), 0, 4);
addComponent(noOfGroups, 1, 4);
addComponent(groupSizeLabel, 2, 4);
addComponent(getLabel("prompt.tigger.threshold"), 0, 5);
addComponent(triggerThreshold, 1, 5);
addComponent(getPercentHintLabel(), 2, 5);
addComponent(getLabel("prompt.error.threshold"), 0, 6);
addComponent(errorThreshold, 1, 6);
addComponent(errorThresholdOptionGroup, 2, 6);
addComponent(getLabel("textfield.description"), 0, 7);
addComponent(description, 1, 7, 2, 7);
addComponent(actionTypeOptionGroupLayout, 0, 8, 2, 8);
addComponent(getMandatoryLabel("textfield.name"), 0, 0);
addComponent(rolloutName, 1, 0);
rolloutName.addValidator(nullValidator);
addComponent(getMandatoryLabel("prompt.distribution.set"), 0, 1);
addComponent(distributionSet, 1, 1);
distributionSet.addValidator(nullValidator);
addComponent(getMandatoryLabel("prompt.target.filter"), 0, 2);
addComponent(targetFilterQueryCombo, 1, 2);
targetFilterQueryCombo.addValidator(nullValidator);
targetFilterQuery.removeValidator(nullValidator);
addComponent(totalTargetsLabel, 2, 2);
addComponent(getMandatoryLabel("prompt.number.of.groups"), 0, 3);
addComponent(noOfGroups, 1, 3);
noOfGroups.addValidator(nullValidator);
addComponent(groupSizeLabel, 2, 3);
addComponent(getMandatoryLabel("prompt.tigger.threshold"), 0, 4);
addComponent(triggerThreshold, 1, 4);
triggerThreshold.addValidator(nullValidator);
addComponent(getPercentHintLabel(), 2, 4);
addComponent(getMandatoryLabel("prompt.error.threshold"), 0, 5);
addComponent(errorThreshold, 1, 5);
errorThreshold.addValidator(nullValidator);
addComponent(errorThresholdOptionGroup, 2, 5);
addComponent(getLabel("textfield.description"), 0, 6);
addComponent(description, 1, 6, 2, 6);
addComponent(actionTypeOptionGroupLayout, 0, 7, 2, 7);
rolloutName.focus();
}
private Label getMandatoryLabel(final String key) {
final Label mandatoryLabel = getLabel(i18n.get(key));
mandatoryLabel.setContentMode(ContentMode.HTML);
mandatoryLabel.setValue(mandatoryLabel.getValue().concat(" <span style='color:#ed473b'>*</span>"));
return mandatoryLabel;
}
private Label getLabel(final String key) {
return SPUIComponentProvider.getLabel(i18n.get(key), SPUILabelDefinitions.SP_LABEL_SIMPLE);
}
private TextField getTextfield(final String key) {
return SPUIComponentProvider.getTextField(null, "", ValoTheme.TEXTFIELD_TINY, true, null, i18n.get(key), true,
return SPUIComponentProvider.getTextField(null, "", ValoTheme.TEXTFIELD_TINY, false, null, i18n.get(key), true,
SPUILabelDefinitions.TEXT_FIELD_MAX_LENGTH);
}
@@ -248,7 +271,6 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
}
private void createRequiredComponents() {
mandatoryLabel = createMandatoryLabel();
rolloutName = createRolloutNameField();
distributionSet = createDistributionSetCombo();
populateDistributionSet();
@@ -258,7 +280,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
noOfGroups = createNoOfGroupsField();
groupSizeLabel = createGroupSizeLabel();
triggerThreshold = createTriggerThresold();
triggerThreshold = createTriggerThreshold();
errorThreshold = createErrorThreshold();
description = createDescription();
errorThresholdOptionGroup = createErrorThresholdOptionGroup();
@@ -307,11 +329,11 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
errorThresoldOptions.addStyleName(ValoTheme.OPTIONGROUP_HORIZONTAL);
errorThresoldOptions.addStyleName(SPUIStyleDefinitions.ROLLOUT_OPTION_GROUP);
errorThresoldOptions.setSizeUndefined();
errorThresoldOptions.addValueChangeListener(this::onErrorThresoldOptionChange);
errorThresoldOptions.addValueChangeListener(this::listenerOnErrorThresoldOptionChange);
return errorThresoldOptions;
}
private void onErrorThresoldOptionChange(final ValueChangeEvent event) {
private void listenerOnErrorThresoldOptionChange(final ValueChangeEvent event) {
errorThreshold.clear();
errorThreshold.removeAllValidators();
if (event.getProperty().getValue().equals(ERRORTHRESOLDOPTIONS.COUNT.getValue())) {
@@ -324,17 +346,17 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
private ComboBox createTargetFilterQueryCombo() {
final ComboBox targetFilter = SPUIComponentProvider.getComboBox(null, "", "", null, ValoTheme.COMBOBOX_SMALL,
true, "", i18n.get("prompt.target.filter"));
false, "", i18n.get("prompt.target.filter"));
targetFilter.setImmediate(true);
targetFilter.setPageLength(7);
targetFilter.setItemCaptionPropertyId(SPUILabelDefinitions.VAR_NAME);
targetFilter.setId(SPUIComponentIdProvider.ROLLOUT_TARGET_FILTER_COMBO_ID);
targetFilter.setSizeUndefined();
targetFilter.addValueChangeListener(event -> onTargetFilterChange());
targetFilter.addValueChangeListener(this::onTargetFilterChange);
return targetFilter;
}
private void onTargetFilterChange() {
private void onTargetFilterChange(final ValueChangeEvent event) {
final String filterQueryString = getTargetFilterQuery();
if (!Strings.isNullOrEmpty(filterQueryString)) {
totalTargetsCount = targetManagement.countTargetByTargetFilterQuery(filterQueryString);
@@ -344,7 +366,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
totalTargetsCount = 0L;
totalTargetsLabel.setVisible(false);
}
onGroupNumberChange();
onGroupNumberChange(event);
}
private String getTotalTargetMessage() {
@@ -368,10 +390,6 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
targetFilterQF);
}
private void onDiscard() {
closeThisWindow();
}
private void onRolloutSave() {
if (editRolloutEnabled) {
editRollout();
@@ -381,7 +399,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
}
private void editRollout() {
if (mandatoryCheckForEdit() && validateFields() && duplicateCheckForEdit() && null != rolloutForEdit) {
if (duplicateCheckForEdit() && rolloutForEdit != null) {
rolloutForEdit.setName(rolloutName.getValue());
rolloutForEdit.setDescription(description.getValue());
final DistributionSetIdName distributionSetIdName = (DistributionSetIdName) distributionSet.getValue();
@@ -399,7 +417,6 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
final Rollout updatedRollout = rolloutManagement.updateRollout(rolloutForEdit);
uiNotification
.displaySuccess(i18n.get("message.update.success", new Object[] { updatedRollout.getName() }));
closeThisWindow();
eventBus.publish(this, RolloutEvent.UPDATE_ROLLOUT);
}
}
@@ -427,11 +444,10 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
}
private void createRollout() {
if (mandatoryCheck() && validateFields() && duplicateCheck()) {
if (duplicateCheck()) {
final Rollout rolloutToCreate = saveRollout();
uiNotification.displaySuccess(i18n.get("message.save.success", new Object[] { rolloutToCreate.getName() }));
eventBus.publish(this, RolloutEvent.CREATE_ROLLOUT);
closeThisWindow();
}
}
@@ -488,48 +504,6 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
return true;
}
private void closeThisWindow() {
addUpdateRolloutWindow.close();
UI.getCurrent().removeWindow(addUpdateRolloutWindow);
}
private boolean mandatoryCheck() {
final DistributionSetIdName ds = getDistributionSetSelected();
final String targetFilter = (String) targetFilterQueryCombo.getValue();
final String triggerThresoldValue = triggerThreshold.getValue();
final String errorThresoldValue = errorThreshold.getValue();
if (hasNoNameOrTargetFilter(targetFilter) || ds == null
|| HawkbitCommonUtil.trimAndNullIfEmpty(noOfGroups.getValue()) == null
|| isThresholdValueMissing(triggerThresoldValue, errorThresoldValue)) {
uiNotification.displayValidationError(i18n.get("message.mandatory.check"));
return false;
}
return true;
}
private boolean mandatoryCheckForEdit() {
final DistributionSetIdName ds = getDistributionSetSelected();
final String targetFilter = targetFilterQuery.getValue();
final String triggerThresoldValue = triggerThreshold.getValue();
final String errorThresoldValue = errorThreshold.getValue();
if (hasNoNameOrTargetFilter(targetFilter) || ds == null
|| HawkbitCommonUtil.trimAndNullIfEmpty(noOfGroups.getValue()) == null
|| isThresholdValueMissing(triggerThresoldValue, errorThresoldValue)) {
uiNotification.displayValidationError(i18n.get("message.mandatory.check"));
return false;
}
return true;
}
private boolean hasNoNameOrTargetFilter(final String targetFilter) {
return getRolloutName() == null || targetFilter == null;
}
private boolean isThresholdValueMissing(final String triggerThresoldValue, final String errorThresoldValue) {
return HawkbitCommonUtil.trimAndNullIfEmpty(triggerThresoldValue) == null
|| HawkbitCommonUtil.trimAndNullIfEmpty(errorThresoldValue) == null;
}
private boolean duplicateCheck() {
if (rolloutManagement.findRolloutByName(getRolloutName()) != null) {
uiNotification.displayValidationError(
@@ -555,17 +529,23 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
private TextField createErrorThreshold() {
final TextField errorField = getTextfield("prompt.error.threshold");
errorField.setNullRepresentation("");
errorField.addValidator(new ThresholdFieldValidator());
errorField.setConverter(new StringToIntegerConverter());
errorField.setConversionError(i18n.get(MESSAGE_ENTER_NUMBER));
errorField.setId(SPUIComponentIdProvider.ROLLOUT_ERROR_THRESOLD_ID);
errorField.setMaxLength(7);
errorField.setSizeUndefined();
return errorField;
}
private TextField createTriggerThresold() {
private TextField createTriggerThreshold() {
final TextField thresholdField = getTextfield("prompt.tigger.threshold");
thresholdField.setId(SPUIComponentIdProvider.ROLLOUT_TRIGGER_THRESOLD_ID);
thresholdField.setNullRepresentation("");
thresholdField.addValidator(new ThresholdFieldValidator());
thresholdField.setConverter(new StringToIntegerConverter());
thresholdField.setConversionError(i18n.get(MESSAGE_ENTER_NUMBER));
thresholdField.setSizeUndefined();
thresholdField.setMaxLength(3);
return thresholdField;
@@ -577,12 +557,15 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
noOfGroupsField.addValidator(new GroupNumberValidator());
noOfGroupsField.setSizeUndefined();
noOfGroupsField.setMaxLength(3);
noOfGroupsField.addValueChangeListener(evevt -> onGroupNumberChange());
noOfGroupsField.setConverter(new StringToIntegerConverter());
noOfGroupsField.setConversionError(i18n.get(MESSAGE_ENTER_NUMBER));
noOfGroupsField.addValueChangeListener(this::onGroupNumberChange);
noOfGroupsField.setNullRepresentation("");
return noOfGroupsField;
}
private void onGroupNumberChange() {
if (noOfGroups.isValid() && !Strings.isNullOrEmpty(noOfGroups.getValue())) {
private void onGroupNumberChange(final ValueChangeEvent event) {
if (event.getProperty().getValue() != null && noOfGroups.isValid()) {
groupSizeLabel.setValue(getTargetPerGroupMessage(String.valueOf(getGroupSize())));
groupSizeLabel.setVisible(true);
} else {
@@ -591,8 +574,8 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
}
private ComboBox createDistributionSetCombo() {
final ComboBox dsSet = SPUIComponentProvider.getComboBox(null, "", "", null, ValoTheme.COMBOBOX_SMALL, true, "",
i18n.get("prompt.distribution.set"));
final ComboBox dsSet = SPUIComponentProvider.getComboBox(null, "", "", null, ValoTheme.COMBOBOX_SMALL, false,
"", i18n.get("prompt.distribution.set"));
dsSet.setImmediate(true);
dsSet.setPageLength(7);
dsSet.setItemCaptionPropertyId(SPUILabelDefinitions.VAR_NAME);
@@ -612,7 +595,6 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
return new LazyQueryContainer(
new LazyQueryDefinition(true, SPUIDefinitions.PAGE_SIZE, SPUILabelDefinitions.VAR_DIST_ID_NAME),
distributionQF);
}
private TextField createRolloutNameField() {
@@ -622,44 +604,42 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
return rolloutNameField;
}
private Label createMandatoryLabel() {
final Label madatoryLbl = new Label(i18n.get("label.mandatory.field"));
madatoryLbl.addStyleName(SPUIStyleDefinitions.SP_TEXTFIELD_ERROR + " " + ValoTheme.LABEL_SMALL);
return madatoryLbl;
}
private String getRolloutName() {
return HawkbitCommonUtil.trimAndNullIfEmpty(rolloutName.getValue());
}
private DistributionSetIdName getDistributionSetSelected() {
return (DistributionSetIdName) distributionSet.getValue();
}
class ErrorThresoldOptionValidator implements Validator {
private static final long serialVersionUID = 9049939751976326550L;
@Override
public void validate(final Object value) {
try {
if (HawkbitCommonUtil.trimAndNullIfEmpty(noOfGroups.getValue()) == null
|| HawkbitCommonUtil.trimAndNullIfEmpty((String) targetFilterQueryCombo.getValue()) == null) {
if (isNoOfGroupsOrTargetFilterEmpty()) {
uiNotification
.displayValidationError(i18n.get("message.rollout.noofgroups.or.targetfilter.missing"));
} else {
new RegexpValidator(NUMBER_REGEXP, i18n.get(MESSAGE_ENTER_NUMBER)).validate(value);
final int groupSize = getGroupSize();
new IntegerRangeValidator(i18n.get(MESSAGE_ROLLOUT_FIELD_VALUE_RANGE, 0, groupSize), 0, groupSize)
.validate(Integer.valueOf(value.toString()));
if (value != null) {
final int groupSize = getGroupSize();
new IntegerRangeValidator(i18n.get(MESSAGE_ROLLOUT_FIELD_VALUE_RANGE, 0, groupSize), 0,
groupSize).validate(Integer.valueOf(value.toString()));
}
}
}
// suppress the need of preserve original exception, will blow
// up the
// log and not necessary here
catch (@SuppressWarnings("squid:S1166") final InvalidValueException ex) {
LOG.error(ex.getMessage());
catch (final InvalidValueException ex) {
// we have to throw the exception here, otherwise the UI won't
// show the vaadin validation error!
throw ex;
}
}
private boolean isNoOfGroupsOrTargetFilterEmpty() {
return HawkbitCommonUtil.trimAndNullIfEmpty(noOfGroups.getValue()) == null
|| (HawkbitCommonUtil.trimAndNullIfEmpty((String) targetFilterQueryCombo.getValue()) == null
&& targetFilterQuery.getValue() == null);
}
}
private int getGroupSize() {
@@ -672,15 +652,18 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
@Override
public void validate(final Object value) {
try {
new RegexpValidator(NUMBER_REGEXP, i18n.get(MESSAGE_ENTER_NUMBER)).validate(value);
new IntegerRangeValidator(i18n.get(MESSAGE_ROLLOUT_FIELD_VALUE_RANGE, 0, 100), 0, 100)
.validate(Integer.valueOf(value.toString()));
if (value != null) {
new IntegerRangeValidator(i18n.get(MESSAGE_ROLLOUT_FIELD_VALUE_RANGE, 0, 100), 0, 100)
.validate(Integer.valueOf(value.toString()));
}
}
// suppress the need of preserve original exception, will blow
// up the
// log and not necessary here
catch (@SuppressWarnings("squid:S1166") final InvalidValueException ex) {
LOG.error(ex.getMessage());
catch (final InvalidValueException ex) {
// we have to throw the exception here, otherwise the UI won't
// show the vaadin validation error!
throw ex;
}
}
}
@@ -691,15 +674,18 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
@Override
public void validate(final Object value) {
try {
new RegexpValidator(NUMBER_REGEXP, i18n.get(MESSAGE_ENTER_NUMBER)).validate(value);
new IntegerRangeValidator(i18n.get(MESSAGE_ROLLOUT_FIELD_VALUE_RANGE, 0, 500), 0, 500)
.validate(Integer.valueOf(value.toString()));
if (value != null) {
new IntegerRangeValidator(i18n.get(MESSAGE_ROLLOUT_FIELD_VALUE_RANGE, 0, 500), 0, 500)
.validate(Integer.valueOf(value.toString()));
}
}
// suppress the need of preserve original exception, will blow
// up the
// log and not necessary here
catch (@SuppressWarnings("squid:S1166") final InvalidValueException ex) {
LOG.error(ex.getMessage());
catch (final InvalidValueException ex) {
// we have to throw the exception here, otherwise the UI won't
// show the vaadin validation error!
throw ex;
}
}
}
@@ -711,15 +697,18 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
* @param rolloutId
* rollout id
*/
public void populateData(final Long rolloutId) {
resetComponents();
private void populateData(final Long rolloutId) {
if (rolloutId == null) {
return;
}
editRolloutEnabled = Boolean.TRUE;
rolloutForEdit = rolloutManagement.findRolloutById(rolloutId);
rolloutName.setValue(rolloutForEdit.getName());
description.setValue(rolloutForEdit.getDescription());
distributionSet.setValue(DistributionSetIdName.generate(rolloutForEdit.getDistributionSet()));
final List<RolloutGroup> rolloutGroups = rolloutForEdit.getRolloutGroups();
setThresoldValues(rolloutGroups);
setThresholdValues(rolloutGroups);
setActionType(rolloutForEdit);
if (rolloutForEdit.getStatus() != RolloutStatus.READY) {
disableRequiredFieldsOnEdit();
@@ -727,12 +716,16 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
noOfGroups.setEnabled(false);
targetFilterQuery.setValue(rolloutForEdit.getTargetFilterQuery());
removeComponent(targetFilterQueryCombo);
addComponent(targetFilterQuery, 1, 3);
removeComponent(1, 2);
targetFilterQueryCombo.removeValidator(nullValidator);
addComponent(targetFilterQuery, 1, 2);
targetFilterQuery.addValidator(nullValidator);
totalTargetsCount = targetManagement.countTargetByTargetFilterQuery(rolloutForEdit.getTargetFilterQuery());
totalTargetsLabel.setValue(getTotalTargetMessage());
totalTargetsLabel.setVisible(true);
window.setOrginaleValues();
}
private void disableRequiredFieldsOnEdit() {
@@ -771,8 +764,8 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
/**
* @param rolloutGroups
*/
private void setThresoldValues(final List<RolloutGroup> rolloutGroups) {
if (null != rolloutGroups && !rolloutGroups.isEmpty()) {
private void setThresholdValues(final List<RolloutGroup> rolloutGroups) {
if (rolloutGroups != null && !rolloutGroups.isEmpty()) {
errorThreshold.setValue(rolloutGroups.get(0).getErrorConditionExp());
triggerThreshold.setValue(rolloutGroups.get(0).getSuccessConditionExp());
noOfGroups.setValue(String.valueOf(rolloutGroups.size()));
@@ -798,7 +791,6 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
public String getValue() {
return value;
}
}
enum ERRORTHRESOLDOPTIONS {
@@ -816,6 +808,6 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
public String getValue() {
return value;
}
}
}

View File

@@ -28,6 +28,7 @@ import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
import org.eclipse.hawkbit.ui.common.CommonDialogWindow;
import org.eclipse.hawkbit.ui.common.grid.AbstractGrid;
import org.eclipse.hawkbit.ui.customrenderers.client.renderers.RolloutRendererData;
import org.eclipse.hawkbit.ui.customrenderers.renderers.HtmlButtonRenderer;
@@ -62,14 +63,11 @@ import com.vaadin.server.FontAwesome;
import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.ViewScope;
import com.vaadin.ui.UI;
import com.vaadin.ui.Window;
import com.vaadin.ui.renderers.ClickableRenderer.RendererClickEvent;
import com.vaadin.ui.renderers.HtmlRenderer;
/**
*
* Rollout list grid component.
*
*/
@SpringComponent
@ViewScope
@@ -145,6 +143,7 @@ public class RolloutListGrid extends AbstractGrid {
final LazyQueryContainer rolloutContainer = (LazyQueryContainer) getContainerDataSource();
final Item item = rolloutContainer.getItem(rolloutChangeEvent.getRolloutId());
if (item == null) {
refreshGrid();
return;
}
item.getItemProperty(SPUILabelDefinitions.VAR_STATUS).setValue(rollout.getStatus());
@@ -159,7 +158,6 @@ public class RolloutListGrid extends AbstractGrid {
}
item.getItemProperty(ROLLOUT_RENDERER_DATA)
.setValue(new RolloutRendererData(rollout.getName(), rollout.getStatus().toString()));
}
@Override
@@ -200,7 +198,6 @@ public class RolloutListGrid extends AbstractGrid {
rolloutGridContainer.addContainerProperty(SPUILabelDefinitions.ACTION, String.class,
FontAwesome.CIRCLE_O.getHtml(), false, false);
}
@Override
@@ -292,7 +289,6 @@ public class RolloutListGrid extends AbstractGrid {
for (final Object propertyId : columnsToBeHidden) {
getColumn(propertyId).setHidden(true);
}
}
@Override
@@ -315,7 +311,6 @@ public class RolloutListGrid extends AbstractGrid {
final RolloutRenderer customObjectRenderer = new RolloutRenderer(RolloutRendererData.class);
customObjectRenderer.addClickListener(this::onClickOfRolloutName);
getColumn(ROLLOUT_RENDERER_DATA).setRenderer(customObjectRenderer);
}
private void createRolloutStatusToFontMap() {
@@ -437,8 +432,7 @@ public class RolloutListGrid extends AbstractGrid {
}
private void onUpdate(final ContextMenuData contextMenuData) {
addUpdateRolloutWindow.populateData(contextMenuData.getRolloutId());
final Window addTargetWindow = addUpdateRolloutWindow.getWindow();
final CommonDialogWindow addTargetWindow = addUpdateRolloutWindow.getWindow(contextMenuData.getRolloutId());
addTargetWindow.setCaption(i18n.get("caption.update.rollout"));
UI.getCurrent().addWindow(addTargetWindow);
addTargetWindow.setVisible(Boolean.TRUE);
@@ -684,7 +678,6 @@ public class RolloutListGrid extends AbstractGrid {
public Class<String> getPresentationType() {
return String.class;
}
}
}

View File

@@ -91,7 +91,6 @@ public class RolloutListHeader extends AbstractGridHeader {
@Override
protected void addNewItem(final ClickEvent event) {
addUpdateRolloutWindow.resetComponents();
final Window addTargetWindow = addUpdateRolloutWindow.getWindow();
UI.getCurrent().addWindow(addTargetWindow);
addTargetWindow.setVisible(Boolean.TRUE);

View File

@@ -176,6 +176,10 @@ public final class SPUIComponentIdProvider {
* ID - Dist jvm combo.
*/
public static final String DIST_MODULE_COMBO = "dist.module.combo.";
/**
* ID for Distribution Tag ComboBox
*/
public static final String DIST_TAG_COMBO = "dist.tag.combo";
/**
* ID-Dist.PIN.
*/
@@ -302,6 +306,22 @@ public final class SPUIComponentIdProvider {
* tag color preview button id.
*/
public static final String TAG_COLOR_PREVIEW_ID = "tag.color.preview";
/**
* Id for ColorPickerLayout
*/
public static final String COLOR_PICKER_LAYOUT = "color.picker.layout";
/**
* Id for ColorPickerLayout's red slider
*/
public static final String COLOR_PICKER_RED_SLIDER = "color.picker.red.slider";
/**
* Id for Color preview field with the color code
*/
public static final String COLOR_PREVIEW_FIELD = "color-preview-field";
/**
* Id for OptionGroup Create/Update tag
*/
public static final String OPTION_GROUP = "create.update.tag";
/**
* Confirmation dialogue OK button id.
*/
@@ -915,6 +935,11 @@ public final class SPUIComponentIdProvider {
*/
public static final String UPLOAD_STATUS_POPUP_ID = "artifact.upload.status.popup.id";
/**
* Table multiselect for selecting DistType
*/
public static final String SELECT_DIST_TYPE = "select-dist-type";
/**
* /* Private Constructor.
*/

View File

@@ -293,8 +293,4 @@
padding-bottom: 12px !important;
}
.v-button-default-color {
color: #551f62;
}
}

View File

@@ -53,4 +53,8 @@
font-size: 16px;
}
.marginTop {
margin-top: 20px !important;
}
}

View File

@@ -164,5 +164,6 @@
.actionButtonsMargin {
margin-top: 30px;
margin-bottom: 10px;
}
}

View File

@@ -79,14 +79,14 @@ caption.delete.swmodule.accordion.tab = Delete SW Modules
caption.delete.dist.set.type.accordion.tab = Delete Distribution Set Type
caption.delete.sw.module.type.accordion.tab = Delete Software Module Type
caption.attributes = Attributes
caption.panel.dist.installed = Installed distribution set
caption.panel.dist.assigned = Assigned distribution set
caption.panel.dist.installed = Installed Distribution Set
caption.panel.dist.assigned = Assigned Distribution Set
caption.soft.delete.confirmbox = Confirm Software Module Delete Action
caption.cancel.action.confirmbox = Confirm action cancel
caption.cancel.action.confirmbox = Confirm Action Cancellation
caption.forcequit.action.confirmbox = Confirm force quit action
caption.forced.datefield = Force update at time
caption.force.action.confirmbox = Confirm Force Active Action
caption.confirm.abort.action = Confirm abort action
caption.confirm.abort.action = Confirm Abort Action
caption.filter.delete.confirmbox = Confirm Filter Delete Action
@@ -220,7 +220,7 @@ message.duplicate.softwaremodule = {0} : {1} already exists!
message.tag.delete = Please unclick the tag {0}, then try to delete
message.dist.type.check.delete = Please unclick the distribution type {0}, then try to delete
message.cannot.delete.default.dstype = Default distribution set type cannot be deleted
message.swmodule.type.check.delete = Please unclick the software module type {0}, then try to delete
message.swmodule.type.check.delete = Please unclick the Software Module type {0}, then try to delete
message.targets.already.deleted = Few Target(s) are already deleted.Pending for action
message.dists.already.deleted = Few distribution(s) are already deleted.Pending for action
message.target.deleted.pending = Target(s) already deleted.Pending for action
@@ -262,10 +262,10 @@ message.dists.assign.tag.alreadyassigned = Few of the DistributionSet's are alre
message.dists.tag.assigned = {0} DistributionSet's assigned to Tag {1}
message.dists.tag.unassigned = {0} DistributionSet's un-assigned from Tag {1}
message.dist.no.operation = {0} - already assigned/installed, No operation
message.sm.delete.confirm = Are you sure that you want to delete the selected {0} software module?
message.sm.delete.confirm = Are you sure that you want to delete the selected {0} Software Module?
message.error.os.softmodule = Please select the OS to delete
message.error.ah.softmodule = Please select the Application to delete
message.error.softmodule.deleted = The selected software module is already deleted
message.error.softmodule.deleted = The selected Software Module is already deleted
message.cancel.action = Cancel..
message.cancel.action.success = Action cancelled successfully !
message.cancel.action.failed = Unable to cancel the action !
@@ -284,7 +284,7 @@ message.action.not.allowed = Action not allowed
message.onlyone.distribution.assigned = Only one distribution set can be assigned
message.onlyone.distribution.dropallowed = Only one distribution set can be dropped
message.error.missing.typename = Missing Type Name
message.error.missing.typenameorkey = Missing Type Name or Key or software module type
message.error.missing.typenameorkey = Missing Type Name or Key or Software Module type
message.tag.cannot.be.assigned = Target/DS cannot be assigned to {0}
message.no.targets.assiged.fortag = No targets are assigned to tag {0}
message.error.missing.tagname = Please select tag name
@@ -314,12 +314,12 @@ soft.module.os =OS
#Artifact upload
message.error.noFileSelected = No file selected for upload
message.error.noProvidedName = Please provide custom file name
message.error.noSwModuleSelected = Please select a software module
message.error.noSwModuleSelected = Please select a Software Module
message.no.duplicateFiles = Duplicate files selected
message.no.duplicateFile = Duplicate file selected :
message.delete.artifact = Are you sure that you want to delete artifact {0} ?
message.duplicate.filename = Duplicate file name
message.swModule.deleted = {0} Software module(s) deleted
message.swModule.deleted = {0} Software Module(s) deleted
message.upload.failed = Streaming Failed
message.uploadedfile.size.exceeded = File size exceeded .Allowed size {0} bytes
message.uploadedfile.aborted = File upload aborted
@@ -329,7 +329,7 @@ message.abort.upload = Are you sure that you want to abort the upload?
upload.swModuleTable.header = Software module
upload.swModuleTable.header = Software Module
upload.selectedfile.name = file selected for upload
upload.file.name = File name
upload.sha1 = SHA1 checksum
@@ -347,7 +347,7 @@ upload.reason = Reason
upload.action = Action
upload.result.status = Upload status
upload.file = Upload File
upload.caption.update.swmodule = Update software module
upload.caption.update.swmodule = Update Software Module
caption.tab.details = Details
caption.tab.description = Description
@@ -363,8 +363,8 @@ label.no.tag.assigned = NO TAG
caption.assign.software.dist.accordion.tab = Assign Software Modules
message.software.assignment = {0} Software Module(s) Assignment(s) done
message.dist.inuse = {0} Distribution is already assigned to target
message.software.dist.already.assigned = {0} Distribution already has software module {1}
message.software.dist.type.notallowed = {0} Software module type can not assign to Distribution {1}
message.software.dist.already.assigned = {0} Distribution already has Software Module {1}
message.software.dist.type.notallowed = {0} Software Module type can not assign to Distribution {1}
message.target.assigned = {0} is assigned to {1}
message.dist.type.delete = {0} DistributionType(s) Deleted successfully.
message.sw.module.type.delete = {0} Software Module Type(s) deleted successfully.
@@ -445,7 +445,7 @@ header.caption.mandatory = Mandatory
header.caption.typename = SoftwareModuleType
header.caption.softwaremodule = SoftwareModule
header.caption.unassign = Unassign
message.sw.unassigned = Software module {0} successfully unassigned
message.sw.unassigned = Software Module {0} successfully unassigned
header.caption.upload.details = Upload details
combo.type.tag.name = Type tag name
@@ -468,10 +468,10 @@ rollout.group.label.target.truncated = {0} targets has been truncated in the lis
prompt.number.of.groups = Number of groups
prompt.tigger.threshold = Trigger threshold
prompt.error.threshold = Error threshold
prompt.distribution.set = Distribution set
caption.configure.rollout = Configure rollout
caption.update.rollout = Update rollout
prompt.target.filter = Custom target filter
prompt.distribution.set = Distribution Set
caption.configure.rollout = Configure Rollout
caption.update.rollout = Update Rollout
prompt.target.filter = Custom Target Filter
message.rollout.nonzero.group.number = Number of groups must be greater than zero
message.rollout.max.group.number = Number of groups must not be greater than 500
message.rollout.duplicate.check = Rollout [ {0} ] must be unique, entered value already exists.
@@ -485,8 +485,8 @@ message.rollout.noofgroups.or.targetfilter.missing = Please enter number of grou
message.rollouts = Rollouts
label.target.per.group = Targets per group :
message.dist.already.assigned = Distribution {0} is already assigned to target
message.error.creating.rollout = Server error. Error creating rollout. Please contact the administrator
message.error.starting.rollout = Server error. Error starting rollout. Please contact the administrator
message.error.creating.rollout = Server error. Error creating Rollout. Please contact the administrator
message.error.starting.rollout = Server error. Error starting Rollout. Please contact the administrator
#rollout - end
#Menu

View File

@@ -77,16 +77,16 @@ caption.delete.swmodule.accordion.tab = Delete SW Modules
caption.delete.dist.set.type.accordion.tab = Delete Distribution Set Type
caption.delete.sw.module.type.accordion.tab = Delete Software Module Type
caption.attributes = Attributes
caption.panel.dist.installed = Installed distribution set
caption.panel.dist.assigned = Assigned distribution set
caption.panel.dist.installed = Installed Distribution Set
caption.panel.dist.assigned = Assigned Distribution Set
caption.soft.delete.confirmbox = Confirm Software Module Delete Action
caption.cancel.action.confirmbox = Confirm action cancellation
caption.cancel.action.confirmbox = Confirm Action Cancellation
caption.forced.datefield = Force update at time
caption.force.action.confirmbox = Confirm Force Active Action
caption.filter.simple = Simple Filter
caption.filter.custom = Custom Filter
caption.filter.delete.confirmbox = Confirm Filter Delete Action
caption.confirm.abort.action = Confirm abort action
caption.confirm.abort.action = Confirm Abort Action
# Labels prefix with - label
@@ -214,13 +214,13 @@ message.target.unassigned.one = {0} is unassigned from {1}
message.target.unassigned.many = {0} Targets are unassigned from {1}
message.target.assigned.pending = Some target(s) are already assigned.Pending for action
message.cannot.delete = Cannot be deleted
message.check.softwaremodule = Please provide both name and verion!
message.check.softwaremodule = Please provide both name and version!
message.cannot.delete.default.dstype = Default distribution set type cannot be deleted
message.duplicate.softwaremodule = {0} : {1} already exists!
message.cannot.delete.default.dstype = Default distribution set type cannot be deleted
message.tag.delete = Please unclick the tag {0}, then try to delete
message.dist.type.check.delete = Please unclick the distribution type {0}, then try to delete
message.swmodule.type.check.delete = Please unclick the software module type {0}, then try to delete
message.swmodule.type.check.delete = Please unclick the Software Module type {0}, then try to delete
message.targets.already.deleted = Few Target(s) are already deleted.Pending for action
message.dists.already.deleted = Few distribution(s) are already deleted.Pending for action
message.target.deleted.pending = Target(s) already deleted.Pending for action
@@ -243,7 +243,7 @@ message.accessdenied.view = No access to view: {0}
message.no.data = No data available
message.target.assignment = {0} Assignment(s) done
message.target.deleted = {0} Target(s) deleted
message.dist.deleted = {0} Distribution set(s) deleted
message.dist.deleted = {0} Distribution Set(s) deleted
message.tag.update.mandatory = Please select the Tag to update
message.tag.duplicate.check = {0} already exists, please enter another value
message.type.key.duplicate.check = Distribution type with key {0} already exists, please give another value
@@ -262,10 +262,10 @@ message.dists.assign.tag.alreadyassigned = Few of the DistributionSet's are alre
message.dists.tag.assigned = {0} DistributionSet's assigned to Tag {1}
message.dists.tag.unassigned = {0} DistributionSet's un-assigned from Tag {1}
message.dist.no.operation = {0} - already assigned/installed, No operation
message.sm.delete.confirm = Are you sure that you want to delete the selected {0} software module?
message.sm.delete.confirm = Are you sure that you want to delete the selected {0} Software Module?
message.error.os.softmodule = Please select the OS to delete
message.error.ah.softmodule = Please select the Application to delete
message.error.softmodule.deleted = The selected software module is already deleted
message.error.softmodule.deleted = The selected Software Module is already deleted
message.cancel.action = Cancel
message.cancel.action.success = Action cancelled successfully !
message.cancel.action.failed = Unable to cancel the action !
@@ -307,12 +307,12 @@ soft.module.os =OS
#Artifact upload
message.error.noFileSelected = No file selected for upload
message.error.noProvidedName = Please provide custom file name
message.error.noSwModuleSelected = Please select a software module
message.error.noSwModuleSelected = Please select a Software Module
message.no.duplicateFiles = Duplicate files selected
message.no.duplicateFile = Duplicate file selected :
message.delete.artifact = Are you sure that you want to delete artifact {0} ?
message.duplicate.filename = Duplicate file name
message.swModule.deleted = {0} Software module(s) deleted
message.swModule.deleted = {0} Software Module(s) deleted
message.error.missing.tagname = Please select tag name
message.upload.failed = Streaming Failed
message.uploadedfile.size.exceeded = File size exceeded .Allowed size {0} bytes
@@ -323,7 +323,7 @@ message.abort.upload = Are you sure that you want to abort the upload?
upload.swModuleTable.header = Software module
upload.swModuleTable.header = Software Module
upload.selectedfile.name = file selected for upload
upload.file.name = File name
upload.sha1 = SHA1 checksum
@@ -341,7 +341,7 @@ upload.reason = Reason
upload.action = Action
upload.result.status = Upload status
upload.file = Upload File
upload.caption.update.swmodule = Update software module
upload.caption.update.swmodule = Update Software Module
caption.tab.details = Details
caption.tab.description = Description
@@ -352,8 +352,8 @@ label.drop.dist.delete.area = Drop here<br>to delete
caption.assign.software.dist.accordion.tab = Assign Software Modules
message.software.assignment = {0} Assignment(s) done
message.dist.inuse = {0} Distribution is already assigned to target
message.software.dist.already.assigned = {0} Distribution already has software module {1}
message.software.dist.type.notallowed = {0} Software module type can not assign to Distribution {1}
message.software.dist.already.assigned = {0} Distribution already has Software Module {1}
message.software.dist.type.notallowed = {0} Software Module type can not assign to Distribution {1}
message.target.assigned = {0} is assigned to {1}
message.dist.type.delete = {0} DistributionType(s) Deleted successfully.
message.sw.module.type.delete = {0} Software Module Type(s) deleted successfully.
@@ -432,13 +432,13 @@ header.rolloutgroup.target.message = Messages
rollout.group.label.target.truncated = {0} targets has been truncated in the list due the target size limit of {1}
distribution.details.header = Distribution set
distribution.details.header = Distribution Set
target.details.header = Target
header.caption.mandatory = Mandatory
header.caption.typename = SoftwareModuleType
header.caption.softwaremodule = SoftwareModule
header.caption.unassign = Unassign
message.sw.unassigned = Software module {0} successfully unassigned
message.sw.unassigned = Software Module {0} successfully unassigned
header.caption.upload.details = Upload details
combo.type.tag.name = Type tag name
@@ -454,10 +454,10 @@ menu.title = Software Provisioning
prompt.number.of.groups = Number of groups
prompt.tigger.threshold = Trigger threshold
prompt.error.threshold = Error threshold
prompt.distribution.set = Distribution set
caption.configure.rollout = Configure rollout
caption.update.rollout = Update rollout
prompt.target.filter = Custom target filter
prompt.distribution.set = Distribution Set
caption.configure.rollout = Configure Rollout
caption.update.rollout = Update Rollout
prompt.target.filter = Custom Target Filter
message.rollout.nonzero.group.number = Number of groups must be greater than zero
message.rollout.max.group.number = Number of groups must not be greater than 500
message.rollout.duplicate.check = Rollout [ {0} ] must be unique, entered value already exists.
@@ -471,8 +471,8 @@ message.rollout.noofgroups.or.targetfilter.missing = Please enter number of grou
message.rollouts = Rollouts
label.target.per.group = Targets per group :
message.dist.already.assigned = Distribution {0} is already assigned to target
message.error.creating.rollout = Server error. Error creating rollout. Please contact the administrator
message.error.starting.rollout = Server error. Error starting rollout. Please contact the administrator
message.error.creating.rollout = Server error. Error creating Rollout. Please contact the administrator
message.error.starting.rollout = Server error. Error starting Rollout. Please contact the administrator
#Target Filter Management
breadcrumb.target.filter.custom.filters = Custom Filters

View File

@@ -79,14 +79,14 @@ caption.delete.swmodule.accordion.tab = Delete SW Modules
caption.delete.dist.set.type.accordion.tab = Delete Distribution Set Type
caption.delete.sw.module.type.accordion.tab = Delete Software Module Type
caption.attributes = Attributes
caption.panel.dist.installed = Installed distribution set
caption.panel.dist.assigned = Assigned distribution set
caption.panel.dist.installed = Installed Distribution Set
caption.panel.dist.assigned = Assigned Distribution Set
caption.soft.delete.confirmbox = Confirm Software Module Delete Action
caption.cancel.action.confirmbox = Confirm action cancellation
caption.cancel.action.confirmbox = Confirm Action Cancellation
caption.forced.datefield = Force update at time
caption.force.action.confirmbox = Confirm Force Active Action
caption.filter.delete.confirmbox = Confirm Filter Delete Action
caption.confirm.abort.action = Confirm abort action
caption.confirm.abort.action = Confirm Abort Action
# Labels prefix with - label
label.dist.details.type = Type :
@@ -213,11 +213,11 @@ message.target.unassigned.one = {0} is unassigned from {1}
message.target.unassigned.many = {0} Targets are unassigned from {1}
message.target.assigned.pending = Some target(s) are already assigned.Pending for action
message.cannot.delete = Cannot be deleted
message.check.softwaremodule = Please provide both name and verion!
message.check.softwaremodule = Please provide both name and version!
message.duplicate.softwaremodule = {0} : {1} already exists!
message.tag.delete = Please unclick the tag {0}, then try to delete
message.dist.type.check.delete = Please unclick the distribution type {0}, then try to delete
message.swmodule.type.check.delete = Please unclick the software module type {0}, then try to delete
message.swmodule.type.check.delete = Please unclick the Software Module type {0}, then try to delete
message.targets.already.deleted = Few Target(s) are already deleted.Pending for action
message.dists.already.deleted = Few distribution(s) are already deleted.Pending for action
message.target.deleted.pending = Target(s) already deleted.Pending for action
@@ -259,10 +259,10 @@ message.dists.assign.tag.alreadyassigned = Few of the DistributionSet's are alre
message.dists.tag.assigned = {0} DistributionSet's assigned to Tag {1}
message.dists.tag.unassigned = {0} DistributionSet's un-assigned from Tag {1}
message.dist.no.operation = {0} - already assigned/installed, No operation
message.sm.delete.confirm = Are you sure that you want to delete the selected {0} software module?
message.sm.delete.confirm = Are you sure that you want to delete the selected {0} Software Module?
message.error.os.softmodule = Please select the OS to delete
message.error.ah.softmodule = Please select the Application to delete
message.error.softmodule.deleted = The selected software module is already deleted
message.error.softmodule.deleted = The selected Software Module is already deleted
message.cancel.action = Cancel
message.cancel.action.success = Action cancelled successfully !
message.cancel.action.failed = Unable to cancel the action !
@@ -277,7 +277,7 @@ message.action.not.allowed = Action not allowed
message.onlyone.distribution.assigned = Only one distribution set can be assigned
message.onlyone.distribution.dropallowed = Only one distribution set can be dropped
message.error.missing.typename = Missing Type Name
message.error.missing.typenameorkey = Missing Type Name or Key or software module type
message.error.missing.typenameorkey = Missing Type Name or Key or Software Module type
message.tag.cannot.be.assigned = Target/DS cannot be assigned to {0}
message.no.targets.assiged.fortag = No targets are assigned to tag {0}
message.error.missing.tagname = Please select tag name
@@ -305,12 +305,12 @@ soft.module.os =OS
#Artifact upload
message.error.noFileSelected = No file selected for upload
message.error.noProvidedName = Please provide custom file name
message.error.noSwModuleSelected = Please select a software module
message.error.noSwModuleSelected = Please select a Software Module
message.no.duplicateFiles = Duplicate files selected
message.no.duplicateFile = Duplicate file selected :
message.delete.artifact = Are you sure that you want to delete artifact {0} ?
message.duplicate.filename = Duplicate file name
message.swModule.deleted = {0} Software module(s) deleted
message.swModule.deleted = {0} Software Module(s) deleted
message.upload.failed = Streaming Failed
message.uploadedfile.size.exceeded = File size exceeded .Allowed size {0} bytes
message.uploadedfile.aborted = File upload aborted
@@ -318,7 +318,7 @@ message.file.not.found = File not found
message.abort.upload = Are you sure that you want to abort the upload?
upload.swModuleTable.header = Software module
upload.swModuleTable.header = Software Module
upload.selectedfile.name = file selected for upload
upload.file.name = File name
upload.sha1 = SHA1 checksum
@@ -336,7 +336,7 @@ upload.reason = Reason
upload.action = Action
upload.result.status = Upload status
upload.file = Upload File
upload.caption.update.swmodule = Update software module
upload.caption.update.swmodule = Update Software Module
caption.tab.details = Details
caption.tab.description = Description
@@ -348,8 +348,8 @@ label.drop.dist.delete.area = Drop here<br>to delete
caption.assign.software.dist.accordion.tab = Assign Software Modules
message.software.assignment = {0} Software Module(s) Assignment(s) done
message.dist.inuse = {0} Distribution is already assigned to target
message.software.dist.already.assigned = {0} Distribution already has software module {1}
message.software.dist.type.notallowed = {0} Software module type can not assign to Distribution {1}
message.software.dist.already.assigned = {0} Distribution already has Software Module {1}
message.software.dist.type.notallowed = {0} Software Module type can not assign to Distribution {1}
message.target.assigned = {0} is assigned to {1}
message.dist.type.delete = {0} DistributionType(s) Deleted successfully.
message.sw.module.type.delete = {0} Software Module Type(s) deleted successfully.
@@ -415,7 +415,7 @@ header.assigned.ds = Assigned DS
header.installed.ds = Installed DS
header.status = Status
header.target.tags = Tags
header.distributionset = Distribution set
header.distributionset = Distribution Set
header.numberofgroups = No. of groups
header.detail.status = Detail status
header.total.targets = Total targets
@@ -429,7 +429,7 @@ header.rolloutgroup.target.message = Messages
rollout.group.label.target.truncated = {0} targets has been truncated in the list due the target size limit of {1}
distribution.details.header = Distribution set
distribution.details.header = Distribution Set
target.details.header = Target
header.caption.mandatory = Mandatory
header.caption.typename = SoftwareModuleType
@@ -448,10 +448,10 @@ menu.title = Software Provisioning
prompt.number.of.groups = Number of groups
prompt.tigger.threshold = Trigger threshold
prompt.error.threshold = Error threshold
prompt.distribution.set = Distribution set
caption.configure.rollout = Configure rollout
caption.update.rollout = Update rollout
prompt.target.filter = Custom target filter
prompt.distribution.set = Distribution Set
caption.configure.rollout = Configure Rollout
caption.update.rollout = Update Rollout
prompt.target.filter = Custom Target Filter
message.rollout.nonzero.group.number = Number of groups must be greater than zero
message.rollout.max.group.number = Number of groups must not be greater than 500
message.rollout.duplicate.check = Rollout [ {0} ] must be unique, entered value already exists.
@@ -465,8 +465,8 @@ message.rollout.noofgroups.or.targetfilter.missing = Please enter number of grou
message.rollouts = Rollouts
label.target.per.group = Targets per group :
message.dist.already.assigned = Distribution {0} is already assigned to target
message.error.creating.rollout = Server error. Error creating rollout. Please contact the administrator
message.error.starting.rollout = Server error. Error starting rollout. Please contact the administrator
message.error.creating.rollout = Server error. Error creating Rollout. Please contact the administrator
message.error.starting.rollout = Server error. Error starting Rollout. Please contact the administrator
#Target Filter Management
breadcrumb.target.filter.custom.filters = Custom Filters

View File

@@ -111,6 +111,7 @@
<jlorem.version>1.1</jlorem.version>
<json-simple.version>1.1.1</json-simple.version>
<commons-lang3.version>3.4</commons-lang3.version>
<commons-collections4.version>4.0</commons-collections4.version>
<json.version>20141113</json.version>
<rsql-parser.version>2.0.0</rsql-parser.version>
<!-- Misc libraries versions - END -->
@@ -560,6 +561,11 @@
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>${commons-collections4.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>