# WARNING: head commit changed in the meantime

Signed-off-by:
This commit is contained in:
Gaurav
2016-08-17 18:11:27 +02:00
55 changed files with 1186 additions and 495 deletions

View File

@@ -24,6 +24,12 @@ The simulator has user authentication enabled in **cloud profile**. Default cred
This can be configured/disabled by spring boot properties
## hawkBit APIs
The simulator supports `DDI` as well as the `DMF` integration APIs.
In case there is no AMQP message broker (like rabbitMQ) running, you can disable the AMQP support for the device simulator, so the simulator is not trying to connect to an amqp message broker.
Configuration property `hawkbit.device.simulator.amqp.enabled=true`
## Usage
### Graphical User Interface

View File

@@ -17,6 +17,4 @@ applications:
services:
- dmf-rabbit
env:
SPRING_PROFILES_ACTIVE: cloud,amqp
CF_STAGING_TIMEOUT: 15
CF_STARTUP_TIMEOUT: 15
SPRING_PROFILES_ACTIVE: cloud

View File

@@ -72,6 +72,7 @@ public class SimulatedDeviceFactory {
final int pollDelaySec, final URL baseEndpoint, final String gatewayToken) {
switch (protocol) {
case DMF_AMQP:
spSenderService.createOrUpdateThing(tenant, id);
return new DMFSimulatedDevice(id, tenant, spSenderService, pollDelaySec);
case DDI_HTTP:
final ControllerResource controllerResource = Feign.builder().logger(new Logger.ErrorLogger())

View File

@@ -8,11 +8,13 @@
*/
package org.eclipse.hawkbit.simulator;
import static org.eclipse.hawkbit.simulator.amqp.AmqpProperties.CONFIGURATION_PREFIX;
import java.net.MalformedURLException;
import java.net.URL;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
import org.eclipse.hawkbit.simulator.amqp.AmqpProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -21,22 +23,19 @@ import org.springframework.web.bind.annotation.RestController;
/**
* REST endpoint for controlling the device simulator.
*
*
*
*/
@RestController
public class SimulationController {
@Autowired
private SpSenderService spSenderService;
@Autowired
private DeviceSimulatorRepository repository;
@Autowired
private SimulatedDeviceFactory deviceFactory;
@Autowired
private AmqpProperties amqpProperties;
/**
* The start resource to start a device creation.
*
@@ -82,6 +81,12 @@ public class SimulationController {
return ResponseEntity.badRequest().body("query param api only allows value of 'dmf' or 'ddi'");
}
if (protocol == Protocol.DMF_AMQP && isDmfDisabled()) {
return ResponseEntity.badRequest()
.body("The AMQP interface has been disabled, to use DMF protocol you need to enable the AMQP interface via '"
+ CONFIGURATION_PREFIX + ".enabled=true'");
}
for (int i = 0; i < amount; i++) {
final String deviceId = name + i;
repository.add(deviceFactory.createSimulatedDevice(deviceId, tenant, protocol, pollDelay, new URL(endpoint),
@@ -90,4 +95,8 @@ public class SimulationController {
return ResponseEntity.ok("Updated " + amount + " DMF connected targets!");
}
private boolean isDmfDisabled() {
return !amqpProperties.isEnabled();
}
}

View File

@@ -11,7 +11,7 @@ package org.eclipse.hawkbit.simulator;
import java.net.MalformedURLException;
import java.net.URL;
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
import org.eclipse.hawkbit.simulator.amqp.AmqpProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -32,24 +32,27 @@ public class SimulatorStartup implements ApplicationListener<ContextRefreshedEve
@Autowired
private SimulationProperties simulationProperties;
@Autowired
private SpSenderService spSenderService;
@Autowired
private DeviceSimulatorRepository repository;
@Autowired
private SimulatedDeviceFactory deviceFactory;
@Autowired
private AmqpProperties amqpProperties;
@Override
public void onApplicationEvent(final ContextRefreshedEvent event) {
simulationProperties.getAutostarts().forEach(autostart -> {
for (int i = 0; i < autostart.getAmount(); i++) {
final String deviceId = autostart.getName() + i;
try {
if (amqpProperties.isEnabled()) {
repository.add(deviceFactory.createSimulatedDevice(deviceId, autostart.getTenant(),
autostart.getApi(), autostart.getPollDelay(), new URL(autostart.getEndpoint()),
autostart.getGatewayToken()));
}
} catch (final MalformedURLException e) {
LOGGER.error("Creation of simulated device at startup failed.", e);
}

View File

@@ -8,6 +8,8 @@
*/
package org.eclipse.hawkbit.simulator.amqp;
import static org.eclipse.hawkbit.simulator.amqp.AmqpProperties.CONFIGURATION_PREFIX;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
@@ -27,6 +29,7 @@ import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -39,6 +42,7 @@ import org.springframework.retry.support.RetryTemplate;
*/
@Configuration
@EnableConfigurationProperties(AmqpProperties.class)
@ConditionalOnProperty(prefix = CONFIGURATION_PREFIX, name = "enabled")
public class AmqpConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(AmqpConfiguration.class);

View File

@@ -19,6 +19,17 @@ import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties("hawkbit.device.simulator.amqp")
public class AmqpProperties {
/**
* The prefix for this configuration.
*/
public static final String CONFIGURATION_PREFIX = "hawkbit.device.simulator.amqp";
/**
* Indicates if the AMQP interface is enabled for the device simulator.
*/
private boolean enabled;
/**
* Queue for receiving DMF messages from update server.
*/
@@ -84,4 +95,12 @@ public class AmqpProperties {
public void setDeadLetterTtl(final int deadLetterTtl) {
this.deadLetterTtl = deadLetterTtl;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(final boolean enabled) {
this.enabled = enabled;
}
}

View File

@@ -8,6 +8,8 @@
*/
package org.eclipse.hawkbit.simulator.amqp;
import static org.eclipse.hawkbit.simulator.amqp.AmqpProperties.CONFIGURATION_PREFIX;
import java.util.Map;
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
@@ -22,6 +24,7 @@ import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
@@ -32,6 +35,7 @@ import com.google.common.collect.Lists;
*
*/
@Component
@ConditionalOnProperty(prefix = CONFIGURATION_PREFIX, name = "enabled")
public class SpReceiverService extends ReceiverService {
private static final Logger LOGGER = LoggerFactory.getLogger(ReceiverService.class);

View File

@@ -52,6 +52,7 @@ public class GenerateDialog extends Window {
private final TextField gatewayTokenTextField;
private OptionGroup protocolGroup;
private Button buttonOk;
private final boolean dmfEnabled;
/**
* Creates a new pop window for setting the configuration of simulating
@@ -60,9 +61,12 @@ public class GenerateDialog extends Window {
* @param callback
* the callback which is called when the dialog has been
* successfully confirmed.
* @param dmfEnabled
* indicates if the AMQP/DMF interface is enabled by
* configuration and if the option DMF should be enabled or not
*/
public GenerateDialog(final GenerateDialogCallback callback) {
public GenerateDialog(final GenerateDialogCallback callback, final boolean dmfEnabled) {
this.dmfEnabled = dmfEnabled;
formLayout.setSpacing(true);
formLayout.setMargin(true);
@@ -187,15 +191,19 @@ public class GenerateDialog extends Window {
this.protocolGroup = new OptionGroup("Simulated Device Protocol");
protocolGroup.addItem(Protocol.DMF_AMQP);
protocolGroup.addItem(Protocol.DDI_HTTP);
protocolGroup.select(Protocol.DMF_AMQP);
protocolGroup.setItemCaption(Protocol.DMF_AMQP, "Device Management Federation API (AMQP push)");
protocolGroup.setItemCaption(Protocol.DDI_HTTP, "Direct Device Interface (HTTP poll)");
protocolGroup.setNullSelectionAllowed(false);
protocolGroup.select(Protocol.DMF_AMQP);
protocolGroup.addValueChangeListener(event -> {
final boolean directDeviceOptionSelected = event.getProperty().getValue().equals(Protocol.DDI_HTTP);
pollUrlTextField.setVisible(directDeviceOptionSelected);
gatewayTokenTextField.setVisible(directDeviceOptionSelected);
});
protocolGroup.setItemEnabled(Protocol.DMF_AMQP, dmfEnabled);
if (!dmfEnabled) {
protocolGroup.select(Protocol.DDI_HTTP);
}
}
private void createOkButton(final GenerateDialogCallback callback) {

View File

@@ -17,6 +17,7 @@ import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Status;
import org.eclipse.hawkbit.simulator.DeviceSimulatorRepository;
import org.eclipse.hawkbit.simulator.SimulatedDeviceFactory;
import org.eclipse.hawkbit.simulator.UpdateStatus.ResponseStatus;
import org.eclipse.hawkbit.simulator.amqp.AmqpProperties;
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
import org.eclipse.hawkbit.simulator.event.InitUpdate;
import org.eclipse.hawkbit.simulator.event.NextPollCounterUpdate;
@@ -90,6 +91,9 @@ public class SimulatorView extends VerticalLayout implements View {
@Autowired
private transient EventBus eventbus;
@Autowired
private transient AmqpProperties amqpProperties;
private final Label caption = new Label("DMF/DDI Simulated Devices");
private final HorizontalLayout toolbar = new HorizontalLayout();
private final Grid grid = new Grid();
@@ -266,9 +270,8 @@ public class SimulatorView extends VerticalLayout implements View {
final String deviceId = namePrefix + index;
beanContainer.addBean(repository.add(deviceFactory.createSimulatedDevice(deviceId,
tenant.toLowerCase(), protocol, pollDelay, basePollUrl, gatewayToken)));
spSenderService.createOrUpdateThing(tenant, deviceId);
}
}));
}, amqpProperties.isEnabled()));
}
private ProtocolConverter createProtocolConverter() {

View File

@@ -8,6 +8,7 @@
#
## Configuration for DMF communication
hawkbit.device.simulator.amqp.enabled=true
hawkbit.device.simulator.amqp.receiverConnectorQueueFromSp=simulator_receiver
hawkbit.device.simulator.amqp.deadLetterQueue=simulator_deadletter
hawkbit.device.simulator.amqp.deadLetterExchange=simulator.deadletter

View File

@@ -281,6 +281,16 @@ public interface DistributionSetManagement {
Page<DistributionSetMetadata> findDistributionSetMetadataByDistributionSetId(@NotNull Long distributionSetId,
@NotNull Pageable pageable);
/**
* Finds all meta data by the given distribution set id.
*
* @param distributionSetId
* the distribution set id to retrieve the meta data from
* @return list of distributionSetMetadata for a given distribution set Id.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
List<DistributionSetMetadata> findDistributionSetMetadataByDistributionSetId(@NotNull Long distributionSetId);
/**
* finds all meta data by the given distribution set id.
*

View File

@@ -484,4 +484,22 @@ public interface SoftwareManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
SoftwareModuleType updateSoftwareModuleType(@NotNull SoftwareModuleType sm);
/**
* Finds all meta data by the given software module id.
*
* @param softwareModuleId
* the software module id to retrieve the meta data from
*
* @throws RSQLParameterUnsupportedFieldException
* if a field in the RSQL string is used but not provided by the
* given {@code fieldNameProvider}
* @throws RSQLParameterSyntaxException
* if the RSQL syntax is wrong
* @return result of all meta data entries for a given software
* module id.
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
List<SoftwareModuleMetadata> findSoftwareModuleMetadataBySoftwareModuleId(Long softwareModuleId);
}

View File

@@ -0,0 +1,28 @@
/**
* 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.repository.model.DistributionSet;
/**
* Defines the {@link AbstractBaseEntityEvent} of creating a new {@link DistributionSet}.
*
*/
public class DistributionCreatedEvent extends AbstractBaseEntityEvent<DistributionSet> {
private static final long serialVersionUID = 1L;
/**
* @param distributionSet
* the distributionSet which has been created
*/
public DistributionCreatedEvent(final DistributionSet distributionSet) {
super(distributionSet);
}
}

View File

@@ -0,0 +1,36 @@
/**
* 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;
import org.eclipse.hawkbit.repository.model.DistributionSet;
/**
* Defines the {@link AbstractDistributedEvent} for deletion of
* {@link DistributionSet}.
*/
public class DistributionDeletedEvent extends AbstractDistributedEvent {
private static final long serialVersionUID = -3308850381757843098L;
private final Long distributionId;
/**
* @param tenant
* the tenant for this event
* @param distributionId
* the ID of the distribution set which has been deleted
*/
public DistributionDeletedEvent(final String tenant, final Long distributionId) {
super(-1, tenant);
this.distributionId = distributionId;
}
public Long getDistributionSetId() {
return distributionId;
}
}

View File

@@ -0,0 +1,29 @@
/**
* 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.repository.model.DistributionSet;
/**
* Defines the {@link AbstractBaseEntityEvent} for update a {@link DistributionSet}.
*
*/
public class DistributionSetUpdateEvent extends AbstractBaseEntityEvent<DistributionSet> {
private static final long serialVersionUID = 1L;
/**
* Constructor
* @param ds Distribution Set
*/
public DistributionSetUpdateEvent(final DistributionSet ds) {
super(ds);
}
}

View File

@@ -0,0 +1,41 @@
/**
* 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;
import org.eclipse.hawkbit.repository.model.Target;
/**
*
* Defines the {@link AbstractBaseEntityEvent} of deleting a {@link Target}.
*/
public class TargetDeletedEvent extends AbstractDistributedEvent {
private static final long serialVersionUID = 1L;
private final long targetId;
/**
* @param tenant
* the tenant for this event
* @param targetId
* the ID of the target which has been deleted
*/
public TargetDeletedEvent(final String tenant, final long targetId) {
super(-1, tenant);
this.targetId = targetId;
}
/**
* @return the targetId
*/
public long getTargetId() {
return targetId;
}
}

View File

@@ -0,0 +1,31 @@
/**
* 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.repository.model.Target;
/**
* Defines the {@link AbstractBaseEntityEvent} of updating a {@link Target}.
*
*/
public class TargetUpdatedEvent extends AbstractBaseEntityEvent<Target> {
private static final long serialVersionUID = 5665118668865832477L;
/**
* Constructor
*
* @param baseEntity
* Target entity
*/
public TargetUpdatedEvent(final Target baseEntity) {
super(baseEntity);
}
}

View File

@@ -28,6 +28,7 @@ import org.eclipse.hawkbit.repository.DistributionSetMetadataFields;
import org.eclipse.hawkbit.repository.DistributionSetTypeFields;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.TagManagement;
import org.eclipse.hawkbit.repository.eventbus.event.DistributionDeletedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.DistributionSetTagAssigmentResultEvent;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityLockedException;
@@ -54,6 +55,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSetTagAssignmentResult;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
@@ -102,6 +104,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Autowired
private AfterTransactionCommitExecutor afterCommit;
@Autowired
private TenantAware tenantAware;
@Override
public DistributionSet findDistributionSetByIdWithDetails(final Long distid) {
return distributionSetRepository.findOne(DistributionSetSpecification.byId(distid));
@@ -193,6 +198,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
// handle the empty list
distributionSetRepository.deleteByIdIn(toHardDelete);
}
Arrays.stream(distributionSetIDs)
.forEach(dsId -> eventBus.post(new DistributionDeletedEvent(tenantAware.getCurrentTenant(), dsId)));
}
@Override
@@ -475,11 +483,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
if (distributionSetMetadataRepository.exists(metadata.getId())) {
throwMetadataKeyAlreadyExists(metadata.getId().getKey());
}
// merge base distribution set so optLockRevision gets updated and audit
// log written because
// modifying metadata is modifying the base distribution set itself for
// auditing purposes.
entityManager.merge((JpaDistributionSet) metadata.getDistributionSet()).setLastModifiedAt(0L);
touch(metadata.getDistributionSet());
return distributionSetMetadataRepository.save(metadata);
}
@@ -494,7 +499,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
for (final JpaDistributionSetMetadata distributionSetMetadata : metadata) {
checkAndThrowAlreadyIfDistributionSetMetadataExists(distributionSetMetadata.getId());
}
metadata.forEach(m -> entityManager.merge((JpaDistributionSet) m.getDistributionSet()).setLastModifiedAt(0L));
metadata.forEach(m -> touch(m.getDistributionSet()));
return new ArrayList<>(
(Collection<? extends DistributionSetMetadata>) distributionSetMetadataRepository.save(metadata));
@@ -510,7 +515,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
findOne(metadata.getDistributionSet(), metadata.getKey());
// touch it to update the lock revision because we are modifying the
// DS indirectly
entityManager.merge((JpaDistributionSet) metadata.getDistributionSet()).setLastModifiedAt(0L);
touch(metadata.getDistributionSet());;
return distributionSetMetadataRepository.save(metadata);
}
@@ -518,13 +523,26 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying
public void deleteDistributionSetMetadata(final DistributionSet distributionSet, final String key) {
touch(distributionSet);
distributionSetMetadataRepository.delete(new DsMetadataCompositeKey(distributionSet, key));
}
/**
* Method to get the latest distribution set based on ds ID after the metadata changes for that distribution set.
* @param distributionSet Distribution set
*/
private void touch(final DistributionSet distributionSet) {
final DistributionSet latestDistributionSet = findDistributionSetById(distributionSet.getId());
// merge base distribution set so optLockRevision gets updated and audit
// log written because
// modifying metadata is modifying the base distribution set itself for
// auditing purposes.
entityManager.merge((JpaDistributionSet) latestDistributionSet).setLastModifiedAt(0L);
}
@Override
public Page<DistributionSetMetadata> findDistributionSetMetadataByDistributionSetId(final Long distributionSetId,
final Pageable pageable) {
return convertMdPage(distributionSetMetadataRepository
.findAll((Specification<JpaDistributionSetMetadata>) (root, query, cb) -> cb.equal(
root.get(JpaDistributionSetMetadata_.distributionSet).get(JpaDistributionSet_.id),
@@ -532,6 +550,14 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
pageable);
}
@Override
public List<DistributionSetMetadata> findDistributionSetMetadataByDistributionSetId(final Long distributionSetId) {
return new ArrayList<DistributionSetMetadata>(distributionSetMetadataRepository
.findAll((Specification<JpaDistributionSetMetadata>) (root, query, cb) -> cb.equal(
root.get(JpaDistributionSetMetadata_.distributionSet).get(JpaDistributionSet_.id),
distributionSetId)));
}
@Override
public Page<DistributionSetMetadata> findDistributionSetMetadataByDistributionSetId(final Long distributionSetId,
final String rsqlParam, final Pageable pageable) {

View File

@@ -621,6 +621,16 @@ public class JpaSoftwareManagement implements SoftwareManagement {
pageable);
}
@Override
public List<SoftwareModuleMetadata> findSoftwareModuleMetadataBySoftwareModuleId(final Long softwareModuleId) {
return new ArrayList<>(
softwareModuleMetadataRepository
.findAll((Specification<JpaSoftwareModuleMetadata>) (root, query,
cb) -> cb.and(cb.equal(
root.get(JpaSoftwareModuleMetadata_.softwareModule).get(JpaSoftwareModule_.id),
softwareModuleId))));
}
@Override
public SoftwareModuleMetadata findSoftwareModuleMetadata(final SoftwareModule softwareModule, final String key) {
return findSoftwareModuleMetadata(new SwMetadataCompositeKey(softwareModule, key));

View File

@@ -29,6 +29,7 @@ import javax.persistence.criteria.Root;
import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.eventbus.event.TargetDeletedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.TargetTagAssigmentResultEvent;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
@@ -48,6 +49,7 @@ import org.eclipse.hawkbit.repository.model.TargetIdName;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.domain.Page;
@@ -94,6 +96,9 @@ public class JpaTargetManagement implements TargetManagement {
@Autowired
private EventBus eventBus;
@Autowired
private TenantAware tenantAware;
@Autowired
private AfterTransactionCommitExecutor afterCommit;
@@ -206,6 +211,8 @@ public class JpaTargetManagement implements TargetManagement {
targetInfoRepository.deleteByTargetIdIn(targetsForCurrentTenant);
targetRepository.deleteByIdIn(targetsForCurrentTenant);
}
targetsForCurrentTenant
.forEach(targetId -> eventBus.post(new TargetDeletedEvent(tenantAware.getCurrentTenant(), targetId)));
}
@Override

View File

@@ -1,154 +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.repository.jpa.eventbus;
import java.util.Collection;
import javax.persistence.EntityManager;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.eclipse.hawkbit.eventbus.event.TargetDeletedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.TargetInfoUpdateEvent;
import org.eclipse.hawkbit.repository.jpa.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.google.common.eventbus.EventBus;
/**
* An aspect implementation which wraps the necessary repository services for
* saving {@link TenantAwareBaseEntity}s to publish create or update events.
*
*
*
*
*/
@Service
@Aspect
public class EntityChangeEventListener {
@Autowired
private EventBus eventBus;
@Autowired
private TenantAware tenantAware;
@Autowired
private EntityManager entityManager;
@Autowired
private AfterTransactionCommitExecutor afterCommit;
/**
* In case the a {@link Target} is created a corresponding
* {@link TargetInfo} is created as well. We need the {@link TargetInfo}
* information in the target created event. So we are listening to the
* {@link TargetInfo} creation to indicate if an Target has been created.
*
* @param joinpoint
* the aspect join point
* @return the object of the {@link ProceedingJoinPoint#proceed()}
* @throws Throwable
* in case exception happens in the
* {@link ProceedingJoinPoint#proceed()}
*/
@Around("execution(* org.eclipse.hawkbit.repository.jpa.TargetInfoRepository.save(..))")
// Exception squid:S00112 - Is aspectJ proxy
@SuppressWarnings({ "squid:S00112" })
public Object targetCreated(final ProceedingJoinPoint joinpoint) throws Throwable {
final boolean isNew = isTargetInfoNew(joinpoint.getArgs()[0]);
final Object result = joinpoint.proceed();
if (result instanceof TargetInfo) {
if (isNew) {
notifyTargetCreated(entityManager.merge(entityManager.merge(((TargetInfo) result).getTarget())));
} else {
notifyTargetInfoChanged((TargetInfo) result);
}
}
return result;
}
/**
* Proxy method around the delete method of the {@link TargetRepository} to
* notify the {@link TargetDeletedEvent} in case targets has been deleted.
*
* @param joinpoint
* the aspect join point
* @return the object of the {@link ProceedingJoinPoint#proceed()}
* @throws Throwable
* in case exception happens in the
* {@link ProceedingJoinPoint#proceed()}
*/
@Around("execution(* org.eclipse.hawkbit.repository.jpa.TargetRepository.deleteByIdIn(..))")
// Exception squid:S00112 - Is aspectJ proxy
@SuppressWarnings({ "squid:S00112" })
public Object targetDeletedById(final ProceedingJoinPoint joinpoint) throws Throwable {
final String currentTenant = tenantAware.getCurrentTenant();
final Object result = joinpoint.proceed();
final Collection<Long> targetIds = (Collection<Long>) joinpoint.getArgs()[0];
targetIds.forEach(targetId -> notifyTargetDeleted(currentTenant, targetId));
return result;
}
/**
* Proxy method around the delete method of the {@link TargetRepository} to
* notify the {@link TargetDeletedEvent} in case targets has been deleted.
*
* @param joinpoint
* the aspect join point
* @return the object of the {@link ProceedingJoinPoint#proceed()}
* @throws Throwable
* in case exception happens in the
* {@link ProceedingJoinPoint#proceed()}
*/
@Around("execution(* org.eclipse.hawkbit.repository.jpa.TargetRepository.delete(..))")
// Exception squid:S00112 - Is aspectJ proxy
@SuppressWarnings({ "squid:S00112", "unchecked" })
public Object targetDeleted(final ProceedingJoinPoint joinpoint) throws Throwable {
final String currentTenant = tenantAware.getCurrentTenant();
final Object result = joinpoint.proceed();
final Object param = joinpoint.getArgs()[0];
// delete by id
if (param instanceof Long) {
notifyTargetDeleted(currentTenant, (Long) param);
} else if (param instanceof Target) {
notifyTargetDeleted(currentTenant, ((Target) param).getId());
} else if (param instanceof Iterable) {
((Iterable<Target>) param).forEach(target -> notifyTargetDeleted(currentTenant, target.getId()));
}
return result;
}
private void notifyTargetCreated(final Target t) {
afterCommit.afterCommit(() -> eventBus.post(new TargetCreatedEvent(t)));
}
private void notifyTargetInfoChanged(final TargetInfo targetInfo) {
afterCommit.afterCommit(() -> eventBus.post(new TargetInfoUpdateEvent(targetInfo)));
}
private void notifyTargetDeleted(final String tenant, final Long targetId) {
afterCommit.afterCommit(() -> eventBus.post(new TargetDeletedEvent(tenant, targetId)));
}
private static boolean isTargetInfoNew(final Object targetInfo) {
return ((JpaTargetInfo) targetInfo).isNew();
}
}

View File

@@ -179,4 +179,5 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity {
return true;
}
}

View File

@@ -8,79 +8,47 @@
*/
package org.eclipse.hawkbit.repository.jpa.model;
import java.util.Map;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.repository.eventbus.event.AbstractPropertyChangeEvent;
import org.eclipse.hawkbit.repository.eventbus.event.ActionCreatedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.ActionPropertyChangeEvent;
import org.eclipse.hawkbit.repository.eventbus.event.RolloutGroupPropertyChangeEvent;
import org.eclipse.hawkbit.repository.eventbus.event.RolloutPropertyChangeEvent;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.EventBusHolder;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.eclipse.persistence.descriptors.DescriptorEventAdapter;
import org.eclipse.persistence.internal.sessions.ObjectChangeSet;
import org.eclipse.persistence.queries.UpdateObjectQuery;
import org.eclipse.persistence.sessions.changesets.DirectToFieldChangeRecord;
import com.google.common.eventbus.EventBus;
/**
* Listens to change in property values of an entity.
* Listens to change in property values of an entity and calls the corresponding
* {@link EventAwareEntity}.
*
*/
public class EntityPropertyChangeListener extends DescriptorEventAdapter {
@Override
public void postInsert(final DescriptorEvent event) {
if (event.getObject().getClass().equals(Action.class)) {
final Action action = (Action) event.getObject();
if (action.getRollout() != null) {
final EventBus eventBus = getEventBus();
final AfterTransactionCommitExecutor afterCommit = getAfterTransactionCommmitExecutor();
afterCommit.afterCommit(() -> eventBus.post(new ActionCreatedEvent(action)));
final Object object = event.getObject();
if (isEventAwareEntity(object)) {
doNotifiy(() -> ((EventAwareEntity) object).fireCreateEvent(event));
}
}
}
@Override
public void postUpdate(final DescriptorEvent event) {
if (event.getObject().getClass().equals(JpaAction.class)) {
getAfterTransactionCommmitExecutor().afterCommit(() -> getEventBus().post(
new ActionPropertyChangeEvent((Action) event.getObject(), getChangeSet(Action.class, event))));
} else if (event.getObject().getClass().equals(JpaRollout.class)) {
getAfterTransactionCommmitExecutor().afterCommit(() -> getEventBus().post(
new RolloutPropertyChangeEvent((Rollout) event.getObject(), getChangeSet(Rollout.class, event))));
} else if (event.getObject().getClass().equals(JpaRolloutGroup.class)) {
getAfterTransactionCommmitExecutor().afterCommit(
() -> getEventBus().post(new RolloutGroupPropertyChangeEvent((RolloutGroup) event.getObject(),
getChangeSet(RolloutGroup.class, event))));
final Object object = event.getObject();
if (isEventAwareEntity(object)) {
doNotifiy(() -> ((EventAwareEntity) object).fireUpdateEvent(event));
}
}
private <T extends TenantAwareBaseEntity> Map<String, AbstractPropertyChangeEvent<T>.Values> getChangeSet(
final Class<T> clazz, final DescriptorEvent event) {
final T rolloutGroup = clazz.cast(event.getObject());
final ObjectChangeSet changeSet = ((UpdateObjectQuery) event.getQuery()).getObjectChangeSet();
return changeSet.getChanges().stream().filter(record -> record instanceof DirectToFieldChangeRecord)
.map(record -> (DirectToFieldChangeRecord) record)
.collect(Collectors.toMap(record -> record.getAttribute(),
record -> new AbstractPropertyChangeEvent<T>(rolloutGroup, null).new Values(
record.getOldValue(), record.getNewValue())));
@Override
public void postDelete(final DescriptorEvent event) {
final Object object = event.getObject();
if (isEventAwareEntity(object)) {
doNotifiy(() -> ((EventAwareEntity) object).fireDeleteEvent(event));
}
}
private AfterTransactionCommitExecutor getAfterTransactionCommmitExecutor() {
return AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit();
private boolean isEventAwareEntity(final Object object) {
return object instanceof EventAwareEntity;
}
private EventBus getEventBus() {
return EventBusHolder.getInstance().getEventBus();
private void doNotifiy(final Runnable runnable) {
AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(runnable);
}
}

View File

@@ -0,0 +1,39 @@
/**
* 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.jpa.model;
import org.eclipse.persistence.descriptors.DescriptorEvent;
/**
* Interfaces which can be implemented by entities to be called when the entity
* should fire an event because the entity has been created, updated or deleted.
*/
public interface EventAwareEntity {
/**
* Fired for the Entity creation.
*
* @param descriptorEvent
*/
void fireCreateEvent(DescriptorEvent descriptorEvent);
/**
* Fired for the Entity updation.
*
* @param descriptorEvent
*/
void fireUpdateEvent(DescriptorEvent descriptorEvent);
/**
* Fired for the Entity deletion.
*
* @param descriptorEvent
*/
void fireDeleteEvent(DescriptorEvent descriptorEvent);
}

View File

@@ -28,6 +28,10 @@ import javax.persistence.NamedSubgraph;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.eclipse.hawkbit.repository.eventbus.event.ActionCreatedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.ActionPropertyChangeEvent;
import org.eclipse.hawkbit.repository.jpa.model.helper.EntityPropertyChangeHelper;
import org.eclipse.hawkbit.repository.jpa.model.helper.EventBusHolder;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -35,6 +39,7 @@ import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.eclipse.persistence.descriptors.DescriptorEvent;
/**
* JPA implementation of {@link Action}.
@@ -49,7 +54,7 @@ import org.eclipse.persistence.annotations.CascadeOnDelete;
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for
// sub entities
@SuppressWarnings("squid:S2160")
public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Action {
public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Action, EventAwareEntity {
private static final long serialVersionUID = 1L;
@ManyToOne(fetch = FetchType.LAZY)
@@ -171,4 +176,20 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
return "Action [distributionSet=" + distributionSet + ", getId()=" + getId() + "]";
}
@Override
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
EventBusHolder.getInstance().getEventBus().post(new ActionCreatedEvent(this));
}
@Override
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
EventBusHolder.getInstance().getEventBus().post(new ActionPropertyChangeEvent(this,
EntityPropertyChangeHelper.getChangeSet(Action.class, descriptorEvent)));
}
@Override
public void fireDeleteEvent(final DescriptorEvent descriptorEvent) {
// there is no action deletion
}
}

View File

@@ -13,6 +13,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -33,8 +34,14 @@ import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.eclipse.hawkbit.repository.eventbus.event.AbstractPropertyChangeEvent;
import org.eclipse.hawkbit.repository.eventbus.event.DistributionCreatedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.DistributionDeletedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.DistributionSetUpdateEvent;
import org.eclipse.hawkbit.repository.exception.DistributionSetTypeUndefinedException;
import org.eclipse.hawkbit.repository.exception.UnsupportedSoftwareModuleForThisDistributionSetException;
import org.eclipse.hawkbit.repository.jpa.model.helper.EntityPropertyChangeHelper;
import org.eclipse.hawkbit.repository.jpa.model.helper.EventBusHolder;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
@@ -45,6 +52,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.eclipse.persistence.descriptors.DescriptorEvent;
/**
* Jpa implementation of {@link DistributionSet}.
@@ -61,9 +69,13 @@ import org.eclipse.persistence.annotations.CascadeOnDelete;
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for
// sub entities
@SuppressWarnings("squid:S2160")
public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implements DistributionSet {
public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implements DistributionSet, EventAwareEntity {
private static final long serialVersionUID = 1L;
private static final String COMPLETE_PROPERTY = "complete";
private static final String DELETED_PROPERTY = "deleted";
@Column(name = "required_migration_step")
private boolean requiredMigrationStep;
@@ -279,4 +291,30 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
return complete;
}
@Override
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
EventBusHolder.getInstance().getEventBus().post(new DistributionCreatedEvent(this));
}
@Override
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
final Map<String, AbstractPropertyChangeEvent<JpaDistributionSet>.Values> changeSet = EntityPropertyChangeHelper
.getChangeSet(JpaDistributionSet.class, descriptorEvent);
EventBusHolder.getInstance().getEventBus().post(new DistributionSetUpdateEvent(this));
if (changeSet.containsKey(DELETED_PROPERTY)) {
final Boolean newDeleted = (Boolean) changeSet.get(DELETED_PROPERTY).getNewValue();
if (newDeleted) {
EventBusHolder.getInstance().getEventBus().post(new DistributionDeletedEvent(getTenant(), getId()));
}
}
}
@Override
public void fireDeleteEvent(final DescriptorEvent descriptorEvent) {
EventBusHolder.getInstance().getEventBus().post(new DistributionDeletedEvent(getTenant(), getId()));
}
}

View File

@@ -25,13 +25,17 @@ import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import org.eclipse.hawkbit.repository.eventbus.event.RolloutPropertyChangeEvent;
import org.eclipse.hawkbit.repository.jpa.cache.CacheField;
import org.eclipse.hawkbit.repository.jpa.cache.CacheKeys;
import org.eclipse.hawkbit.repository.jpa.model.helper.EntityPropertyChangeHelper;
import org.eclipse.hawkbit.repository.jpa.model.helper.EventBusHolder;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
import org.eclipse.persistence.descriptors.DescriptorEvent;
/**
* JPA implementation of a {@link Rollout}.
@@ -44,7 +48,7 @@ import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for
// sub entities
@SuppressWarnings("squid:S2160")
public class JpaRollout extends AbstractJpaNamedEntity implements Rollout {
public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, EventAwareEntity {
private static final long serialVersionUID = 1L;
@@ -197,4 +201,21 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout {
+ ", getName()=" + getName() + ", getId()=" + getId() + "]";
}
@Override
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
// there is no rollout creation event
}
@Override
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
EventBusHolder.getInstance().getEventBus().post(new RolloutPropertyChangeEvent(this,
EntityPropertyChangeHelper.getChangeSet(Rollout.class, descriptorEvent)));
}
@Override
public void fireDeleteEvent(final DescriptorEvent descriptorEvent) {
// there is no rollout deletion event
}
}

View File

@@ -25,9 +25,13 @@ import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import org.eclipse.hawkbit.repository.eventbus.event.RolloutGroupPropertyChangeEvent;
import org.eclipse.hawkbit.repository.jpa.model.helper.EntityPropertyChangeHelper;
import org.eclipse.hawkbit.repository.jpa.model.helper.EventBusHolder;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
import org.eclipse.persistence.descriptors.DescriptorEvent;
/**
* JPA entity definition of persisting a group of an rollout.
@@ -40,7 +44,7 @@ import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for
// sub entities
@SuppressWarnings("squid:S2160")
public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGroup {
public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGroup, EventAwareEntity {
private static final long serialVersionUID = 1L;
@@ -236,4 +240,19 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr
+ ", getId()=" + getId() + "]";
}
@Override
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
// there is no RolloutGroup created event
}
@Override
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
EventBusHolder.getInstance().getEventBus().post(new RolloutGroupPropertyChangeEvent(this,
EntityPropertyChangeHelper.getChangeSet(RolloutGroup.class, descriptorEvent)));
}
@Override
public void fireDeleteEvent(final DescriptorEvent descriptorEvent) {
// there is no RolloutGroup deleted event
}
}

View File

@@ -36,6 +36,10 @@ import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.eventbus.event.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.TargetDeletedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.jpa.model.helper.EventBusHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityChecker;
import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.SystemSecurityContextHolder;
@@ -45,6 +49,7 @@ import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.springframework.data.domain.Persistable;
/**
@@ -64,7 +69,7 @@ import org.springframework.data.domain.Persistable;
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for
// sub entities
@SuppressWarnings("squid:S2160")
public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Long>, Target {
public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Long>, Target, EventAwareEntity {
private static final long serialVersionUID = 1L;
@Column(name = "controller_id", length = 64)
@@ -222,6 +227,7 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Lon
* @param securityToken
* the securityToken to set
*/
@Override
public void setSecurityToken(final String securityToken) {
this.securityToken = securityToken;
}
@@ -231,4 +237,18 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Lon
return "Target [controllerId=" + controllerId + ", getId()=" + getId() + "]";
}
@Override
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
EventBusHolder.getInstance().getEventBus().post(new TargetCreatedEvent(this));
}
@Override
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
EventBusHolder.getInstance().getEventBus().post(new TargetUpdatedEvent(this));
}
@Override
public void fireDeleteEvent(final DescriptorEvent descriptorEvent) {
EventBusHolder.getInstance().getEventBus().post(new TargetDeletedEvent(getTenant(), getId()));
}
}

View File

@@ -23,6 +23,7 @@ import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
@@ -37,7 +38,9 @@ import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.eclipse.hawkbit.repository.eventbus.event.TargetInfoUpdateEvent;
import org.eclipse.hawkbit.repository.exception.InvalidTargetAddressException;
import org.eclipse.hawkbit.repository.jpa.model.helper.EventBusHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.SystemSecurityContextHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.TenantConfigurationManagementHolder;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -48,6 +51,7 @@ import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Persistable;
@@ -64,7 +68,8 @@ import org.springframework.data.domain.Persistable;
@Table(name = "sp_target_info", indexes = {
@Index(name = "sp_idx_target_info_02", columnList = "target_id,update_status") })
@Entity
public class JpaTargetInfo implements Persistable<Long>, TargetInfo {
@EntityListeners(EntityPropertyChangeListener.class)
public class JpaTargetInfo implements Persistable<Long>, TargetInfo, EventAwareEntity {
private static final long serialVersionUID = 1L;
private static final Logger LOG = LoggerFactory.getLogger(TargetInfo.class);
@@ -321,4 +326,19 @@ public class JpaTargetInfo implements Persistable<Long>, TargetInfo {
}
return true;
}
@Override
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
// there is no target info created event
}
@Override
public void fireUpdateEvent(final DescriptorEvent descriptorEvent) {
EventBusHolder.getInstance().getEventBus().post(new TargetInfoUpdateEvent(this));
}
@Override
public void fireDeleteEvent(final DescriptorEvent descriptorEvent) {
// there is no target info deleted event
}
}

View File

@@ -0,0 +1,46 @@
/**
* 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.jpa.model.helper;
import java.util.Map;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.repository.eventbus.event.AbstractPropertyChangeEvent;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.eclipse.persistence.internal.sessions.ObjectChangeSet;
import org.eclipse.persistence.queries.UpdateObjectQuery;
import org.eclipse.persistence.sessions.changesets.DirectToFieldChangeRecord;
/**
* Helper class to get the change set for the property changes in the Entity.
*
* @param <T>
*/
public class EntityPropertyChangeHelper<T extends TenantAwareBaseEntity> {
/**
* To get the map of entity property change set
*
* @param clazz
* @param event
* @return the map of the changeSet
*/
public static <T extends TenantAwareBaseEntity> Map<String, AbstractPropertyChangeEvent<T>.Values> getChangeSet(
final Class<T> clazz, final DescriptorEvent event) {
final T rolloutGroup = clazz.cast(event.getObject());
final ObjectChangeSet changeSet = ((UpdateObjectQuery) event.getQuery()).getObjectChangeSet();
return changeSet.getChanges().stream().filter(record -> record instanceof DirectToFieldChangeRecord)
.map(record -> (DirectToFieldChangeRecord) record)
.collect(Collectors.toMap(record -> record.getAttribute(),
record -> new AbstractPropertyChangeEvent<T>(rolloutGroup, null).new Values(
record.getOldValue(), record.getNewValue())));
}
}

View File

@@ -0,0 +1,161 @@
/**
* 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.jpa.eventbus;
import static org.fest.assertions.Assertions.assertThat;
import java.net.URI;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.eclipse.hawkbit.eventbus.event.Event;
import org.eclipse.hawkbit.repository.eventbus.event.DistributionCreatedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.DistributionDeletedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.TargetDeletedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.TargetInfoUpdateEvent;
import org.eclipse.hawkbit.repository.eventbus.event.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.fest.assertions.api.Assertions;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
@Features("Component Tests - Repository")
@Stories("Entity Events")
public class RepositoryEntityEventTest extends AbstractJpaIntegrationTest {
@Autowired
private EventBus eventBus;
private final MyEventListener eventListener = new MyEventListener();
@Before
public void beforeTest() {
eventListener.queue.clear();
eventBus.register(eventListener);
}
@Test
@Description("Verifies that the target created event is published when a target has been created")
public void targetCreatedEventIsPublished() throws InterruptedException {
final Target createdTarget = targetManagement.createTarget(entityFactory.generateTarget("12345"));
final TargetCreatedEvent targetCreatedEvent = eventListener.waitForEvent(TargetCreatedEvent.class, 1,
TimeUnit.SECONDS);
assertThat(targetCreatedEvent).isNotNull();
assertThat(targetCreatedEvent.getEntity().getId()).isEqualTo(createdTarget.getId());
}
@Test
@Description("Verifies that the target update event is published when a target has been updated")
public void targetUpdateEventIsPublished() throws InterruptedException {
final Target createdTarget = targetManagement.createTarget(entityFactory.generateTarget("12345"));
createdTarget.setName("updateName");
targetManagement.updateTarget(createdTarget);
final TargetUpdatedEvent targetUpdatedEvent = eventListener.waitForEvent(TargetUpdatedEvent.class, 1,
TimeUnit.SECONDS);
assertThat(targetUpdatedEvent).isNotNull();
assertThat(targetUpdatedEvent.getEntity().getId()).isEqualTo(createdTarget.getId());
}
@Test
@Description("Verifies that the target info update event is published when a target info has been updated")
public void targetInfoUpdateEventIsPublished() throws InterruptedException {
final Target createdTarget = targetManagement.createTarget(entityFactory.generateTarget("12345"));
controllerManagament.updateTargetStatus(createdTarget.getTargetInfo(), TargetUpdateStatus.PENDING,
System.currentTimeMillis(), URI.create("http://127.0.0.1"));
final TargetInfoUpdateEvent targetInfoUpdatedEvent = eventListener.waitForEvent(TargetInfoUpdateEvent.class, 1,
TimeUnit.SECONDS);
assertThat(targetInfoUpdatedEvent).isNotNull();
assertThat(targetInfoUpdatedEvent.getEntity().getTarget().getId()).isEqualTo(createdTarget.getId());
}
@Test
@Description("Verifies that the target deleted event is published when a target has been deleted")
public void targetDeletedEventIsPublished() throws InterruptedException {
final Target createdTarget = targetManagement.createTarget(entityFactory.generateTarget("12345"));
targetManagement.deleteTargets(createdTarget.getId());
final TargetDeletedEvent targetDeletedEvent = eventListener.waitForEvent(TargetDeletedEvent.class, 1,
TimeUnit.SECONDS);
assertThat(targetDeletedEvent).isNotNull();
assertThat(targetDeletedEvent.getTargetId()).isEqualTo(createdTarget.getId());
}
@Test
@Description("Verifies that the distribution set created event is published when a distribution set has been created")
public void distributionSetCreatedEventIsPublished() throws InterruptedException {
final DistributionSet generateDistributionSet = entityFactory.generateDistributionSet();
generateDistributionSet.setName("dsEventTest");
generateDistributionSet.setVersion("1");
final DistributionSet createDistributionSet = distributionSetManagement
.createDistributionSet(generateDistributionSet);
final DistributionCreatedEvent dsCreatedEvent = eventListener.waitForEvent(DistributionCreatedEvent.class, 1,
TimeUnit.SECONDS);
assertThat(dsCreatedEvent).isNotNull();
assertThat(dsCreatedEvent.getEntity().getId()).isEqualTo(createDistributionSet.getId());
}
@Test
@Description("Verifies that the distribution set deleted event is published when a distribution set has been deleted")
public void distributionSetDeletedEventIsPublished() throws InterruptedException {
final DistributionSet generateDistributionSet = entityFactory.generateDistributionSet();
generateDistributionSet.setName("dsEventTest");
generateDistributionSet.setVersion("1");
final DistributionSet createDistributionSet = distributionSetManagement
.createDistributionSet(generateDistributionSet);
distributionSetManagement.deleteDistributionSet(createDistributionSet);
final DistributionDeletedEvent dsDeletedEvent = eventListener.waitForEvent(DistributionDeletedEvent.class, 1,
TimeUnit.SECONDS);
assertThat(dsDeletedEvent).isNotNull();
assertThat(dsDeletedEvent.getDistributionSetId()).isEqualTo(createDistributionSet.getId());
}
private class MyEventListener {
private final BlockingQueue<Event> queue = new LinkedBlockingQueue<>();
@Subscribe
public void onEvent(final Event event) {
queue.offer(event);
}
public <T> T waitForEvent(final Class<T> eventType, final long timeout, final TimeUnit timeUnit)
throws InterruptedException {
Event event = null;
while ((event = queue.poll(timeout, timeUnit)) != null) {
if (event.getClass().isAssignableFrom(eventType)) {
return (T) event;
}
}
Assertions.fail("Missing event " + eventType + " within timeout " + timeout + " " + timeUnit);
return null;
}
}
}

View File

@@ -12,25 +12,29 @@ import java.util.HashSet;
import java.util.Set;
import org.eclipse.hawkbit.eventbus.event.Event;
import org.eclipse.hawkbit.eventbus.event.TargetDeletedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.DistributionCreatedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.DistributionDeletedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.DistributionSetTagCreatedBulkEvent;
import org.eclipse.hawkbit.repository.eventbus.event.DistributionSetTagDeletedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.DistributionSetTagUpdateEvent;
import org.eclipse.hawkbit.repository.eventbus.event.DistributionSetUpdateEvent;
import org.eclipse.hawkbit.repository.eventbus.event.RolloutChangeEvent;
import org.eclipse.hawkbit.repository.eventbus.event.RolloutGroupChangeEvent;
import org.eclipse.hawkbit.repository.eventbus.event.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.TargetDeletedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.TargetInfoUpdateEvent;
import org.eclipse.hawkbit.repository.eventbus.event.TargetTagCreatedBulkEvent;
import org.eclipse.hawkbit.repository.eventbus.event.TargetTagDeletedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.TargetTagUpdateEvent;
import org.eclipse.hawkbit.repository.eventbus.event.TargetUpdatedEvent;
/**
* The default hawkbit event provider.
*/
public class HawkbitEventProvider implements UIEventProvider {
private static final Set<Class<? extends Event>> SINGLE_EVENTS = new HashSet<>(6);
private static final Set<Class<? extends Event>> BULK_EVENTS = new HashSet<>(3);
private static final Set<Class<? extends Event>> SINGLE_EVENTS = new HashSet<>(9);
private static final Set<Class<? extends Event>> BULK_EVENTS = new HashSet<>(5);
static {
SINGLE_EVENTS.add(TargetTagCreatedBulkEvent.class);
@@ -41,10 +45,14 @@ public class HawkbitEventProvider implements UIEventProvider {
SINGLE_EVENTS.add(RolloutGroupChangeEvent.class);
SINGLE_EVENTS.add(RolloutChangeEvent.class);
SINGLE_EVENTS.add(TargetTagUpdateEvent.class);
SINGLE_EVENTS.add(DistributionSetUpdateEvent.class);
BULK_EVENTS.add(TargetCreatedEvent.class);
BULK_EVENTS.add(TargetInfoUpdateEvent.class);
BULK_EVENTS.add(TargetDeletedEvent.class);
BULK_EVENTS.add(DistributionDeletedEvent.class);
BULK_EVENTS.add(DistributionCreatedEvent.class);
BULK_EVENTS.add(TargetUpdatedEvent.class);
}
@Override

View File

@@ -222,7 +222,6 @@ public abstract class AbstractTableDetailsLayout<T extends NamedEntity> extends
populateLog();
populateDescription();
populateDetailsWidget();
populateMetadataDetails();
}
protected void populateLog() {

View File

@@ -40,18 +40,17 @@ import com.vaadin.ui.themes.ValoTheme;
@SpringComponent
@VaadinSessionScope
public class DistributionSetMetadatadetailslayout extends Table{
public class DistributionSetMetadatadetailslayout extends Table {
private static final long serialVersionUID = 2913758299611837718L;
private DistributionSetManagement distributionSetManagement;
private DsMetadataPopupLayout dsMetadataPopupLayout;
private static final String METADATA_KEY = "Key";
private static final String VIEW ="view";
private static final String VIEW = "view";
private SpPermissionChecker permissionChecker;
@@ -62,16 +61,16 @@ public class DistributionSetMetadatadetailslayout extends Table{
private Long selectedDistSetId;
/**
*
* Initialize the component.
* @param i18n
* @param permissionChecker
* @param distributionSetManagement
* @param dsMetadataPopupLayout
* @param entityFactory
*/
public void init(final I18N i18n, final SpPermissionChecker permissionChecker,
final DistributionSetManagement distributionSetManagement,
final DsMetadataPopupLayout dsMetadataPopupLayout,
final EntityFactory entityFactory) {
final DsMetadataPopupLayout dsMetadataPopupLayout, final EntityFactory entityFactory) {
this.i18n = i18n;
this.permissionChecker = permissionChecker;
this.distributionSetManagement = distributionSetManagement;
@@ -81,7 +80,6 @@ public class DistributionSetMetadatadetailslayout extends Table{
addCustomGeneratedColumns();
}
/**
* Populate software module metadata.
*
@@ -93,34 +91,14 @@ public class DistributionSetMetadatadetailslayout extends Table{
return;
}
selectedDistSetId = distributionSet.getId();
final List<DistributionSetMetadata> dsMetadataList = distributionSet.getMetadata();
final List<DistributionSetMetadata> dsMetadataList = distributionSetManagement
.findDistributionSetMetadataByDistributionSetId(selectedDistSetId);
if (null != dsMetadataList && !dsMetadataList.isEmpty()) {
dsMetadataList.forEach(dsMetadata -> setDSMetadataProperties(dsMetadata));
}
}
/**
* Create metadata .
*
* @param metadataKeyName
*/
public void createMetadata(final String metadataKeyName){
final IndexedContainer metadataContainer = (IndexedContainer) getContainerDataSource();
final Item item = metadataContainer.addItem(metadataKeyName);
item.getItemProperty(METADATA_KEY).setValue(metadataKeyName);
}
/**
* Delete metadata.
*
* @param metadataKeyName
*/
public void deleteMetadata(final String metadataKeyName){
final IndexedContainer metadataContainer = (IndexedContainer) getContainerDataSource();
metadataContainer.removeItem(metadataKeyName);
}
private void createDSMetadataTable() {
addStyleName(ValoTheme.TABLE_NO_HORIZONTAL_LINES);
addStyleName(ValoTheme.TABLE_NO_STRIPES);
@@ -132,8 +110,8 @@ public class DistributionSetMetadatadetailslayout extends Table{
setColumnHeaderMode(ColumnHeaderMode.EXPLICIT);
addDSMetadataTableHeader();
setSizeFull();
//same as height of other tabs in details tabsheet
setHeight(116,Unit.PIXELS);
// same as height of other tabs in details tabsheet
setHeight(116, Unit.PIXELS);
}
private IndexedContainer getDistSetContainer() {
@@ -154,21 +132,19 @@ public class DistributionSetMetadatadetailslayout extends Table{
setColumnHeader(METADATA_KEY, i18n.get("header.key"));
}
private void setDSMetadataProperties(final DistributionSetMetadata dsMetadata){
private void setDSMetadataProperties(final DistributionSetMetadata dsMetadata) {
final Item item = getContainerDataSource().addItem(dsMetadata.getKey());
item.getItemProperty(METADATA_KEY).setValue(dsMetadata.getKey());
}
private void addCustomGeneratedColumns() {
addGeneratedColumn(METADATA_KEY,
(source, itemId, columnId) -> customMetadataDetailButton((String) itemId));
addGeneratedColumn(METADATA_KEY, (source, itemId, columnId) -> customMetadataDetailButton((String) itemId));
}
private Button customMetadataDetailButton(final String metadataKey) {
final Button viewIcon = SPUIComponentProvider.getButton(getDetailLinkId(metadataKey), metadataKey, "View "
+ metadataKey + " Metadata details", null, false, null, SPUIButtonStyleSmallNoBorder.class);
final Button viewIcon = SPUIComponentProvider.getButton(getDetailLinkId(metadataKey), metadataKey,
"View " + metadataKey + " Metadata details", null, false, null, SPUIButtonStyleSmallNoBorder.class);
viewIcon.setData(metadataKey);
viewIcon.addStyleName(ValoTheme.BUTTON_TINY + " " + ValoTheme.BUTTON_LINK + " " + "on-focus-no-border link"
+ " " + "text-style");
@@ -177,16 +153,15 @@ public class DistributionSetMetadatadetailslayout extends Table{
}
private static String getDetailLinkId(final String name) {
return new StringBuilder(SPUIComponentIdProvider.DS_METADATA_DETAIL_LINK).append('.').append(name)
.toString();
return new StringBuilder(SPUIComponentIdProvider.DS_METADATA_DETAIL_LINK).append('.').append(name).toString();
}
private void showMetadataDetails(final Long selectedDistSetId , final String metadataKey) {
DistributionSet distSet = distributionSetManagement.findDistributionSetById(selectedDistSetId);
private void showMetadataDetails(final Long selectedDistSetId, final String metadataKey) {
final DistributionSet distSet = distributionSetManagement.findDistributionSetById(selectedDistSetId);
/* display the window */
UI.getCurrent().addWindow(dsMetadataPopupLayout.getWindow(distSet,
entityFactory.generateDistributionSetMetadata(distSet, metadataKey, "") ));
entityFactory.generateDistributionSetMetadata(distSet, metadataKey, "")));
}
}

View File

@@ -94,7 +94,7 @@ public class SoftwareModuleMetadatadetailslayout extends Table {
return;
}
selectedSWModuleId = swModule.getId();
final List<SoftwareModuleMetadata> swMetadataList = swModule.getMetadata();
final List<SoftwareModuleMetadata> swMetadataList = softwareManagement.findSoftwareModuleMetadataBySoftwareModuleId(selectedSWModuleId);
if (null != swMetadataList && !swMetadataList.isEmpty()) {
swMetadataList.forEach(swMetadata -> setSWMetadataProperties(swMetadata));
}
@@ -132,8 +132,8 @@ public class SoftwareModuleMetadatadetailslayout extends Table {
setColumnHeaderMode(ColumnHeaderMode.EXPLICIT);
addSMMetadataTableHeader();
setSizeFull();
// same as height of other tabs in details tabsheet
setHeight(116, Unit.PIXELS);
//same as height of other tabs in details tabsheet
setHeight(116,Unit.PIXELS);
}
private IndexedContainer getSwModuleMetadataContainer() {
@@ -147,6 +147,7 @@ public class SoftwareModuleMetadatadetailslayout extends Table {
setColumnHeader(METADATA_KEY, i18n.get("header.key"));
}
private void setSWMetadataProperties(final SoftwareModuleMetadata swMetadata) {
final Item item = getContainerDataSource().addItem(swMetadata.getKey());
item.getItemProperty(METADATA_KEY).setValue(swMetadata.getKey());
@@ -157,8 +158,8 @@ public class SoftwareModuleMetadatadetailslayout extends Table {
}
private Button customMetadataDetailButton(final String metadataKey) {
final Button viewLink = SPUIComponentProvider.getButton(getDetailLinkId(metadataKey), metadataKey,
"View" + metadataKey + " Metadata details", null, false, null, SPUIButtonStyleSmallNoBorder.class);
final Button viewLink = SPUIComponentProvider.getButton(getDetailLinkId(metadataKey), metadataKey, "View"
+ metadataKey + " Metadata details", null, false, null, SPUIButtonStyleSmallNoBorder.class);
viewLink.setData(metadataKey);
if (permissionChecker.hasUpdateDistributionPermission()) {
viewLink.addStyleName(ValoTheme.BUTTON_TINY + " " + ValoTheme.BUTTON_LINK + " " + "on-focus-no-border link"
@@ -175,7 +176,8 @@ public class SoftwareModuleMetadatadetailslayout extends Table {
private void showMetadataDetails(final Long selectedSWModuleId, final String metadataKey) {
final SoftwareModule swmodule = softwareManagement.findSoftwareModuleById(selectedSWModuleId);
/* display the window */
UI.getCurrent().addWindow(swMetadataPopupLayout.getWindow(swmodule,
UI.getCurrent().addWindow(
swMetadataPopupLayout.getWindow(swmodule,
entityFactory.generateSoftwareModuleMetadata(swmodule, metadataKey, "")));
}

View File

@@ -17,7 +17,6 @@ import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleIdName;
import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleEvent;
@@ -29,7 +28,6 @@ import org.eclipse.hawkbit.ui.common.detailslayout.SoftwareModuleDetailsTable;
import org.eclipse.hawkbit.ui.common.tagdetails.DistributionTagToken;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmallNoBorder;
import org.eclipse.hawkbit.ui.distributions.event.MetadataEvent;
import org.eclipse.hawkbit.ui.distributions.event.SaveActionWindowEvent;
import org.eclipse.hawkbit.ui.distributions.event.SoftwareModuleAssignmentDiscardEvent;
import org.eclipse.hawkbit.ui.distributions.state.ManageDistUIState;
@@ -97,21 +95,6 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet
private final Map<String, StringBuilder> assignedSWModule = new HashMap<>();
@EventBusListenerMethod(scope = EventScope.SESSION)
void onEvent(final MetadataEvent event) {
UI.getCurrent().access(() -> {
final DistributionSetMetadata dsMetadata = event.getDistributionSetMetadata();
if (dsMetadata != null && isDistributionSetSelected(dsMetadata.getDistributionSet())) {
if (event.getMetadataUIEvent() == MetadataEvent.MetadataUIEvent.CREATE_DISTRIBUTION_SET_METADATA) {
dsMetadataTable.createMetadata(event.getDistributionSetMetadata().getKey());
} else if (event
.getMetadataUIEvent() == MetadataEvent.MetadataUIEvent.DELETE_DISTRIBUTION_SET_METADATA) {
dsMetadataTable.deleteMetadata(event.getDistributionSetMetadata().getKey());
}
}
});
}
/**
* softwareLayout Initialize the component.
*/

View File

@@ -21,6 +21,9 @@ import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.SoftwareManagement;
import org.eclipse.hawkbit.repository.SpPermissionChecker;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.eventbus.event.DistributionCreatedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.DistributionDeletedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.DistributionSetUpdateEvent;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleIdName;
@@ -124,6 +127,33 @@ public class DistributionSetTable extends AbstractNamedVersionTable<Distribution
}
}
@EventBusListenerMethod(scope = EventScope.SESSION)
void onEvents(final DistributionSetUpdateEvent event) {
final DistributionSet ds = event.getEntity();
final DistributionSetIdName lastSelectedDsIdName = manageDistUIState.getLastSelectedDistribution().isPresent()
? manageDistUIState.getLastSelectedDistribution().get() : null;
final List<DistributionSetIdName> visibleItemIds = (List<DistributionSetIdName>) getVisibleItemIds();
// refresh the details tabs only if selected ds is updated
if (lastSelectedDsIdName != null && lastSelectedDsIdName.getId().equals(ds.getId())) {
// update table row+details layout
eventBus.publish(this, new DistributionTableEvent(BaseEntityEventType.SELECTED_ENTITY, ds));
} else if (visibleItemIds.stream().filter(e -> e.getId().equals(ds.getId())).findFirst().isPresent()) {
// update the name/version details visible in table
UI.getCurrent().access(() -> updateDistributionInTable(event.getEntity()));
}
}
@EventBusListenerMethod(scope = EventScope.SESSION)
void onEvents(final List<?> events) {
final Object firstEvent = events.get(0);
if (DistributionCreatedEvent.class.isInstance(firstEvent)) {
refreshDistributions();
} else if (DistributionDeletedEvent.class.isInstance(firstEvent)) {
onDistributionDeleteEvent((List<DistributionDeletedEvent>) events);
}
}
@Override
protected String getTableId() {
return SPUIComponentIdProvider.DIST_TABLE_ID;
@@ -189,9 +219,9 @@ public class DistributionSetTable extends AbstractNamedVersionTable<Distribution
protected void publishEntityAfterValueChange(final DistributionSet distributionSet) {
eventBus.publish(this, new DistributionTableEvent(BaseEntityEventType.SELECTED_ENTITY, distributionSet));
eventBus.publish(this, DistributionsUIEvent.ORDER_BY_DISTRIBUTION);
if(distributionSet!=null){
if (distributionSet != null) {
manageDistUIState.setLastSelectedEntity(new DistributionSetIdName(distributionSet.getId(),
distributionSet.getName(),distributionSet.getVersion()));
distributionSet.getName(), distributionSet.getVersion()));
}
}
@@ -432,6 +462,10 @@ public class DistributionSetTable extends AbstractNamedVersionTable<Distribution
@EventBusListenerMethod(scope = EventScope.SESSION)
void onEvent(final DistributionTableEvent event) {
onBaseEntityEvent(event);
if (BaseEntityEventType.UPDATED_ENTITY != event.getEventType()) {
return;
}
UI.getCurrent().access(() -> updateDistributionInTable(event.getEntity()));
}
@EventBusListenerMethod(scope = EventScope.SESSION)
@@ -477,6 +511,7 @@ public class DistributionSetTable extends AbstractNamedVersionTable<Distribution
@Override
protected void setDataAvailable(final boolean available) {
manageDistUIState.setNoDataAvailableDist(!available);
}
@Override
@@ -488,13 +523,13 @@ public class DistributionSetTable extends AbstractNamedVersionTable<Distribution
public Object generateCell(final Table source, final Object itemId, final Object columnId) {
final String nameVersionStr = getNameAndVerion(itemId);
final Button manageMetaDataBtn = createManageMetadataButton(nameVersionStr);
manageMetaDataBtn.addClickListener(event -> showMetadataDetails(((DistributionSetIdName) itemId).getId()));
manageMetaDataBtn
.addClickListener(event -> showMetadataDetails(((DistributionSetIdName) itemId).getId()));
return manageMetaDataBtn;
}
});
}
@Override
protected List<TableColumn> getTableVisibleColumns() {
final List<TableColumn> columnList = super.getTableVisibleColumns();
@@ -525,4 +560,73 @@ public class DistributionSetTable extends AbstractNamedVersionTable<Distribution
return name + "." + version;
}
private void refreshDistributions() {
final LazyQueryContainer dsContainer = (LazyQueryContainer) getContainerDataSource();
final int size = dsContainer.size();
if (size < SPUIDefinitions.MAX_TABLE_ENTRIES) {
refreshTablecontainer();
}
if (size != 0) {
setData(SPUIDefinitions.DATA_AVAILABLE);
}
}
private void refreshTablecontainer() {
final LazyQueryContainer dsContainer = (LazyQueryContainer) getContainerDataSource();
dsContainer.refresh();
selectRow();
}
private void updateDistributionInTable(final DistributionSet editedDs) {
final Item item = getContainerDataSource()
.getItem(new DistributionSetIdName(editedDs.getId(), editedDs.getName(), editedDs.getVersion()));
updateEntity(editedDs, item);
}
private void onDistributionDeleteEvent(final List<DistributionDeletedEvent> events) {
final LazyQueryContainer dsContainer = (LazyQueryContainer) getContainerDataSource();
final List<Object> visibleItemIds = (List<Object>) getVisibleItemIds();
boolean shouldRefreshDs = false;
for (final DistributionDeletedEvent deletedEvent : events) {
final Long distributionSetId = deletedEvent.getDistributionSetId();
final DistributionSetIdName targetIdName = new DistributionSetIdName(distributionSetId, null, null);
if (visibleItemIds.contains(targetIdName)) {
dsContainer.removeItem(targetIdName);
} else {
shouldRefreshDs = true;
}
}
if (shouldRefreshDs) {
refreshOnDelete();
} else {
dsContainer.commit();
}
reSelectItemsAfterDeletionEvent();
}
private void refreshOnDelete() {
final LazyQueryContainer dsContainer = (LazyQueryContainer) getContainerDataSource();
final int size = dsContainer.size();
refreshTablecontainer();
if (size != 0) {
setData(SPUIDefinitions.DATA_AVAILABLE);
}
}
private void reSelectItemsAfterDeletionEvent() {
Set<Object> values = new HashSet<>();
if (isMultiSelect()) {
values = new HashSet<>((Set<?>) getValue());
} else {
values.add(getValue());
}
setValue(null);
for (final Object value : values) {
if (getVisibleItemIds().contains(value)) {
select(value);
}
}
}
}

View File

@@ -16,8 +16,6 @@ import org.eclipse.hawkbit.repository.SpPermissionChecker;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.ui.common.AbstractMetadataPopupLayout;
import org.eclipse.hawkbit.ui.distributions.event.MetadataEvent;
import org.eclipse.hawkbit.ui.distributions.event.MetadataEvent.MetadataUIEvent;
import org.springframework.beans.factory.annotation.Autowired;
import com.vaadin.spring.annotation.SpringComponent;
@@ -55,7 +53,6 @@ public class DsMetadataPopupLayout extends AbstractMetadataPopupLayout<Distribut
final DistributionSetMetadata dsMetaData = distributionSetManagement
.createDistributionSetMetadata(entityFactory.generateDistributionSetMetadata(entity, key, value));
setSelectedEntity(dsMetaData.getDistributionSet());
eventBus.publish(this, new MetadataEvent(MetadataUIEvent.CREATE_DISTRIBUTION_SET_METADATA, dsMetaData));
return dsMetaData;
}
@@ -84,7 +81,6 @@ public class DsMetadataPopupLayout extends AbstractMetadataPopupLayout<Distribut
protected void deleteMetadata(final DistributionSet entity, final String key, final String value) {
final DistributionSetMetadata dsMetaData = entityFactory.generateDistributionSetMetadata(entity, key, value);
distributionSetManagement.deleteDistributionSetMetadata(entity, key);
eventBus.publish(this, new MetadataEvent(MetadataUIEvent.DELETE_DISTRIBUTION_SET_METADATA, dsMetaData));
}
@Override

View File

@@ -42,7 +42,7 @@ import com.google.common.base.Strings;
public class ManageDistBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
private static final long serialVersionUID = 5176481314404662215L;
private Sort sort = new Sort(Direction.ASC, "name", "version");
private Sort sort = new Sort(Direction.ASC, "createdAt");
private String searchText = null;
private transient DistributionSetManagement distributionSetManagement;
private transient Page<DistributionSet> firstPageDistributionSets = null;

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.ui.distributions.event;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
/**
*
* Metadata Events.
@@ -18,21 +19,21 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
public class MetadataEvent {
public enum MetadataUIEvent {
CREATE_DISTRIBUTION_SET_METADATA, DELETE_DISTRIBUTION_SET_METADATA, DELETE_SOFTWARE_MODULE_METADATA, CREATE_SOFTWARE_MODULE_METADATA;
DELETE_SOFTWARE_MODULE_METADATA, CREATE_SOFTWARE_MODULE_METADATA;
}
private MetadataUIEvent metadataUIEvent;
private final MetadataUIEvent metadataUIEvent;
private DistributionSetMetadata distributionSetMetadata;
private SoftwareModuleMetadata softwareModuleMetadata;
public MetadataEvent(MetadataUIEvent metadataUIEvent, final DistributionSetMetadata distributionSetMetadata) {
public MetadataEvent(final MetadataUIEvent metadataUIEvent, final DistributionSetMetadata distributionSetMetadata) {
this.metadataUIEvent = metadataUIEvent;
this.distributionSetMetadata = distributionSetMetadata;
}
public MetadataEvent(MetadataUIEvent metadataUIEvent, final SoftwareModuleMetadata softwareModuleMetadata) {
public MetadataEvent(final MetadataUIEvent metadataUIEvent, final SoftwareModuleMetadata softwareModuleMetadata) {
this.metadataUIEvent = metadataUIEvent;
this.softwareModuleMetadata = softwareModuleMetadata;
}

View File

@@ -155,9 +155,9 @@ public class CustomTargetBeanQuery extends AbstractBeanQuery<ProxyTarget> {
size = getTargetManagement().countTargetByTargetFilterQuery(filterQuery);
}
getFilterManagementUIState().setTargetsCountAll(size);
if (size > SPUIDefinitions.MAX_TARGET_TABLE_ENTRIES) {
getFilterManagementUIState().setTargetsTruncated(size - SPUIDefinitions.MAX_TARGET_TABLE_ENTRIES);
size = SPUIDefinitions.MAX_TARGET_TABLE_ENTRIES;
if (size > SPUIDefinitions.MAX_TABLE_ENTRIES) {
getFilterManagementUIState().setTargetsTruncated(size - SPUIDefinitions.MAX_TABLE_ENTRIES);
size = SPUIDefinitions.MAX_TABLE_ENTRIES;
} else {
getFilterManagementUIState().setTargetsTruncated(null);
}

View File

@@ -93,7 +93,7 @@ public class TargetFilterCountMessageLabel extends Label {
// set the icon
setIcon(FontAwesome.INFO_CIRCLE);
setDescription(i18n.get("label.target.filter.truncated", filterManagementUIState.getTargetsTruncated(),
SPUIDefinitions.MAX_TARGET_TABLE_ENTRIES));
SPUIDefinitions.MAX_TABLE_ENTRIES));
} else {
setIcon(null);
@@ -102,8 +102,8 @@ public class TargetFilterCountMessageLabel extends Label {
targetMessage.append(totalTargets);
targetMessage.append(HawkbitCommonUtil.SP_STRING_SPACE);
targetMessage.append(i18n.get("label.filter.shown"));
if (totalTargets > SPUIDefinitions.MAX_TARGET_TABLE_ENTRIES) {
targetMessage.append(SPUIDefinitions.MAX_TARGET_TABLE_ENTRIES);
if (totalTargets > SPUIDefinitions.MAX_TABLE_ENTRIES) {
targetMessage.append(SPUIDefinitions.MAX_TABLE_ENTRIES);
} else {
targetMessage.append(HawkbitCommonUtil.SP_STRING_SPACE);
targetMessage.append(totalTargets);

View File

@@ -9,7 +9,9 @@
package org.eclipse.hawkbit.ui.management.dstable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct;
@@ -21,10 +23,12 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
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;
@@ -33,6 +37,7 @@ 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.SpringContextHelper;
import org.eclipse.hawkbit.ui.utils.UINotification;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -237,7 +242,10 @@ public class DistributionAddUpdateWindowLayout extends CustomComponent {
notificationMessage.displaySuccess(
i18n.get("message.new.dist.save.success", new Object[] { newDist.getName(), newDist.getVersion() }));
eventBus.publish(this, new DistributionTableEvent(BaseEntityEventType.NEW_ENTITY, newDist));
final Set<DistributionSetIdName> s = new HashSet<>();
s.add(new DistributionSetIdName(newDist.getId(), newDist.getName(), newDist.getVersion()));
final DistributionSetTable distributionSetTable = SpringContextHelper.getBean(DistributionSetTable.class);
distributionSetTable.setValue(s);
}
/**

View File

@@ -41,7 +41,7 @@ import com.google.common.base.Strings;
public class DistributionBeanQuery extends AbstractBeanQuery<ProxyDistribution> {
private static final long serialVersionUID = 5862679853949173536L;
private Sort sort = new Sort(Direction.ASC, "name", "version");
private Sort sort = new Sort(Direction.ASC, "createdAt");
private Collection<String> distributionTags;
private String searchText;
private String pinnedControllerId;

View File

@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.ui.management.dstable;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.ui.common.DistributionSetIdName;
import org.eclipse.hawkbit.ui.common.detailslayout.AbstractNamedVersionedEntityTableDetailsLayout;
import org.eclipse.hawkbit.ui.common.detailslayout.DistributionSetMetadatadetailslayout;
@@ -19,7 +18,6 @@ import org.eclipse.hawkbit.ui.common.detailslayout.SoftwareModuleDetailsTable;
import org.eclipse.hawkbit.ui.common.tagdetails.DistributionTagToken;
import org.eclipse.hawkbit.ui.components.SPUIComponentProvider;
import org.eclipse.hawkbit.ui.distributions.dstable.DsMetadataPopupLayout;
import org.eclipse.hawkbit.ui.distributions.event.MetadataEvent;
import org.eclipse.hawkbit.ui.management.event.DistributionTableEvent;
import org.eclipse.hawkbit.ui.management.state.ManagementUIState;
import org.eclipse.hawkbit.ui.utils.SPUIComponentIdProvider;
@@ -79,21 +77,6 @@ public class DistributionDetails extends AbstractNamedVersionedEntityTableDetail
super.init();
}
@EventBusListenerMethod(scope = EventScope.SESSION)
void onEvent(final MetadataEvent event) {
UI.getCurrent().access(() -> {
final DistributionSetMetadata dsMetadata = event.getDistributionSetMetadata();
if (dsMetadata != null && isDistributionSetSelected(dsMetadata.getDistributionSet())) {
if (event.getMetadataUIEvent() == MetadataEvent.MetadataUIEvent.CREATE_DISTRIBUTION_SET_METADATA) {
dsMetadataTable.createMetadata(event.getDistributionSetMetadata().getKey());
} else if (event
.getMetadataUIEvent() == MetadataEvent.MetadataUIEvent.DELETE_DISTRIBUTION_SET_METADATA) {
dsMetadataTable.deleteMetadata(event.getDistributionSetMetadata().getKey());
}
}
});
}
@EventBusListenerMethod(scope = EventScope.SESSION)
void onEvent(final DistributionTableEvent distributionTableEvent) {
onBaseEntityEvent(distributionTableEvent);
@@ -210,18 +193,18 @@ public class DistributionDetails extends AbstractNamedVersionedEntityTableDetail
return true;
}
private boolean isDistributionSetSelected(final DistributionSet ds) {
final DistributionSetIdName lastselectedManageDS = managementUIState.getLastSelectedDistribution().isPresent() ? managementUIState
.getLastSelectedDistribution().get() : null;
return ds!=null && lastselectedManageDS != null && lastselectedManageDS.getName().equals(ds.getName())
final DistributionSetIdName lastselectedManageDS = managementUIState.getLastSelectedDistribution().isPresent()
? managementUIState.getLastSelectedDistribution().get() : null;
return ds != null && lastselectedManageDS != null && lastselectedManageDS.getName().equals(ds.getName())
&& lastselectedManageDS.getVersion().endsWith(ds.getVersion());
}
@Override
protected void showMetadata(final ClickEvent event) {
final DistributionSet ds = distributionSetManagement.findDistributionSetByIdWithDetails(getSelectedBaseEntityId());
UI.getCurrent().addWindow(dsMetadataPopupLayout.getWindow(ds,null));
final DistributionSet ds = distributionSetManagement
.findDistributionSetByIdWithDetails(getSelectedBaseEntityId());
UI.getCurrent().addWindow(dsMetadataPopupLayout.getWindow(ds, null));
}
}

View File

@@ -18,8 +18,12 @@ import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.SpPermissionChecker;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.eventbus.event.DistributionCreatedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.DistributionDeletedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.DistributionSetUpdateEvent;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTagAssignmentResult;
import org.eclipse.hawkbit.repository.model.Target;
@@ -99,6 +103,9 @@ public class DistributionTable extends AbstractNamedVersionTable<DistributionSet
@Autowired
private transient DistributionSetManagement distributionSetManagement;
@Autowired
private transient EntityFactory entityFactory;
private String notAllowedMsg;
private Boolean isDistPinned = false;
@@ -111,6 +118,47 @@ public class DistributionTable extends AbstractNamedVersionTable<DistributionSet
notAllowedMsg = i18n.get("message.action.not.allowed");
}
@EventBusListenerMethod(scope = EventScope.SESSION)
void onEvents(final List<?> events) {
final Object firstEvent = events.get(0);
if (DistributionDeletedEvent.class.isInstance(firstEvent)) {
onDistributionDeleteEvent((List<DistributionDeletedEvent>) events);
} else if (DistributionCreatedEvent.class.isInstance(firstEvent)
&& ((DistributionCreatedEvent) firstEvent).getEntity().isComplete()) {
refreshDistributions();
}
}
@EventBusListenerMethod(scope = EventScope.SESSION)
void onEvents(final DistributionSetUpdateEvent event) {
final DistributionSet ds = event.getEntity();
final List<DistributionSetIdName> visibleItemIds = (List<DistributionSetIdName>) getVisibleItemIds();
final Boolean dsVisible = visibleItemIds.stream().filter(e -> e.getId().equals(ds.getId())).findFirst()
.isPresent();
if ((ds.isComplete() && !dsVisible)) {
refreshDistributions();
} else if ((!ds.isComplete() && dsVisible)) {
refreshDistributions();
if (ds.getId().equals(managementUIState.getLastSelectedDsIdName().getId())) {
managementUIState.setLastSelectedDistribution(null);
managementUIState.setLastSelectedEntity(null);
}
} else if (dsVisible) {
UI.getCurrent().access(() -> updateDistributionInTable(event.getEntity()));
}
final DistributionSetIdName lastSelectedDsIdName = managementUIState.getLastSelectedDsIdName();
// refresh the details tabs only if selected ds is updated
if (lastSelectedDsIdName != null && lastSelectedDsIdName.getId().equals(ds.getId())) {
// update table row+details layout
eventBus.publish(this, new DistributionTableEvent(BaseEntityEventType.SELECTED_ENTITY, ds));
}
}
/**
* DistributionTableFilterEvent.
*
@@ -699,4 +747,67 @@ public class DistributionTable extends AbstractNamedVersionTable<DistributionSet
UI.getCurrent().addWindow(dsMetadataPopupLayout.getWindow(ds, null));
}
private void onDistributionDeleteEvent(final List<DistributionDeletedEvent> events) {
final LazyQueryContainer dsContainer = (LazyQueryContainer) getContainerDataSource();
final List<Object> visibleItemIds = (List<Object>) getVisibleItemIds();
boolean shouldRefreshDs = false;
for (final DistributionDeletedEvent deletedEvent : events) {
final Long distributionSetId = deletedEvent.getDistributionSetId();
final DistributionSetIdName targetIdName = new DistributionSetIdName(distributionSetId, null, null);
if (visibleItemIds.contains(targetIdName)) {
dsContainer.removeItem(targetIdName);
} else {
shouldRefreshDs = true;
}
}
if (shouldRefreshDs) {
refreshOnDelete();
} else {
dsContainer.commit();
}
reSelectItemsAfterDeletionEvent();
}
private void reSelectItemsAfterDeletionEvent() {
Set<Object> values = new HashSet<>();
if (isMultiSelect()) {
values = new HashSet<>((Set<?>) getValue());
} else {
values.add(getValue());
}
setValue(null);
for (final Object value : values) {
if (getVisibleItemIds().contains(value)) {
select(value);
}
}
}
private void refreshDistributions() {
final LazyQueryContainer dsContainer = (LazyQueryContainer) getContainerDataSource();
final int size = dsContainer.size();
if (size < SPUIDefinitions.MAX_TABLE_ENTRIES) {
refreshTablecontainer();
}
if (size != 0) {
setData(SPUIDefinitions.DATA_AVAILABLE);
}
}
private void refreshOnDelete() {
final LazyQueryContainer dsContainer = (LazyQueryContainer) getContainerDataSource();
final int size = dsContainer.size();
refreshTablecontainer();
if (size != 0) {
setData(SPUIDefinitions.DATA_AVAILABLE);
}
}
private void refreshTablecontainer() {
final LazyQueryContainer dsContainer = (LazyQueryContainer) getContainerDataSource();
dsContainer.refresh();
selectRow();
}
}

View File

@@ -174,7 +174,7 @@ public class CountMessageLabel extends Label {
// set the icon
setIcon(FontAwesome.INFO_CIRCLE);
setDescription(i18n.get("label.target.filter.truncated", managementUIState.getTargetsTruncated(),
SPUIDefinitions.MAX_TARGET_TABLE_ENTRIES));
SPUIDefinitions.MAX_TABLE_ENTRIES));
totalTargetTableEnteries += managementUIState.getTargetsTruncated();
} else {
setIcon(null);
@@ -184,9 +184,9 @@ public class CountMessageLabel extends Label {
final StringBuilder message = new StringBuilder(i18n.get("label.target.filter.count"));
message.append(managementUIState.getTargetsCountAll());
message.append(HawkbitCommonUtil.SP_STRING_SPACE);
if (totalTargetTableEnteries > SPUIDefinitions.MAX_TARGET_TABLE_ENTRIES) {
if (totalTargetTableEnteries > SPUIDefinitions.MAX_TABLE_ENTRIES) {
message.append(i18n.get("label.filter.shown"));
message.append(SPUIDefinitions.MAX_TARGET_TABLE_ENTRIES);
message.append(SPUIDefinitions.MAX_TABLE_ENTRIES);
} else {
if (!targFilParams.hasFilter()) {
message.append(i18n.get("label.filter.shown"));

View File

@@ -387,7 +387,6 @@ public class ManangementConfirmationWindowLayout extends AbstractConfirmationWin
managementUIState.getTargetTableFilters().getPinnedDistId()
.ifPresent(distId -> unPinDeletedDS(deletedIds, distId));
eventBus.publish(this, SaveActionWindowEvent.DELETED_DISTRIBUTIONS);
managementUIState.getDeletedDistributionList().clear();
}

View File

@@ -190,9 +190,9 @@ public class TargetBeanQuery extends AbstractBeanQuery<ProxyTarget> {
final ManagementUIState tmpManagementUIState = getManagementUIState();
tmpManagementUIState.setTargetsCountAll(totSize);
if (size > SPUIDefinitions.MAX_TARGET_TABLE_ENTRIES) {
tmpManagementUIState.setTargetsTruncated(size - SPUIDefinitions.MAX_TARGET_TABLE_ENTRIES);
size = SPUIDefinitions.MAX_TARGET_TABLE_ENTRIES;
if (size > SPUIDefinitions.MAX_TABLE_ENTRIES) {
tmpManagementUIState.setTargetsTruncated(size - SPUIDefinitions.MAX_TABLE_ENTRIES);
size = SPUIDefinitions.MAX_TABLE_ENTRIES;
} else {
tmpManagementUIState.setTargetsTruncated(null);
}

View File

@@ -17,11 +17,12 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.eventbus.event.TargetDeletedEvent;
import org.eclipse.hawkbit.repository.SpPermissionChecker;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.eventbus.event.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.TargetDeletedEvent;
import org.eclipse.hawkbit.repository.eventbus.event.TargetInfoUpdateEvent;
import org.eclipse.hawkbit.repository.eventbus.event.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetIdName;
import org.eclipse.hawkbit.repository.model.TargetInfo;
@@ -142,6 +143,8 @@ public class TargetTable extends AbstractTable<Target, TargetIdName> {
onTargetInfoUpdateEvents((List<TargetInfoUpdateEvent>) events);
} else if (TargetDeletedEvent.class.isInstance(firstEvent)) {
onTargetDeletedEvent((List<TargetDeletedEvent>) events);
} else if(TargetUpdatedEvent.class.isInstance(firstEvent)){
onTargetUpdateEvents((List<TargetUpdatedEvent>) events);
}
}
@@ -809,7 +812,7 @@ public class TargetTable extends AbstractTable<Target, TargetIdName> {
private void refreshTargets() {
final LazyQueryContainer targetContainer = (LazyQueryContainer) getContainerDataSource();
final int size = targetContainer.size();
if (size < SPUIDefinitions.MAX_TARGET_TABLE_ENTRIES) {
if (size < SPUIDefinitions.MAX_TABLE_ENTRIES) {
refreshTablecontainer();
} else {
// If table is not refreshed , explicitly target total count and
@@ -829,10 +832,12 @@ public class TargetTable extends AbstractTable<Target, TargetIdName> {
final TargetIdName targetIdName) {
final LazyQueryContainer targetContainer = (LazyQueryContainer) getContainerDataSource();
final Item item = targetContainer.getItem(targetIdName);
item.getItemProperty(SPUILabelDefinitions.VAR_TARGET_STATUS).setValue(targetInfo.getUpdateStatus());
item.getItemProperty(SPUILabelDefinitions.VAR_NAME).setValue(target.getName());
item.getItemProperty(SPUILabelDefinitions.VAR_POLL_STATUS_TOOL_TIP)
.setValue(HawkbitCommonUtil.getPollStatusToolTip(targetInfo.getPollStatus(), i18n));
if (targetInfo != null) {
item.getItemProperty(SPUILabelDefinitions.VAR_POLL_STATUS_TOOL_TIP).setValue(
HawkbitCommonUtil.getPollStatusToolTip(targetInfo.getPollStatus(), i18n));
item.getItemProperty(SPUILabelDefinitions.VAR_TARGET_STATUS).setValue(targetInfo.getUpdateStatus());
}
}
private boolean isLastSelectedTarget(final TargetIdName targetIdName) {
@@ -879,6 +884,35 @@ public class TargetTable extends AbstractTable<Target, TargetIdName> {
}
}
private void onTargetUpdateEvents(List<TargetUpdatedEvent> events) {
final List<Object> visibleItemIds = (List<Object>) getVisibleItemIds();
boolean shoulTargetsUpdated = false;
Target lastSelectedTarget = null;
for (final TargetUpdatedEvent targetUpdatedEvent : events) {
Target target = targetUpdatedEvent.getEntity();
final TargetIdName targetIdName = target.getTargetIdName();
if (Filters.or(getTargetTableFilters(target)).doFilter()) {
shoulTargetsUpdated = true;
} else {
if (visibleItemIds.contains(targetIdName)) {
updateVisibleItemOnEvent(null, target, targetIdName);
}
}
if (isLastSelectedTarget(targetIdName)) {
lastSelectedTarget = target;
}
}
if (shoulTargetsUpdated) {
refreshTargets();
}
if (lastSelectedTarget != null) {
eventBus.publish(this, new TargetTableEvent(BaseEntityEventType.SELECTED_ENTITY, lastSelectedTarget));
}
}
private void onTargetCreatedEvents() {
refreshTargets();
}
@@ -954,8 +988,8 @@ public class TargetTable extends AbstractTable<Target, TargetIdName> {
size = getTargetsCountWithFilter(totalTargetsCount, status, targetTags, distributionId, searchText,
noTagClicked, pinnedDistId);
if (size > SPUIDefinitions.MAX_TARGET_TABLE_ENTRIES) {
managementUIState.setTargetsTruncated(size - SPUIDefinitions.MAX_TARGET_TABLE_ENTRIES);
if (size > SPUIDefinitions.MAX_TABLE_ENTRIES) {
managementUIState.setTargetsTruncated(size - SPUIDefinitions.MAX_TABLE_ENTRIES);
}
}

View File

@@ -140,9 +140,9 @@ public class RolloutGroupTargetsBeanQuery extends AbstractBeanQuery<ProxyTarget>
size = firstPageTargetSets.getTotalElements();
}
getRolloutUIState().setRolloutGroupTargetsTotalCount(size);
if (size > SPUIDefinitions.MAX_TARGET_TABLE_ENTRIES) {
getRolloutUIState().setRolloutGroupTargetsTruncated(size - SPUIDefinitions.MAX_TARGET_TABLE_ENTRIES);
return SPUIDefinitions.MAX_TARGET_TABLE_ENTRIES;
if (size > SPUIDefinitions.MAX_TABLE_ENTRIES) {
getRolloutUIState().setRolloutGroupTargetsTruncated(size - SPUIDefinitions.MAX_TABLE_ENTRIES);
return SPUIDefinitions.MAX_TABLE_ENTRIES;
}
return (int) size;

View File

@@ -94,7 +94,7 @@ public class RolloutGroupTargetsCountLabelMessage extends Label {
// set the icon
setIcon(FontAwesome.INFO_CIRCLE);
setDescription(i18n.get("rollout.group.label.target.truncated",
rolloutUIState.getRolloutGroupTargetsTruncated(), SPUIDefinitions.MAX_TARGET_TABLE_ENTRIES));
rolloutUIState.getRolloutGroupTargetsTruncated(), SPUIDefinitions.MAX_TABLE_ENTRIES));
totalTargetTableEnteries += rolloutUIState.getRolloutGroupTargetsTruncated();
} else {
setIcon(null);
@@ -104,9 +104,9 @@ public class RolloutGroupTargetsCountLabelMessage extends Label {
final StringBuilder message = new StringBuilder(i18n.get("label.target.filter.count"));
message.append(rolloutUIState.getRolloutGroupTargetsTotalCount());
message.append(HawkbitCommonUtil.SP_STRING_SPACE);
if (totalTargetTableEnteries > SPUIDefinitions.MAX_TARGET_TABLE_ENTRIES) {
if (totalTargetTableEnteries > SPUIDefinitions.MAX_TABLE_ENTRIES) {
message.append(i18n.get("label.filter.shown"));
message.append(SPUIDefinitions.MAX_TARGET_TABLE_ENTRIES);
message.append(SPUIDefinitions.MAX_TABLE_ENTRIES);
} else {
message.append(i18n.get("label.filter.shown"));
message.append(rolloutGroupTargetsListGrid.getContainerDataSource().size());

View File

@@ -273,10 +273,6 @@ public final class SPUIDefinitions {
* New Target save icon id.
*/
public static final String NEW_TARGET_SAVE = "target.add.save";
/**
* New Target discard icon id.
*/
// public static final String NEW_TARGET_DISCARD = "target.add.discard";
/**
* New Target add icon id.
*/
@@ -861,7 +857,7 @@ public final class SPUIDefinitions {
* truncates it. This protects to endless scroll to very high page numbers
* which is very in performant.
*/
public static final int MAX_TARGET_TABLE_ENTRIES = 5000;
public static final int MAX_TABLE_ENTRIES = 5000;
/**
* New software module set type add icon id.