Target poll DB performance improvements (#613)
* Async update on last target query. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * 10 seconds Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Completed batch and optimzed schema. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Cleanup and test. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * readibility. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Batch update. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Sonar issue fixed. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
@@ -8,6 +8,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.repository;
|
package org.eclipse.hawkbit.repository;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
|
import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
|
||||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
@@ -34,6 +36,45 @@ public class RepositoryProperties {
|
|||||||
*/
|
*/
|
||||||
private boolean publishTargetPollEvent = true;
|
private boolean publishTargetPollEvent = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of poll operations queued before flush.
|
||||||
|
*/
|
||||||
|
private int pollPersistenceQueueSize = 10_000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum time before queue is flushed in {@link TimeUnit#MILLISECONDS}.
|
||||||
|
*/
|
||||||
|
private long pollPersistenceFlushTime = TimeUnit.SECONDS.toMillis(10);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to true to persist polls immediately.
|
||||||
|
*/
|
||||||
|
private boolean eagerPollPersistence;
|
||||||
|
|
||||||
|
public boolean isEagerPollPersistence() {
|
||||||
|
return eagerPollPersistence;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEagerPollPersistence(final boolean eagerPollPersistence) {
|
||||||
|
this.eagerPollPersistence = eagerPollPersistence;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getPollPersistenceFlushTime() {
|
||||||
|
return pollPersistenceFlushTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPollPersistenceFlushTime(final long pollPersistenceFlushTime) {
|
||||||
|
this.pollPersistenceFlushTime = pollPersistenceFlushTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPollPersistenceQueueSize() {
|
||||||
|
return pollPersistenceQueueSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPollPersistenceQueueSize(final int pollPersistenceQueueSize) {
|
||||||
|
this.pollPersistenceQueueSize = pollPersistenceQueueSize;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isRejectActionStatusForClosedAction() {
|
public boolean isRejectActionStatusForClosedAction() {
|
||||||
return rejectActionStatusForClosedAction;
|
return rejectActionStatusForClosedAction;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ public class TargetPollEvent extends RemoteTenantAwareEvent {
|
|||||||
// for serialization libs like jackson
|
// for serialization libs like jackson
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TargetPollEvent(final String controllerId, final String tenant, final String applicationId) {
|
||||||
|
super(controllerId, tenant, applicationId);
|
||||||
|
this.controllerId = controllerId;
|
||||||
|
}
|
||||||
|
|
||||||
public TargetPollEvent(final Target target, final String applicationId) {
|
public TargetPollEvent(final Target target, final String applicationId) {
|
||||||
super(target.getControllerId(), target.getTenant(), applicationId);
|
super(target.getControllerId(), target.getTenant(), applicationId);
|
||||||
this.controllerId = target.getControllerId();
|
this.controllerId = target.getControllerId();
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public interface ActionStatus extends TenantAwareBaseEntity {
|
|||||||
* @return time in {@link TimeUnit#MILLISECONDS} when the status was
|
* @return time in {@link TimeUnit#MILLISECONDS} when the status was
|
||||||
* reported.
|
* reported.
|
||||||
*/
|
*/
|
||||||
Long getOccurredAt();
|
long getOccurredAt();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {@link Action} this {@link ActionStatus} belongs to.
|
* @return {@link Action} this {@link ActionStatus} belongs to.
|
||||||
|
|||||||
@@ -14,9 +14,15 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.BlockingDeque;
|
||||||
|
import java.util.concurrent.LinkedBlockingDeque;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.Query;
|
||||||
import javax.persistence.criteria.CriteriaBuilder;
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
import javax.persistence.criteria.CriteriaQuery;
|
import javax.persistence.criteria.CriteriaQuery;
|
||||||
import javax.persistence.criteria.Root;
|
import javax.persistence.criteria.Root;
|
||||||
@@ -51,6 +57,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
|||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||||
|
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -66,10 +73,20 @@ import org.springframework.data.domain.Sort.Direction;
|
|||||||
import org.springframework.data.jpa.domain.Specification;
|
import org.springframework.data.jpa.domain.Specification;
|
||||||
import org.springframework.retry.annotation.Backoff;
|
import org.springframework.retry.annotation.Backoff;
|
||||||
import org.springframework.retry.annotation.Retryable;
|
import org.springframework.retry.annotation.Retryable;
|
||||||
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
|
import org.springframework.transaction.TransactionDefinition;
|
||||||
import org.springframework.transaction.annotation.Isolation;
|
import org.springframework.transaction.annotation.Isolation;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.transaction.support.DefaultTransactionDefinition;
|
||||||
|
import org.springframework.transaction.support.TransactionCallback;
|
||||||
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
|
import com.google.common.base.Joiner;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JPA based {@link ControllerManagement} implementation.
|
* JPA based {@link ControllerManagement} implementation.
|
||||||
*
|
*
|
||||||
@@ -79,6 +96,8 @@ import org.springframework.validation.annotation.Validated;
|
|||||||
public class JpaControllerManagement implements ControllerManagement {
|
public class JpaControllerManagement implements ControllerManagement {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ControllerManagement.class);
|
private static final Logger LOG = LoggerFactory.getLogger(ControllerManagement.class);
|
||||||
|
|
||||||
|
private final BlockingDeque<TargetPoll> queue;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private EntityManager entityManager;
|
private EntityManager entityManager;
|
||||||
|
|
||||||
@@ -97,9 +116,6 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private QuotaManagement quotaManagement;
|
private QuotaManagement quotaManagement;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RepositoryProperties repositoryProperties;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private TenantConfigurationManagement tenantConfigurationManagement;
|
private TenantConfigurationManagement tenantConfigurationManagement;
|
||||||
|
|
||||||
@@ -121,6 +137,38 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private SoftwareModuleMetadataRepository softwareModuleMetadataRepository;
|
private SoftwareModuleMetadataRepository softwareModuleMetadataRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PlatformTransactionManager txManager;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TenantAware tenantAware;
|
||||||
|
|
||||||
|
private final RepositoryProperties repositoryProperties;
|
||||||
|
|
||||||
|
JpaControllerManagement(final ScheduledExecutorService executorService,
|
||||||
|
final RepositoryProperties repositoryProperties) {
|
||||||
|
|
||||||
|
if (!repositoryProperties.isEagerPollPersistence()) {
|
||||||
|
executorService.scheduleWithFixedDelay(this::flushUpdateQueue,
|
||||||
|
repositoryProperties.getPollPersistenceFlushTime(),
|
||||||
|
repositoryProperties.getPollPersistenceFlushTime(), TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
|
queue = new LinkedBlockingDeque<>(repositoryProperties.getPollPersistenceQueueSize());
|
||||||
|
} else {
|
||||||
|
queue = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.repositoryProperties = repositoryProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> T runInNewTransaction(final String transactionName, final TransactionCallback<T> action) {
|
||||||
|
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
|
||||||
|
def.setName(transactionName);
|
||||||
|
def.setReadOnly(false);
|
||||||
|
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
|
||||||
|
return new TransactionTemplate(txManager, def).execute(action);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPollingTime() {
|
public String getPollingTime() {
|
||||||
return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement
|
return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement
|
||||||
@@ -214,19 +262,121 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
return updateTargetStatus(target, address);
|
return updateTargetStatus(target, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush the update queue by means to persisting
|
||||||
|
* {@link Target#getLastTargetQuery()}.
|
||||||
|
*/
|
||||||
|
private void flushUpdateQueue() {
|
||||||
|
LOG.debug("Run flushUpdateQueue.");
|
||||||
|
|
||||||
|
final int size = queue.size();
|
||||||
|
if (size <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.debug("{} events in flushUpdateQueue.", size);
|
||||||
|
|
||||||
|
final Set<TargetPoll> events = Sets.newHashSetWithExpectedSize(queue.size());
|
||||||
|
final int drained = queue.drainTo(events);
|
||||||
|
|
||||||
|
if (drained <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
events.stream().collect(Collectors.groupingBy(TargetPoll::getTenant)).forEach((tenant, polls) -> {
|
||||||
|
final TransactionCallback<Void> createTransaction = status -> updateLastTargetQueries(tenant, polls);
|
||||||
|
tenantAware.runAsTenant(tenant, () -> runInNewTransaction("flushUpdateQueue", createTransaction));
|
||||||
|
});
|
||||||
|
} catch (final RuntimeException ex) {
|
||||||
|
LOG.error("Failed to persist UpdateQueue content.", ex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.debug("{} events persisted.", drained);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Void updateLastTargetQueries(final String tenant, final List<TargetPoll> polls) {
|
||||||
|
LOG.debug("Persist {} targetqueries.", polls.size());
|
||||||
|
|
||||||
|
final List<List<String>> pollChunks = Lists.partition(
|
||||||
|
polls.stream().map(TargetPoll::getControllerId).collect(Collectors.toList()),
|
||||||
|
Constants.MAX_ENTRIES_IN_STATEMENT);
|
||||||
|
|
||||||
|
pollChunks.forEach(chunk -> {
|
||||||
|
setLastTargetQuery(tenant, System.currentTimeMillis(), chunk);
|
||||||
|
chunk.forEach(controllerId -> afterCommit.afterCommit(() -> eventPublisher
|
||||||
|
.publishEvent(new TargetPollEvent(controllerId, tenant, applicationContext.getId()))));
|
||||||
|
});
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets {@link Target#getLastTargetQuery()} by native SQL in order to avoid
|
||||||
|
* raising opt lock revision as this update is not mission critical and in
|
||||||
|
* fact only written by {@link ControllerManagement}, i.e. the target
|
||||||
|
* itself.
|
||||||
|
*/
|
||||||
|
private void setLastTargetQuery(final String tenant, final long currentTimeMillis, final List<String> chunk) {
|
||||||
|
final Map<String, String> paramMapping = Maps.newHashMapWithExpectedSize(chunk.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < chunk.size(); i++) {
|
||||||
|
paramMapping.put("cid" + i, chunk.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
final Query updateQuery = entityManager.createNativeQuery(
|
||||||
|
"UPDATE sp_target t SET t.last_target_query = #last_target_query WHERE t.controller_id IN ("
|
||||||
|
+ formatQueryInStatementParams(paramMapping.keySet()) + ") AND t.tenant = #tenant");
|
||||||
|
|
||||||
|
paramMapping.entrySet().forEach(entry -> updateQuery.setParameter(entry.getKey(), entry.getValue()));
|
||||||
|
updateQuery.setParameter("last_target_query", currentTimeMillis);
|
||||||
|
updateQuery.setParameter("tenant", tenant);
|
||||||
|
|
||||||
|
final int updated = updateQuery.executeUpdate();
|
||||||
|
if (updated < chunk.size()) {
|
||||||
|
LOG.error("Targets polls could not be applied completely ({} instead of {}).", updated, chunk.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String formatQueryInStatementParams(final Collection<String> paramNames) {
|
||||||
|
return "#" + Joiner.on(",#").join(paramNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores target directly to DB in case either {@link Target#getAddress()}
|
||||||
|
* or {@link Target#getUpdateStatus()} changes or the buffer queue is full.
|
||||||
|
*
|
||||||
|
*/
|
||||||
private Target updateTargetStatus(final JpaTarget toUpdate, final URI address) {
|
private Target updateTargetStatus(final JpaTarget toUpdate, final URI address) {
|
||||||
|
boolean storeEager = isStoreEager(toUpdate, address);
|
||||||
|
|
||||||
if (TargetUpdateStatus.UNKNOWN.equals(toUpdate.getUpdateStatus())) {
|
if (TargetUpdateStatus.UNKNOWN.equals(toUpdate.getUpdateStatus())) {
|
||||||
toUpdate.setUpdateStatus(TargetUpdateStatus.REGISTERED);
|
toUpdate.setUpdateStatus(TargetUpdateStatus.REGISTERED);
|
||||||
|
storeEager = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
toUpdate.setAddress(address.toString());
|
if (storeEager || !queue.offer(new TargetPoll(toUpdate))) {
|
||||||
toUpdate.setLastTargetQuery(System.currentTimeMillis());
|
toUpdate.setAddress(address.toString());
|
||||||
|
toUpdate.setLastTargetQuery(System.currentTimeMillis());
|
||||||
|
|
||||||
afterCommit.afterCommit(
|
afterCommit.afterCommit(
|
||||||
() -> eventPublisher.publishEvent(new TargetPollEvent(toUpdate, applicationContext.getId())));
|
() -> eventPublisher.publishEvent(new TargetPollEvent(toUpdate, applicationContext.getId())));
|
||||||
|
|
||||||
return targetRepository.save(toUpdate);
|
return targetRepository.save(toUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return toUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isStoreEager(final JpaTarget toUpdate, final URI address) {
|
||||||
|
if (repositoryProperties.isEagerPollPersistence()) {
|
||||||
|
return true;
|
||||||
|
} else if (toUpdate.getAddress() == null) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return !toUpdate.getAddress().equals(address);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -540,4 +690,62 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
.getContent().stream().collect(Collectors.groupingBy(o -> (Long) o[0],
|
.getContent().stream().collect(Collectors.groupingBy(o -> (Long) o[0],
|
||||||
Collectors.mapping(o -> (SoftwareModuleMetadata) o[1], Collectors.toList())));
|
Collectors.mapping(o -> (SoftwareModuleMetadata) o[1], Collectors.toList())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class TargetPoll {
|
||||||
|
|
||||||
|
private final String tenant;
|
||||||
|
private final String controllerId;
|
||||||
|
|
||||||
|
TargetPoll(final Target target) {
|
||||||
|
this.tenant = target.getTenant();
|
||||||
|
this.controllerId = target.getControllerId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTenant() {
|
||||||
|
return tenant;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getControllerId() {
|
||||||
|
return controllerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((controllerId == null) ? 0 : controllerId.hashCode());
|
||||||
|
result = prime * result + ((tenant == null) ? 0 : tenant.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final TargetPoll other = (TargetPoll) obj;
|
||||||
|
if (controllerId == null) {
|
||||||
|
if (other.controllerId != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!controllerId.equals(other.controllerId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (tenant == null) {
|
||||||
|
if (other.tenant != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!tenant.equals(other.tenant)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
package org.eclipse.hawkbit.repository.jpa;
|
package org.eclipse.hawkbit.repository.jpa;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
@@ -554,8 +555,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
|||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
ControllerManagement controllerManagement() {
|
ControllerManagement controllerManagement(final ScheduledExecutorService executorService,
|
||||||
return new JpaControllerManagement();
|
final RepositoryProperties repositoryProperties) {
|
||||||
|
return new JpaControllerManagement(executorService, repositoryProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
*/
|
*/
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public interface TargetRepository extends BaseEntityRepository<JpaTarget, Long>, JpaSpecificationExecutor<JpaTarget> {
|
public interface TargetRepository extends BaseEntityRepository<JpaTarget, Long>, JpaSpecificationExecutor<JpaTarget> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets {@link JpaTarget#getAssignedDistributionSet()}.
|
* Sets {@link JpaTarget#getAssignedDistributionSet()}.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ import java.util.List;
|
|||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.ConstraintMode;
|
import javax.persistence.ConstraintMode;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.EnumType;
|
|
||||||
import javax.persistence.Enumerated;
|
|
||||||
import javax.persistence.FetchType;
|
import javax.persistence.FetchType;
|
||||||
import javax.persistence.ForeignKey;
|
import javax.persistence.ForeignKey;
|
||||||
import javax.persistence.Index;
|
import javax.persistence.Index;
|
||||||
@@ -40,6 +38,9 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
|||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
|
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
|
||||||
import org.eclipse.persistence.annotations.CascadeOnDelete;
|
import org.eclipse.persistence.annotations.CascadeOnDelete;
|
||||||
|
import org.eclipse.persistence.annotations.ConversionValue;
|
||||||
|
import org.eclipse.persistence.annotations.Convert;
|
||||||
|
import org.eclipse.persistence.annotations.ObjectTypeConverter;
|
||||||
import org.eclipse.persistence.descriptors.DescriptorEvent;
|
import org.eclipse.persistence.descriptors.DescriptorEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,8 +72,12 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
|||||||
@Column(name = "active")
|
@Column(name = "active")
|
||||||
private boolean active;
|
private boolean active;
|
||||||
|
|
||||||
@Column(name = "action_type", nullable = false, length = 16)
|
@Column(name = "action_type", nullable = false)
|
||||||
@Enumerated(EnumType.STRING)
|
@ObjectTypeConverter(name = "actionType", objectType = Action.ActionType.class, dataType = Integer.class, conversionValues = {
|
||||||
|
@ConversionValue(objectValue = "FORCED", dataValue = "0"),
|
||||||
|
@ConversionValue(objectValue = "SOFT", dataValue = "1"),
|
||||||
|
@ConversionValue(objectValue = "TIMEFORCED", dataValue = "2") })
|
||||||
|
@Convert("actionType")
|
||||||
@NotNull
|
@NotNull
|
||||||
private ActionType actionType;
|
private ActionType actionType;
|
||||||
|
|
||||||
@@ -80,6 +85,18 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
|||||||
private long forcedTime;
|
private long forcedTime;
|
||||||
|
|
||||||
@Column(name = "status", nullable = false)
|
@Column(name = "status", nullable = false)
|
||||||
|
@ObjectTypeConverter(name = "status", objectType = Action.Status.class, dataType = Integer.class, conversionValues = {
|
||||||
|
@ConversionValue(objectValue = "FINISHED", dataValue = "0"),
|
||||||
|
@ConversionValue(objectValue = "ERROR", dataValue = "1"),
|
||||||
|
@ConversionValue(objectValue = "WARNING", dataValue = "2"),
|
||||||
|
@ConversionValue(objectValue = "RUNNING", dataValue = "3"),
|
||||||
|
@ConversionValue(objectValue = "CANCELED", dataValue = "4"),
|
||||||
|
@ConversionValue(objectValue = "CANCELING", dataValue = "5"),
|
||||||
|
@ConversionValue(objectValue = "RETRIEVED", dataValue = "6"),
|
||||||
|
@ConversionValue(objectValue = "DOWNLOAD", dataValue = "7"),
|
||||||
|
@ConversionValue(objectValue = "SCHEDULED", dataValue = "8"),
|
||||||
|
@ConversionValue(objectValue = "CANCEL_REJECTED", dataValue = "9") })
|
||||||
|
@Convert("status")
|
||||||
@NotNull
|
@NotNull
|
||||||
private Status status;
|
private Status status;
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ import org.eclipse.hawkbit.repository.model.Action;
|
|||||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||||
import org.eclipse.persistence.annotations.CascadeOnDelete;
|
import org.eclipse.persistence.annotations.CascadeOnDelete;
|
||||||
|
import org.eclipse.persistence.annotations.ConversionValue;
|
||||||
|
import org.eclipse.persistence.annotations.Convert;
|
||||||
|
import org.eclipse.persistence.annotations.ObjectTypeConverter;
|
||||||
|
|
||||||
import com.google.common.base.Splitter;
|
import com.google.common.base.Splitter;
|
||||||
|
|
||||||
@@ -50,8 +53,8 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements
|
|||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Column(name = "target_occurred_at")
|
@Column(name = "target_occurred_at", nullable = false, updatable = false)
|
||||||
private Long occurredAt;
|
private long occurredAt;
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||||
@JoinColumn(name = "action", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_act_stat_action"))
|
@JoinColumn(name = "action", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_act_stat_action"))
|
||||||
@@ -59,6 +62,18 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements
|
|||||||
private JpaAction action;
|
private JpaAction action;
|
||||||
|
|
||||||
@Column(name = "status", nullable = false, updatable = false)
|
@Column(name = "status", nullable = false, updatable = false)
|
||||||
|
@ObjectTypeConverter(name = "status", objectType = Action.Status.class, dataType = Integer.class, conversionValues = {
|
||||||
|
@ConversionValue(objectValue = "FINISHED", dataValue = "0"),
|
||||||
|
@ConversionValue(objectValue = "ERROR", dataValue = "1"),
|
||||||
|
@ConversionValue(objectValue = "WARNING", dataValue = "2"),
|
||||||
|
@ConversionValue(objectValue = "RUNNING", dataValue = "3"),
|
||||||
|
@ConversionValue(objectValue = "CANCELED", dataValue = "4"),
|
||||||
|
@ConversionValue(objectValue = "CANCELING", dataValue = "5"),
|
||||||
|
@ConversionValue(objectValue = "RETRIEVED", dataValue = "6"),
|
||||||
|
@ConversionValue(objectValue = "DOWNLOAD", dataValue = "7"),
|
||||||
|
@ConversionValue(objectValue = "SCHEDULED", dataValue = "8"),
|
||||||
|
@ConversionValue(objectValue = "CANCEL_REJECTED", dataValue = "9") })
|
||||||
|
@Convert("status")
|
||||||
@NotNull
|
@NotNull
|
||||||
private Status status;
|
private Status status;
|
||||||
|
|
||||||
@@ -66,7 +81,7 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements
|
|||||||
@ElementCollection(fetch = FetchType.LAZY, targetClass = String.class)
|
@ElementCollection(fetch = FetchType.LAZY, targetClass = String.class)
|
||||||
@CollectionTable(name = "sp_action_status_messages", joinColumns = @JoinColumn(name = "action_status_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_stat_msg_act_stat")), indexes = {
|
@CollectionTable(name = "sp_action_status_messages", joinColumns = @JoinColumn(name = "action_status_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_stat_msg_act_stat")), indexes = {
|
||||||
@Index(name = "sp_idx_action_status_msgs_01", columnList = "action_status_id") })
|
@Index(name = "sp_idx_action_status_msgs_01", columnList = "action_status_id") })
|
||||||
@Column(name = "detail_message", length = 512)
|
@Column(name = "detail_message", length = MESSAGE_ENTRY_LENGTH, nullable = false, updatable = false)
|
||||||
private List<String> messages;
|
private List<String> messages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -97,7 +112,7 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements
|
|||||||
* @param message
|
* @param message
|
||||||
* the message which should be added to this action status
|
* the message which should be added to this action status
|
||||||
*/
|
*/
|
||||||
public JpaActionStatus(final JpaAction action, final Status status, final Long occurredAt, final String message) {
|
public JpaActionStatus(final JpaAction action, final Status status, final long occurredAt, final String message) {
|
||||||
this.action = action;
|
this.action = action;
|
||||||
this.status = status;
|
this.status = status;
|
||||||
this.occurredAt = occurredAt;
|
this.occurredAt = occurredAt;
|
||||||
@@ -125,11 +140,11 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long getOccurredAt() {
|
public long getOccurredAt() {
|
||||||
return occurredAt;
|
return occurredAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOccurredAt(final Long occurredAt) {
|
public void setOccurredAt(final long occurredAt) {
|
||||||
this.occurredAt = occurredAt;
|
this.occurredAt = occurredAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,13 +41,13 @@ public class JpaArtifact extends AbstractJpaTenantAwareBaseEntity implements Art
|
|||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Column(name = "sha1_hash", length = 40, nullable = false, updatable = false)
|
@Column(name = "sha1_hash", length = 40, nullable = false, updatable = false)
|
||||||
@Size(max = 40)
|
@Size(min = 1, max = 40)
|
||||||
@NotEmpty
|
@NotNull
|
||||||
private String sha1Hash;
|
private String sha1Hash;
|
||||||
|
|
||||||
@Column(name = "provided_file_name", length = 256)
|
@Column(name = "provided_file_name", length = 256)
|
||||||
@Size(max = 256)
|
@Size(min = 1, max = 256)
|
||||||
@NotEmpty
|
@NotNull
|
||||||
private String filename;
|
private String filename;
|
||||||
|
|
||||||
@ManyToOne(optional = false, cascade = { CascadeType.PERSIST }, fetch = FetchType.LAZY)
|
@ManyToOne(optional = false, cascade = { CascadeType.PERSIST }, fetch = FetchType.LAZY)
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ import java.util.stream.Collectors;
|
|||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.ConstraintMode;
|
import javax.persistence.ConstraintMode;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.EnumType;
|
|
||||||
import javax.persistence.Enumerated;
|
|
||||||
import javax.persistence.FetchType;
|
import javax.persistence.FetchType;
|
||||||
import javax.persistence.ForeignKey;
|
import javax.persistence.ForeignKey;
|
||||||
import javax.persistence.JoinColumn;
|
import javax.persistence.JoinColumn;
|
||||||
@@ -30,10 +28,12 @@ import javax.validation.constraints.Size;
|
|||||||
|
|
||||||
import org.eclipse.hawkbit.repository.event.remote.RolloutDeletedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.RolloutDeletedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.RolloutUpdatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.RolloutUpdatedEvent;
|
||||||
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||||
|
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||||
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
|
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
|
||||||
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
|
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
|
||||||
import org.eclipse.persistence.annotations.CascadeOnDelete;
|
import org.eclipse.persistence.annotations.CascadeOnDelete;
|
||||||
@@ -44,7 +44,6 @@ import org.eclipse.persistence.descriptors.DescriptorEvent;
|
|||||||
import org.eclipse.persistence.queries.UpdateObjectQuery;
|
import org.eclipse.persistence.queries.UpdateObjectQuery;
|
||||||
import org.eclipse.persistence.sessions.changesets.DirectToFieldChangeRecord;
|
import org.eclipse.persistence.sessions.changesets.DirectToFieldChangeRecord;
|
||||||
import org.eclipse.persistence.sessions.changesets.ObjectChangeSet;
|
import org.eclipse.persistence.sessions.changesets.ObjectChangeSet;
|
||||||
import org.hibernate.validator.constraints.NotEmpty;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JPA implementation of a {@link Rollout}.
|
* JPA implementation of a {@link Rollout}.
|
||||||
@@ -56,18 +55,6 @@ import org.hibernate.validator.constraints.NotEmpty;
|
|||||||
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for
|
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for
|
||||||
// sub entities
|
// sub entities
|
||||||
@SuppressWarnings("squid:S2160")
|
@SuppressWarnings("squid:S2160")
|
||||||
@ObjectTypeConverter(name = "rolloutstatus", objectType = Rollout.RolloutStatus.class, dataType = Integer.class, conversionValues = {
|
|
||||||
@ConversionValue(objectValue = "CREATING", dataValue = "0"),
|
|
||||||
@ConversionValue(objectValue = "READY", dataValue = "1"),
|
|
||||||
@ConversionValue(objectValue = "PAUSED", dataValue = "2"),
|
|
||||||
@ConversionValue(objectValue = "STARTING", dataValue = "3"),
|
|
||||||
@ConversionValue(objectValue = "STOPPED", dataValue = "4"),
|
|
||||||
@ConversionValue(objectValue = "RUNNING", dataValue = "5"),
|
|
||||||
@ConversionValue(objectValue = "FINISHED", dataValue = "6"),
|
|
||||||
@ConversionValue(objectValue = "ERROR_CREATING", dataValue = "7"),
|
|
||||||
@ConversionValue(objectValue = "ERROR_STARTING", dataValue = "8"),
|
|
||||||
@ConversionValue(objectValue = "DELETING", dataValue = "9"),
|
|
||||||
@ConversionValue(objectValue = "DELETED", dataValue = "10") })
|
|
||||||
public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, EventAwareEntity {
|
public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, EventAwareEntity {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
@@ -78,9 +65,9 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
|
|||||||
@OneToMany(targetEntity = JpaRolloutGroup.class, fetch = FetchType.LAZY, mappedBy = "rollout")
|
@OneToMany(targetEntity = JpaRolloutGroup.class, fetch = FetchType.LAZY, mappedBy = "rollout")
|
||||||
private List<JpaRolloutGroup> rolloutGroups;
|
private List<JpaRolloutGroup> rolloutGroups;
|
||||||
|
|
||||||
@Column(name = "target_filter", length = 1024, nullable = false)
|
@Column(name = "target_filter", length = TargetFilterQuery.QUERY_MAX_SIZE, nullable = false)
|
||||||
@Size(max = 1024)
|
@Size(min = 1, max = TargetFilterQuery.QUERY_MAX_SIZE)
|
||||||
@NotEmpty
|
@NotNull
|
||||||
private String targetFilterQuery;
|
private String targetFilterQuery;
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||||
@@ -89,6 +76,18 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
|
|||||||
private JpaDistributionSet distributionSet;
|
private JpaDistributionSet distributionSet;
|
||||||
|
|
||||||
@Column(name = "status", nullable = false)
|
@Column(name = "status", nullable = false)
|
||||||
|
@ObjectTypeConverter(name = "rolloutstatus", objectType = Rollout.RolloutStatus.class, dataType = Integer.class, conversionValues = {
|
||||||
|
@ConversionValue(objectValue = "CREATING", dataValue = "0"),
|
||||||
|
@ConversionValue(objectValue = "READY", dataValue = "1"),
|
||||||
|
@ConversionValue(objectValue = "PAUSED", dataValue = "2"),
|
||||||
|
@ConversionValue(objectValue = "STARTING", dataValue = "3"),
|
||||||
|
@ConversionValue(objectValue = "STOPPED", dataValue = "4"),
|
||||||
|
@ConversionValue(objectValue = "RUNNING", dataValue = "5"),
|
||||||
|
@ConversionValue(objectValue = "FINISHED", dataValue = "6"),
|
||||||
|
@ConversionValue(objectValue = "ERROR_CREATING", dataValue = "7"),
|
||||||
|
@ConversionValue(objectValue = "ERROR_STARTING", dataValue = "8"),
|
||||||
|
@ConversionValue(objectValue = "DELETING", dataValue = "9"),
|
||||||
|
@ConversionValue(objectValue = "DELETED", dataValue = "10") })
|
||||||
@Convert("rolloutstatus")
|
@Convert("rolloutstatus")
|
||||||
@NotNull
|
@NotNull
|
||||||
private RolloutStatus status = RolloutStatus.CREATING;
|
private RolloutStatus status = RolloutStatus.CREATING;
|
||||||
@@ -96,8 +95,12 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
|
|||||||
@Column(name = "last_check")
|
@Column(name = "last_check")
|
||||||
private long lastCheck;
|
private long lastCheck;
|
||||||
|
|
||||||
@Column(name = "action_type", nullable = false, length = 16)
|
@Column(name = "action_type", nullable = false)
|
||||||
@Enumerated(EnumType.STRING)
|
@ObjectTypeConverter(name = "actionType", objectType = Action.ActionType.class, dataType = Integer.class, conversionValues = {
|
||||||
|
@ConversionValue(objectValue = "FORCED", dataValue = "0"),
|
||||||
|
@ConversionValue(objectValue = "SOFT", dataValue = "1"),
|
||||||
|
@ConversionValue(objectValue = "TIMEFORCED", dataValue = "2") })
|
||||||
|
@Convert("actionType")
|
||||||
@NotNull
|
@NotNull
|
||||||
private ActionType actionType = ActionType.FORCED;
|
private ActionType actionType = ActionType.FORCED;
|
||||||
|
|
||||||
|
|||||||
@@ -48,13 +48,6 @@ import org.eclipse.persistence.descriptors.DescriptorEvent;
|
|||||||
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for
|
// exception squid:S2160 - BaseEntity equals/hashcode is handling correctly for
|
||||||
// sub entities
|
// sub entities
|
||||||
@SuppressWarnings("squid:S2160")
|
@SuppressWarnings("squid:S2160")
|
||||||
@ObjectTypeConverter(name = "rolloutgroupstatus", objectType = RolloutGroup.RolloutGroupStatus.class, dataType = Integer.class, conversionValues = {
|
|
||||||
@ConversionValue(objectValue = "READY", dataValue = "0"),
|
|
||||||
@ConversionValue(objectValue = "SCHEDULED", dataValue = "1"),
|
|
||||||
@ConversionValue(objectValue = "FINISHED", dataValue = "2"),
|
|
||||||
@ConversionValue(objectValue = "ERROR", dataValue = "3"),
|
|
||||||
@ConversionValue(objectValue = "RUNNING", dataValue = "4"),
|
|
||||||
@ConversionValue(objectValue = "CREATING", dataValue = "5") })
|
|
||||||
public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGroup, EventAwareEntity {
|
public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGroup, EventAwareEntity {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
@@ -64,6 +57,13 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr
|
|||||||
private JpaRollout rollout;
|
private JpaRollout rollout;
|
||||||
|
|
||||||
@Column(name = "status", nullable = false)
|
@Column(name = "status", nullable = false)
|
||||||
|
@ObjectTypeConverter(name = "rolloutgroupstatus", objectType = RolloutGroup.RolloutGroupStatus.class, dataType = Integer.class, conversionValues = {
|
||||||
|
@ConversionValue(objectValue = "READY", dataValue = "0"),
|
||||||
|
@ConversionValue(objectValue = "SCHEDULED", dataValue = "1"),
|
||||||
|
@ConversionValue(objectValue = "FINISHED", dataValue = "2"),
|
||||||
|
@ConversionValue(objectValue = "ERROR", dataValue = "3"),
|
||||||
|
@ConversionValue(objectValue = "RUNNING", dataValue = "4"),
|
||||||
|
@ConversionValue(objectValue = "CREATING", dataValue = "5") })
|
||||||
@Convert("rolloutgroupstatus")
|
@Convert("rolloutgroupstatus")
|
||||||
private RolloutGroupStatus status = RolloutGroupStatus.CREATING;
|
private RolloutGroupStatus status = RolloutGroupStatus.CREATING;
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,6 @@ import javax.persistence.Column;
|
|||||||
import javax.persistence.ConstraintMode;
|
import javax.persistence.ConstraintMode;
|
||||||
import javax.persistence.ElementCollection;
|
import javax.persistence.ElementCollection;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.EnumType;
|
|
||||||
import javax.persistence.Enumerated;
|
|
||||||
import javax.persistence.FetchType;
|
import javax.persistence.FetchType;
|
||||||
import javax.persistence.ForeignKey;
|
import javax.persistence.ForeignKey;
|
||||||
import javax.persistence.Index;
|
import javax.persistence.Index;
|
||||||
@@ -63,6 +61,9 @@ import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagement
|
|||||||
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
|
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
|
||||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||||
import org.eclipse.persistence.annotations.CascadeOnDelete;
|
import org.eclipse.persistence.annotations.CascadeOnDelete;
|
||||||
|
import org.eclipse.persistence.annotations.ConversionValue;
|
||||||
|
import org.eclipse.persistence.annotations.Convert;
|
||||||
|
import org.eclipse.persistence.annotations.ObjectTypeConverter;
|
||||||
import org.eclipse.persistence.descriptors.DescriptorEvent;
|
import org.eclipse.persistence.descriptors.DescriptorEvent;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -87,10 +88,10 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
|||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(JpaTarget.class);
|
private static final Logger LOG = LoggerFactory.getLogger(JpaTarget.class);
|
||||||
|
|
||||||
private static final List<String> TARGET_UPDATE_EVENT_IGNORE_FIELDS = Arrays.asList("lastTargetQuery",
|
private static final List<String> TARGET_UPDATE_EVENT_IGNORE_FIELDS = Arrays.asList("lastTargetQuery", "address",
|
||||||
"lastTargetQuery", "address", "optLockRevision", "lastModifiedAt", "lastModifiedBy");
|
"optLockRevision", "lastModifiedAt", "lastModifiedBy");
|
||||||
|
|
||||||
@Column(name = "controller_id", length = Target.CONTROLLER_ID_MAX_SIZE)
|
@Column(name = "controller_id", length = Target.CONTROLLER_ID_MAX_SIZE, updatable = false, nullable = false)
|
||||||
@Size(min = 1, max = Target.CONTROLLER_ID_MAX_SIZE)
|
@Size(min = 1, max = Target.CONTROLLER_ID_MAX_SIZE)
|
||||||
@NotNull
|
@NotNull
|
||||||
@Pattern(regexp = "[.\\S]*", message = "has whitespaces which are not allowed")
|
@Pattern(regexp = "[.\\S]*", message = "has whitespaces which are not allowed")
|
||||||
@@ -130,8 +131,14 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
|||||||
@Column(name = "install_date")
|
@Column(name = "install_date")
|
||||||
private Long installationDate;
|
private Long installationDate;
|
||||||
|
|
||||||
@Column(name = "update_status", nullable = false, length = 16)
|
@Column(name = "update_status", nullable = false)
|
||||||
@Enumerated(EnumType.STRING)
|
@ObjectTypeConverter(name = "updateStatus", objectType = TargetUpdateStatus.class, dataType = Integer.class, conversionValues = {
|
||||||
|
@ConversionValue(objectValue = "UNKNOWN", dataValue = "0"),
|
||||||
|
@ConversionValue(objectValue = "IN_SYNC", dataValue = "1"),
|
||||||
|
@ConversionValue(objectValue = "PENDING", dataValue = "2"),
|
||||||
|
@ConversionValue(objectValue = "ERROR", dataValue = "3"),
|
||||||
|
@ConversionValue(objectValue = "REGISTERED", dataValue = "4") })
|
||||||
|
@Convert("updateStatus")
|
||||||
@NotNull
|
@NotNull
|
||||||
private TargetUpdateStatus updateStatus = TargetUpdateStatus.UNKNOWN;
|
private TargetUpdateStatus updateStatus = TargetUpdateStatus.UNKNOWN;
|
||||||
|
|
||||||
|
|||||||
@@ -19,12 +19,12 @@ import javax.persistence.JoinColumn;
|
|||||||
import javax.persistence.OneToOne;
|
import javax.persistence.OneToOne;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import javax.persistence.UniqueConstraint;
|
import javax.persistence.UniqueConstraint;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
import javax.validation.constraints.Size;
|
import javax.validation.constraints.Size;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||||
import org.eclipse.hawkbit.repository.model.TenantMetaData;
|
import org.eclipse.hawkbit.repository.model.TenantMetaData;
|
||||||
import org.hibernate.validator.constraints.NotEmpty;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tenant entity with meta data that is configured globally for the entire
|
* Tenant entity with meta data that is configured globally for the entire
|
||||||
@@ -45,8 +45,8 @@ public class JpaTenantMetaData extends AbstractJpaBaseEntity implements TenantMe
|
|||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Column(name = "tenant", nullable = false, updatable = false, length = 40)
|
@Column(name = "tenant", nullable = false, updatable = false, length = 40)
|
||||||
@Size(max = 40)
|
@Size(min = 1, max = 40)
|
||||||
@NotEmpty
|
@NotNull
|
||||||
private String tenant;
|
private String tenant;
|
||||||
|
|
||||||
@OneToOne(fetch = FetchType.LAZY)
|
@OneToOne(fetch = FetchType.LAZY)
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
ALTER TABLE sp_action ADD COLUMN action_type_new integer not null;
|
||||||
|
UPDATE sp_action SET action_type_new =
|
||||||
|
CASE WHEN (action_type = 'SOFT') THEN 1
|
||||||
|
WHEN (action_type = 'TIMEFORCED') THEN 2
|
||||||
|
ELSE 0 END;
|
||||||
|
ALTER TABLE sp_action DROP COLUMN action_type;
|
||||||
|
ALTER TABLE sp_action CHANGE COLUMN action_type_new action_type integer;
|
||||||
|
|
||||||
|
ALTER TABLE sp_rollout ADD COLUMN action_type_new integer not null;
|
||||||
|
UPDATE sp_rollout SET action_type_new =
|
||||||
|
CASE WHEN (action_type = 'SOFT') THEN 1
|
||||||
|
WHEN (action_type = 'TIMEFORCED') THEN 2
|
||||||
|
ELSE 0 END;
|
||||||
|
ALTER TABLE sp_rollout DROP COLUMN action_type;
|
||||||
|
ALTER TABLE sp_rollout CHANGE COLUMN action_type_new action_type integer;
|
||||||
|
|
||||||
|
ALTER TABLE sp_target ADD COLUMN update_status_new integer not null;
|
||||||
|
UPDATE sp_target SET update_status_new =
|
||||||
|
CASE WHEN (update_status = 'IN_SYNC') THEN 1
|
||||||
|
WHEN (update_status = 'PENDING') THEN 2
|
||||||
|
WHEN (update_status = 'ERROR') THEN 3
|
||||||
|
WHEN (update_status = 'REGISTERED') THEN 4
|
||||||
|
ELSE 0 END;
|
||||||
|
ALTER TABLE sp_target DROP COLUMN update_status;
|
||||||
|
ALTER TABLE sp_target CHANGE COLUMN update_status_new update_status integer;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
ALTER TABLE sp_action_status_messages CHANGE COLUMN detail_message detail_message varchar(512) not null;
|
||||||
|
ALTER TABLE sp_action CHANGE COLUMN distribution_set distribution_set bigint not null;
|
||||||
|
ALTER TABLE sp_action CHANGE COLUMN target target bigint not null;
|
||||||
|
ALTER TABLE sp_action CHANGE COLUMN status status integer not null;
|
||||||
|
ALTER TABLE sp_action_status CHANGE COLUMN target_occurred_at target_occurred_at bigint not null;
|
||||||
|
ALTER TABLE sp_action_status CHANGE COLUMN status status integer not null;
|
||||||
|
ALTER TABLE sp_rollout CHANGE COLUMN distribution_set distribution_set bigint not null;
|
||||||
|
ALTER TABLE sp_rollout CHANGE COLUMN status status integer not null;
|
||||||
|
ALTER TABLE sp_rolloutgroup CHANGE COLUMN rollout rollout bigint not null;
|
||||||
|
ALTER TABLE sp_rolloutgroup CHANGE COLUMN status status integer not null;
|
||||||
|
ALTER TABLE sp_artifact CHANGE COLUMN sha1_hash sha1_hash varchar(40) not null;
|
||||||
|
ALTER TABLE sp_target CHANGE COLUMN controller_id controller_id varchar(64) not null;
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
ALTER TABLE sp_action ADD COLUMN action_type_new integer not null;
|
||||||
|
UPDATE sp_action SET action_type_new =
|
||||||
|
CASE WHEN (action_type = 'SOFT') THEN 1
|
||||||
|
WHEN (action_type = 'TIMEFORCED') THEN 2
|
||||||
|
ELSE 0 END;
|
||||||
|
ALTER TABLE sp_action DROP COLUMN action_type;
|
||||||
|
ALTER TABLE sp_action CHANGE COLUMN action_type_new action_type integer;
|
||||||
|
|
||||||
|
ALTER TABLE sp_rollout ADD COLUMN action_type_new integer not null;
|
||||||
|
UPDATE sp_rollout SET action_type_new =
|
||||||
|
CASE WHEN (action_type = 'SOFT') THEN 1
|
||||||
|
WHEN (action_type = 'TIMEFORCED') THEN 2
|
||||||
|
ELSE 0 END;
|
||||||
|
ALTER TABLE sp_rollout DROP COLUMN action_type;
|
||||||
|
ALTER TABLE sp_rollout CHANGE COLUMN action_type_new action_type integer;
|
||||||
|
|
||||||
|
ALTER TABLE sp_target ADD COLUMN update_status_new integer not null;
|
||||||
|
UPDATE sp_target SET update_status_new =
|
||||||
|
CASE WHEN (update_status = 'IN_SYNC') THEN 1
|
||||||
|
WHEN (update_status = 'PENDING') THEN 2
|
||||||
|
WHEN (update_status = 'ERROR') THEN 3
|
||||||
|
WHEN (update_status = 'REGISTERED') THEN 4
|
||||||
|
ELSE 0 END;
|
||||||
|
ALTER TABLE sp_target DROP COLUMN update_status;
|
||||||
|
ALTER TABLE sp_target CHANGE COLUMN update_status_new update_status integer;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
ALTER TABLE sp_action_status_messages CHANGE COLUMN detail_message detail_message varchar(512) not null;
|
||||||
|
ALTER TABLE sp_action CHANGE COLUMN distribution_set distribution_set bigint not null;
|
||||||
|
ALTER TABLE sp_action CHANGE COLUMN target target bigint not null;
|
||||||
|
ALTER TABLE sp_action CHANGE COLUMN status status integer not null;
|
||||||
|
ALTER TABLE sp_action_status CHANGE COLUMN target_occurred_at target_occurred_at bigint not null;
|
||||||
|
ALTER TABLE sp_action_status CHANGE COLUMN status status integer not null;
|
||||||
|
ALTER TABLE sp_rollout CHANGE COLUMN distribution_set distribution_set bigint not null;
|
||||||
|
ALTER TABLE sp_rollout CHANGE COLUMN status status integer not null;
|
||||||
|
ALTER TABLE sp_rolloutgroup CHANGE COLUMN rollout rollout bigint not null;
|
||||||
|
ALTER TABLE sp_rolloutgroup CHANGE COLUMN status status integer not null;
|
||||||
|
ALTER TABLE sp_artifact CHANGE COLUMN sha1_hash sha1_hash varchar(40) not null;
|
||||||
|
ALTER TABLE sp_target CHANGE COLUMN controller_id controller_id varchar(64) not null;
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||||
|
import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
|
||||||
|
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
|
||||||
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
|
import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
||||||
|
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.test.context.TestPropertySource;
|
||||||
|
|
||||||
|
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("Controller Management")
|
||||||
|
@TestPropertySource(locations = "classpath:/jpa-test.properties", properties = {
|
||||||
|
"hawkbit.server.repository.eagerPollPersistence=false",
|
||||||
|
"hawkbit.server.repository.pollPersistenceFlushTime=1000" })
|
||||||
|
public class LazyControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RepositoryProperties repositoryProperties;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verfies that lazy target poll update is executed as specified.")
|
||||||
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetPollEvent.class, count = 2) })
|
||||||
|
public void lazyFindOrRegisterTargetIfItDoesNotexist() throws InterruptedException {
|
||||||
|
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist("AA", LOCALHOST);
|
||||||
|
assertThat(target).as("target should not be null").isNotNull();
|
||||||
|
|
||||||
|
TimeUnit.MILLISECONDS.sleep(10);
|
||||||
|
controllerManagement.findOrRegisterTargetIfItDoesNotexist("AA", LOCALHOST);
|
||||||
|
TimeUnit.MILLISECONDS.sleep(repositoryProperties.getPollPersistenceFlushTime() + 1);
|
||||||
|
|
||||||
|
final Target updated = targetManagement.get(target.getId()).get();
|
||||||
|
|
||||||
|
assertThat(updated.getOptLockRevision()).isEqualTo(target.getOptLockRevision());
|
||||||
|
assertThat(updated.getLastTargetQuery()).isGreaterThan(target.getLastTargetQuery());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.test;
|
|||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.ControllerPollProperties;
|
import org.eclipse.hawkbit.ControllerPollProperties;
|
||||||
import org.eclipse.hawkbit.HawkbitServerProperties;
|
import org.eclipse.hawkbit.HawkbitServerProperties;
|
||||||
@@ -60,9 +61,12 @@ import org.springframework.integration.support.locks.LockRegistry;
|
|||||||
import org.springframework.messaging.converter.MessageConverter;
|
import org.springframework.messaging.converter.MessageConverter;
|
||||||
import org.springframework.scheduling.annotation.AsyncConfigurer;
|
import org.springframework.scheduling.annotation.AsyncConfigurer;
|
||||||
import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;
|
import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;
|
||||||
|
import org.springframework.security.concurrent.DelegatingSecurityContextScheduledExecutorService;
|
||||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||||
import org.springframework.util.AntPathMatcher;
|
import org.springframework.util.AntPathMatcher;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spring context configuration required for Dev.Environment.
|
* Spring context configuration required for Dev.Environment.
|
||||||
*/
|
*/
|
||||||
@@ -186,6 +190,12 @@ public class TestConfiguration implements AsyncConfigurer {
|
|||||||
return new SimpleAsyncUncaughtExceptionHandler();
|
return new SimpleAsyncUncaughtExceptionHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ScheduledExecutorService scheduledExecutorService() {
|
||||||
|
return new DelegatingSecurityContextScheduledExecutorService(Executors.newScheduledThreadPool(1,
|
||||||
|
new ThreadFactoryBuilder().setNameFormat("central-scheduled-executor-pool-%d").build()));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @return returns a VirtualPropertyReplacer
|
* @return returns a VirtualPropertyReplacer
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
import org.eclipse.hawkbit.artifact.repository.ArtifactFilesystemProperties;
|
|
||||||
import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
|
import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
|
||||||
import org.eclipse.hawkbit.cache.TenantAwareCacheManager;
|
import org.eclipse.hawkbit.cache.TenantAwareCacheManager;
|
||||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||||
@@ -176,9 +175,6 @@ public abstract class AbstractIntegrationTest {
|
|||||||
@Autowired
|
@Autowired
|
||||||
protected SystemSecurityContext systemSecurityContext;
|
protected SystemSecurityContext systemSecurityContext;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ArtifactFilesystemProperties artifactFilesystemProperties;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
protected ArtifactRepository binaryArtifactRepository;
|
protected ArtifactRepository binaryArtifactRepository;
|
||||||
|
|
||||||
@@ -271,18 +267,18 @@ public abstract class AbstractIntegrationTest {
|
|||||||
|
|
||||||
osType = securityRule
|
osType = securityRule
|
||||||
.runAsPrivileged(() -> testdataFactory.findOrCreateSoftwareModuleType(TestdataFactory.SM_TYPE_OS));
|
.runAsPrivileged(() -> testdataFactory.findOrCreateSoftwareModuleType(TestdataFactory.SM_TYPE_OS));
|
||||||
osType = securityRule.runAsPrivileged(() -> softwareModuleTypeManagement.update(
|
osType = securityRule.runAsPrivileged(() -> softwareModuleTypeManagement
|
||||||
entityFactory.softwareModuleType().update(osType.getId()).description(description)));
|
.update(entityFactory.softwareModuleType().update(osType.getId()).description(description)));
|
||||||
|
|
||||||
appType = securityRule.runAsPrivileged(
|
appType = securityRule.runAsPrivileged(
|
||||||
() -> testdataFactory.findOrCreateSoftwareModuleType(TestdataFactory.SM_TYPE_APP, Integer.MAX_VALUE));
|
() -> testdataFactory.findOrCreateSoftwareModuleType(TestdataFactory.SM_TYPE_APP, Integer.MAX_VALUE));
|
||||||
appType = securityRule.runAsPrivileged(() -> softwareModuleTypeManagement.update(
|
appType = securityRule.runAsPrivileged(() -> softwareModuleTypeManagement
|
||||||
entityFactory.softwareModuleType().update(appType.getId()).description(description)));
|
.update(entityFactory.softwareModuleType().update(appType.getId()).description(description)));
|
||||||
|
|
||||||
runtimeType = securityRule
|
runtimeType = securityRule
|
||||||
.runAsPrivileged(() -> testdataFactory.findOrCreateSoftwareModuleType(TestdataFactory.SM_TYPE_RT));
|
.runAsPrivileged(() -> testdataFactory.findOrCreateSoftwareModuleType(TestdataFactory.SM_TYPE_RT));
|
||||||
runtimeType = securityRule.runAsPrivileged(() -> softwareModuleTypeManagement.update(
|
runtimeType = securityRule.runAsPrivileged(() -> softwareModuleTypeManagement
|
||||||
entityFactory.softwareModuleType().update(runtimeType.getId()).description(description)));
|
.update(entityFactory.softwareModuleType().update(runtimeType.getId()).description(description)));
|
||||||
|
|
||||||
standardDsType = securityRule.runAsPrivileged(() -> testdataFactory.findOrCreateDefaultTestDsType());
|
standardDsType = securityRule.runAsPrivileged(() -> testdataFactory.findOrCreateDefaultTestDsType());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ spring.jpa.properties.eclipselink.logging.parameters=true
|
|||||||
## JPA Repository - START
|
## JPA Repository - START
|
||||||
spring.datasource.url=jdbc:h2:mem:sp-db;DB_CLOSE_ON_EXIT=FALSE
|
spring.datasource.url=jdbc:h2:mem:sp-db;DB_CLOSE_ON_EXIT=FALSE
|
||||||
## JPA Repository - END
|
## JPA Repository - END
|
||||||
|
|
||||||
|
# Enforce persistence of targetpolls for test predictability.
|
||||||
|
hawkbit.server.repository.eagerPollPersistence=true
|
||||||
|
|
||||||
# Default properties for test that can be overridden during test run - END
|
# Default properties for test that can be overridden during test run - END
|
||||||
|
|
||||||
# Properties that are managed by autoconfigure module at runtime and not available during test - START
|
# Properties that are managed by autoconfigure module at runtime and not available during test - START
|
||||||
|
|||||||
@@ -238,7 +238,8 @@ public abstract class AbstractMetadataPopupLayout<E extends NamedVersionedEntity
|
|||||||
private TextField createKeyTextField() {
|
private TextField createKeyTextField() {
|
||||||
final TextField keyField = new TextFieldBuilder().caption(i18n.getMessage("textfield.key")).required(true)
|
final TextField keyField = new TextFieldBuilder().caption(i18n.getMessage("textfield.key")).required(true)
|
||||||
.prompt(i18n.getMessage("textfield.key")).immediate(true)
|
.prompt(i18n.getMessage("textfield.key")).immediate(true)
|
||||||
.id(UIComponentIdProvider.METADATA_KEY_FIELD_ID).maxLengthAllowed(128).buildTextComponent();
|
.id(UIComponentIdProvider.METADATA_KEY_FIELD_ID).maxLengthAllowed(MetaData.KEY_MAX_SIZE)
|
||||||
|
.buildTextComponent();
|
||||||
keyField.addTextChangeListener(this::onKeyChange);
|
keyField.addTextChangeListener(this::onKeyChange);
|
||||||
keyField.setTextChangeEventMode(TextChangeEventMode.EAGER);
|
keyField.setTextChangeEventMode(TextChangeEventMode.EAGER);
|
||||||
keyField.setWidth("100%");
|
keyField.setWidth("100%");
|
||||||
@@ -248,7 +249,7 @@ public abstract class AbstractMetadataPopupLayout<E extends NamedVersionedEntity
|
|||||||
private TextArea createValueTextField() {
|
private TextArea createValueTextField() {
|
||||||
valueTextArea = new TextAreaBuilder().caption(i18n.getMessage("textfield.value")).required(true)
|
valueTextArea = new TextAreaBuilder().caption(i18n.getMessage("textfield.value")).required(true)
|
||||||
.prompt(i18n.getMessage("textfield.value")).immediate(true).id(UIComponentIdProvider.METADATA_VALUE_ID)
|
.prompt(i18n.getMessage("textfield.value")).immediate(true).id(UIComponentIdProvider.METADATA_VALUE_ID)
|
||||||
.maxLengthAllowed(4000).buildTextComponent();
|
.maxLengthAllowed(MetaData.VALUE_MAX_SIZE).buildTextComponent();
|
||||||
valueTextArea.setNullRepresentation("");
|
valueTextArea.setNullRepresentation("");
|
||||||
valueTextArea.setSizeFull();
|
valueTextArea.setSizeFull();
|
||||||
valueTextArea.setHeight(100, Unit.PERCENTAGE);
|
valueTextArea.setHeight(100, Unit.PERCENTAGE);
|
||||||
|
|||||||
Reference in New Issue
Block a user