Fix sonar issues and add DMF tests for maintenance window feature (#655)

* Fix sonar issues.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* POM cleanup and more sonar issues fixed.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Remove unneeded JavaDocs.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* More sonar issues.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* More issues.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Adapt maintenance window to naming.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Add DMF tests.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Readibility.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Typos fixed.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
Kai Zimmermann
2018-03-08 10:42:25 +01:00
committed by GitHub
parent f4278c45ef
commit b4414438b0
32 changed files with 411 additions and 364 deletions

View File

@@ -50,7 +50,6 @@
<dependency>
<groupId>com.cronutils</groupId>
<artifactId>cron-utils</artifactId>
<version>5.0.5</version>
</dependency>
<!-- TEST -->

View File

@@ -236,7 +236,7 @@ public interface ControllerManagement {
* {@link TenantConfigurationKey#MAINTENANCE_WINDOW_POLL_COUNT}.
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public int getMaintenanceWindowPollCount();
int getMaintenanceWindowPollCount();
/**
* Returns polling time based on the maintenance window for an action.
@@ -248,14 +248,14 @@ public interface ControllerManagement {
* of maintenance window, it resets to default
* {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
*
* @param action
* @param actionId
* id the {@link Action} for which polling time is calculated
* based on it having maintenance window or not
*
* @return current {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
String getPollingTimeForAction(final Action action);
String getPollingTimeForAction(long actionId);
/**
* Checks if a given target has currently or has even been assigned to the

View File

@@ -16,10 +16,9 @@ import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeParseException;
import java.util.Optional;
import java.util.TimeZone;
import java.util.regex.Pattern;
import org.eclipse.hawkbit.repository.exception.InvalidMaintenanceScheduleException;
import org.springframework.util.StringUtils;
import com.cronutils.model.Cron;
import com.cronutils.model.definition.CronDefinition;
@@ -35,9 +34,7 @@ import com.cronutils.parser.CronParser;
*/
public class MaintenanceScheduleHelper {
ExecutionTime scheduleExecutor = null;
Duration duration = null;
TimeZone timeZone = null;
private final ExecutionTime scheduleExecutor;
/**
* Constructor that accepts a cron expression, duration and time zone and
@@ -48,22 +45,11 @@ public class MaintenanceScheduleHelper {
* maintenance window. Expression has 6 mandatory fields and 1
* last optional field: "second minute hour dayofmonth month
* weekday year"
* @param duration
* in HH:mm:ss format specifying the duration of a maintenance
* window, for example 00:30:00 for 30 minutes
* @param timezone
* is the time zone specified as +/-hh:mm offset from UTC. For
* example +02:00 for CET summer time and +00:00 for UTC. The
* start time of a maintenance window calculated based on the
* cron expression is relative to this time zone.
*/
public MaintenanceScheduleHelper(String cronSchedule, String duration, String timeZone) {
this.timeZone = TimeZone.getTimeZone(ZoneOffset.of(timeZone));
this.duration = Duration.parse(convertToISODuration(duration));
CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(QUARTZ);
CronParser parser = new CronParser(cronDefinition);
Cron quartzCron = parser.parse(cronSchedule);
public MaintenanceScheduleHelper(final String cronSchedule) {
final CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(QUARTZ);
final CronParser parser = new CronParser(cronDefinition);
final Cron quartzCron = parser.parse(cronSchedule);
this.scheduleExecutor = ExecutionTime.forCron(quartzCron);
}
@@ -78,11 +64,13 @@ public class MaintenanceScheduleHelper {
* @return {@link Optional<ZonedDateTime>} of the next available window. In
* case there is none, returns empty value.
*/
public Optional<ZonedDateTime> nextExecution(ZonedDateTime after) {
// Exception squid:S1166 - lib throws exception as well if no value found
@SuppressWarnings("squid:S1166")
public Optional<ZonedDateTime> nextExecution(final ZonedDateTime after) {
try {
ZonedDateTime next = this.scheduleExecutor.nextExecution(after);
return Optional.of(next);
} catch (IllegalArgumentException e) {
final ZonedDateTime next = this.scheduleExecutor.nextExecution(after);
return Optional.ofNullable(next);
} catch (final IllegalArgumentException ignored) {
return Optional.empty();
}
}
@@ -98,7 +86,7 @@ public class MaintenanceScheduleHelper {
* @return true if there is at least one valid schedule remaining, else
* false.
*/
public boolean hasValidScheduleAfter(ZonedDateTime after) {
private boolean hasValidScheduleAfter(final ZonedDateTime after) {
return nextExecution(after).isPresent();
}
@@ -122,34 +110,41 @@ public class MaintenanceScheduleHelper {
* start time of a maintenance window calculated based on the
* cron expression is relative to this time zone
*
* @return true if the schedule is valid, else throw an exception
*
* @throws InvalidMaintenanceScheduleException
* if the defined schedule fails the validity criteria.
*/
public static boolean validateMaintenanceSchedule(String cronSchedule, String duration, String timezone) {
// check if schedule, duration and timezone are all not null.
if (cronSchedule != null && duration != null && timezone != null) {
// check if schedule, duration and timezone are all not empty.
if (!(cronSchedule.isEmpty() || duration.isEmpty() || timezone.isEmpty())) {
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.of(timezone));
MaintenanceScheduleHelper scheduleHelper = new MaintenanceScheduleHelper(cronSchedule, duration,
timezone);
// check if there is a window currently active or available in
// future.
if (!scheduleHelper.hasValidScheduleAfter(now.minus(Duration.parse(convertToISODuration(duration))))) {
throw new InvalidMaintenanceScheduleException(
"No valid maintenance window available after current time");
}
} else {
throw new InvalidMaintenanceScheduleException("Either of schedule, duration or timezone empty.");
public static void validateMaintenanceSchedule(final String cronSchedule, final String duration,
final String timezone) {
// check if schedule, duration and timezone are all not empty.
if (allNotEmpty(cronSchedule, duration, timezone)) {
final ZonedDateTime now;
try {
now = ZonedDateTime.now(ZoneOffset.of(timezone));
Duration.parse(convertToISODuration(duration));
} catch (final RuntimeException validationFailed) {
throw new InvalidMaintenanceScheduleException("No valid maintenance window provided", validationFailed);
}
} else if (!(cronSchedule == null && duration == null && timezone == null)) {
final MaintenanceScheduleHelper scheduleHelper = new MaintenanceScheduleHelper(cronSchedule);
// check if there is a window currently active or available in
// future.
if (!scheduleHelper.hasValidScheduleAfter(now.minus(Duration.parse(convertToISODuration(duration))))) {
throw new InvalidMaintenanceScheduleException(
"No valid maintenance window available after current time");
}
} else if (atLeastOneNotEmpty(cronSchedule, duration, timezone)) {
throw new InvalidMaintenanceScheduleException(
"All of schedule, duration and timezone should either be null or non empty.");
}
}
return true;
private static boolean atLeastOneNotEmpty(final String cronSchedule, final String duration, final String timezone) {
return !(StringUtils.isEmpty(cronSchedule) && StringUtils.isEmpty(duration) && StringUtils.isEmpty(timezone));
}
private static boolean allNotEmpty(final String cronSchedule, final String duration, final String timezone) {
return !StringUtils.isEmpty(cronSchedule) && !StringUtils.isEmpty(duration) && !StringUtils.isEmpty(timezone);
}
/**
@@ -166,7 +161,7 @@ public class MaintenanceScheduleHelper {
* @throws DateTimeParseException
* if the text cannot be converted to ISO format.
*/
public static String convertToISODuration(String timeInterval) {
public static String convertToISODuration(final String timeInterval) {
return Duration.between(LocalTime.MIN, LocalTime.parse(timeInterval)).toString();
}
}

View File

@@ -26,6 +26,8 @@ public class TargetAssignDistributionSetEvent extends RemoteTenantAwareEvent {
private long distributionSetId;
private boolean maintenanceWindowAvailable;
private final Map<String, Long> actions = new HashMap<>();
/**
@@ -46,24 +48,32 @@ public class TargetAssignDistributionSetEvent extends RemoteTenantAwareEvent {
* the actions and the targets
* @param applicationId
* the application id.
* @param maintenanceWindowAvailable
* see {@link Action#isMaintenanceWindowAvailable()}
*/
public TargetAssignDistributionSetEvent(final String tenant, final long distributionSetId, final List<Action> a,
final String applicationId) {
final String applicationId, final boolean maintenanceWindowAvailable) {
super(distributionSetId, tenant, applicationId);
this.distributionSetId = distributionSetId;
this.maintenanceWindowAvailable = maintenanceWindowAvailable;
actions.putAll(a.stream().filter(action -> action.getDistributionSet().getId().longValue() == distributionSetId)
.collect(Collectors.toMap(action -> action.getTarget().getControllerId(), Action::getId)));
}
public TargetAssignDistributionSetEvent(final Action action, final String applicationId) {
this(action.getTenant(), action.getDistributionSet().getId(), Arrays.asList(action), applicationId);
this(action.getTenant(), action.getDistributionSet().getId(), Arrays.asList(action), applicationId,
action.isMaintenanceWindowAvailable());
}
public Long getDistributionSetId() {
return distributionSetId;
}
public boolean isMaintenanceWindowAvailable() {
return maintenanceWindowAvailable;
}
public Map<String, Long> getActions() {
return actions;
}

View File

@@ -14,8 +14,6 @@ import org.eclipse.hawkbit.repository.model.Action.ActionType;
/**
* A custom view on {@link Target} with {@link ActionType}.
*
*
*
*/
public class TargetWithActionType {
@@ -23,9 +21,9 @@ public class TargetWithActionType {
private final String controllerId;
private final ActionType actionType;
private final long forceTime;
private String maintenanceSchedule = null;
private String maintenanceWindowDuration = null;
private String maintenanceWindowTimeZone = null;
private String maintenanceSchedule;
private String maintenanceWindowDuration;
private String maintenanceWindowTimeZone;
public TargetWithActionType(final String controllerId) {
this.controllerId = controllerId;
@@ -35,7 +33,7 @@ public class TargetWithActionType {
public TargetWithActionType(final String controllerId, final ActionType actionType, final long forceTime) {
this.controllerId = controllerId;
this.actionType = actionType;
this.actionType = actionType != null ? actionType : ActionType.FORCED;
this.forceTime = forceTime;
}
@@ -64,19 +62,16 @@ public class TargetWithActionType {
* if the parameters do not define a valid maintenance schedule.
*/
public TargetWithActionType(final String controllerId, final ActionType actionType, final long forceTime,
String maintenanceSchedule, String maintenanceWindowDuration, String maintenanceWindowTimeZone) {
this.controllerId = controllerId;
this.actionType = actionType;
this.forceTime = forceTime;
final String maintenanceSchedule, final String maintenanceWindowDuration,
final String maintenanceWindowTimeZone) {
this(controllerId, actionType, forceTime);
if (MaintenanceScheduleHelper.validateMaintenanceSchedule(maintenanceSchedule, maintenanceWindowDuration,
maintenanceWindowTimeZone)) {
this.maintenanceSchedule = maintenanceSchedule;
this.maintenanceWindowDuration = maintenanceWindowDuration;
this.maintenanceWindowTimeZone = maintenanceWindowTimeZone;
} else {
throw new InvalidMaintenanceScheduleException("Invalid maintenance window definition");
}
this.maintenanceSchedule = maintenanceSchedule;
this.maintenanceWindowDuration = maintenanceWindowDuration;
this.maintenanceWindowTimeZone = maintenanceWindowTimeZone;
MaintenanceScheduleHelper.validateMaintenanceSchedule(maintenanceSchedule, maintenanceWindowDuration,
maintenanceWindowTimeZone);
}
public ActionType getActionType() {

View File

@@ -129,8 +129,9 @@ public abstract class AbstractDsAssignmentStrategy {
return;
}
afterCommit.afterCommit(() -> eventPublisher.publishEvent(
new TargetAssignDistributionSetEvent(tenant, distributionSetId, actions, applicationContext.getId())));
afterCommit.afterCommit(
() -> eventPublisher.publishEvent(new TargetAssignDistributionSetEvent(tenant, distributionSetId,
actions, applicationContext.getId(), actions.get(0).isMaintenanceWindowAvailable())));
}
protected void sendTargetUpdatedEvent(final JpaTarget target) {

View File

@@ -14,10 +14,6 @@ import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.util.Collection;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -62,6 +58,7 @@ import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.model.Target;
@@ -210,31 +207,17 @@ public class JpaControllerManagement implements ControllerManagement {
.getConfigurationValue(TenantConfigurationKey.MAINTENANCE_WINDOW_POLL_COUNT, Integer.class).getValue());
}
/**
* Returns polling time based on the maintenance window for an action.
* Server will reduce the polling interval as the start time for maintenance
* window approaches, so that at least these many attempts are made between
* current polling until start of maintenance window. Poll time keeps
* reducing with MinPollingTime as lower limit
* {@link TenantConfigurationKey#MIN_POLLING_TIME_INTERVAL}. After the start
* of maintenance window, it resets to default
* {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
*
* @param action
* id the {@link Action} for which polling time is calculated
* based on it having maintenance window or not
*
* @return current {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
*/
@Override
public String getPollingTimeForAction(final Action action) {
if (action == null || !action.hasMaintenanceSchedule() || action.isMaintenanceScheduleLapsed()) {
public String getPollingTimeForAction(final long actionId) {
final JpaAction action = getActionAndThrowExceptionIfNotFound(actionId);
if (!action.hasMaintenanceSchedule() || action.isMaintenanceScheduleLapsed()) {
return getPollingTime();
}
JpaAction jpaAction = getActionAndThrowExceptionIfNotFound(action.getId());
return (new EventTimer(getPollingTime(), getMinPollingTime(), ChronoUnit.SECONDS))
.timeToNextEvent(getMaintenanceWindowPollCount(), jpaAction.getMaintenanceWindowStartTime().get());
.timeToNextEvent(getMaintenanceWindowPollCount(), action.getMaintenanceWindowStartTime().orElse(null));
}
/**
@@ -244,7 +227,7 @@ public class JpaControllerManagement implements ControllerManagement {
* polling, should happen when timer expires. Class makes use of java.time
* package to manipulate and calculate timer duration.
*/
private class EventTimer {
private static class EventTimer {
private final String defaultEventInterval;
private final Duration defaultEventIntervalDuration;
@@ -266,7 +249,7 @@ public class JpaControllerManagement implements ControllerManagement {
* @param timerUnit
* representing the unit of time to be used for timer.
*/
EventTimer(String defaultEventInterval, String minimumEventInterval, TemporalUnit timeUnit) {
EventTimer(final String defaultEventInterval, final String minimumEventInterval, final TemporalUnit timeUnit) {
this.defaultEventInterval = defaultEventInterval;
this.defaultEventIntervalDuration = Duration
.parse(MaintenanceScheduleHelper.convertToISODuration(defaultEventInterval));
@@ -294,8 +277,8 @@ public class JpaControllerManagement implements ControllerManagement {
*
* @return String in HH:mm:ss format for time to next event.
*/
String timeToNextEvent(int eventCount, ZonedDateTime timerResetTime) {
ZonedDateTime currentTime = ZonedDateTime.now();
String timeToNextEvent(final int eventCount, final ZonedDateTime timerResetTime) {
final ZonedDateTime currentTime = ZonedDateTime.now();
// If there is no reset time, or if we already past the reset time,
// return the default interval.
@@ -304,7 +287,7 @@ public class JpaControllerManagement implements ControllerManagement {
}
// Calculate the interval timer based on desired event count.
Duration currentIntervalDuration = Duration.of(currentTime.until(timerResetTime, timeUnit), timeUnit)
final Duration currentIntervalDuration = Duration.of(currentTime.until(timerResetTime, timeUnit), timeUnit)
.dividedBy(eventCount);
// Need not return interval greater than the default.
@@ -810,7 +793,8 @@ public class JpaControllerManagement implements ControllerManagement {
// For negative and large value of messageCount, limit the number of
// messages.
final int limit = messageCount < 0 || messageCount >= RepositoryConstants.MAX_ACTION_HISTORY_MSG_COUNT
? RepositoryConstants.MAX_ACTION_HISTORY_MSG_COUNT : messageCount;
? RepositoryConstants.MAX_ACTION_HISTORY_MSG_COUNT
: messageCount;
final PageRequest pageable = new PageRequest(0, limit, new Sort(Direction.DESC, "occurredAt"));
final Page<String> messages = actionStatusRepository.findMessagesByActionIdAndMessageNotLike(pageable, actionId,
@@ -913,9 +897,10 @@ public class JpaControllerManagement implements ControllerManagement {
* @throws EntityNotFoundException
* if action with given actionId does not exist.
*/
@Override
@Modifying
@Transactional(isolation = Isolation.READ_COMMITTED)
public Action cancelAction(long actionId) {
public Action cancelAction(final long actionId) {
LOG.debug("cancelAction({})", actionId);
final JpaAction action = actionRepository.findById(actionId)

View File

@@ -402,6 +402,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
}
final String tenant = rolloutGroupActions.getContent().get(0).getTenant();
final boolean maintenanceWindowAvailable = rolloutGroupActions.getContent().get(0)
.isMaintenanceWindowAvailable();
final List<Action> targetAssignments = rolloutGroupActions.getContent().stream()
.map(action -> (JpaAction) action).map(this::closeActionIfSetWasAlreadyAssigned)
@@ -410,7 +412,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
if (!CollectionUtils.isEmpty(targetAssignments)) {
afterCommit.afterCommit(() -> eventPublisher.publishEvent(new TargetAssignDistributionSetEvent(tenant,
distributionSetId, targetAssignments, applicationContext.getId())));
distributionSetId, targetAssignments, applicationContext.getId(), maintenanceWindowAvailable)));
}
return rolloutGroupActions.getTotalElements();

View File

@@ -101,7 +101,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@ConversionValue(objectValue = "DOWNLOAD", dataValue = "7"),
@ConversionValue(objectValue = "SCHEDULED", dataValue = "8"),
@ConversionValue(objectValue = "CANCEL_REJECTED", dataValue = "9"),
@ConversionValue(objectValue = "DOWNLOADED", dataValue = "10")})
@ConversionValue(objectValue = "DOWNLOADED", dataValue = "10") })
@Convert("status")
@NotNull
private Status status;
@@ -118,13 +118,13 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@JoinColumn(name = "rollout", updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_rollout"))
private JpaRollout rollout;
@Column(name = "maintenance_cron_schedule", length = Action.MAINTENANCE_SCHEDULE_CRON_LENGTH)
@Column(name = "maintenance_cron_schedule", updatable = false, length = Action.MAINTENANCE_SCHEDULE_CRON_LENGTH)
private String maintenanceSchedule;
@Column(name = "maintenance_duration", length = Action.MAINTENANCE_WINDOW_DURATION_LENGTH)
@Column(name = "maintenance_duration", updatable = false, length = Action.MAINTENANCE_WINDOW_DURATION_LENGTH)
private String maintenanceWindowDuration;
@Column(name = "maintenance_time_zone", length = Action.MAINTENANCE_WINDOW_TIMEZONE_LENGTH)
@Column(name = "maintenance_time_zone", updatable = false, length = Action.MAINTENANCE_WINDOW_TIMEZONE_LENGTH)
private String maintenanceWindowTimeZone;
/**
@@ -243,7 +243,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
* @param maintenanceSchedule
* is a cron expression to be used for scheduling.
*/
public void setMaintenanceSchedule(String maintenanceSchedule) {
public void setMaintenanceSchedule(final String maintenanceSchedule) {
this.maintenanceSchedule = maintenanceSchedule;
}
@@ -254,7 +254,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
* is the duration of an available maintenance schedule in
* HH:mm:ss format.
*/
public void setMaintenanceWindowDuration(String maintenanceWindowDuration) {
public void setMaintenanceWindowDuration(final String maintenanceWindowDuration) {
this.maintenanceWindowDuration = maintenanceWindowDuration;
}
@@ -267,7 +267,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
* start time of a maintenance window calculated based on the
* cron expression is relative to this time zone.
*/
public void setMaintenanceWindowTimeZone(String maintenanceWindowTimeZone) {
public void setMaintenanceWindowTimeZone(final String maintenanceWindowTimeZone) {
this.maintenanceWindowTimeZone = maintenanceWindowTimeZone;
}
@@ -277,10 +277,9 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
*
* @return the {@link MaintenanceScheduleHelper} object.
*/
MaintenanceScheduleHelper getScheduler() {
private MaintenanceScheduleHelper getScheduler() {
if (this.scheduleHelper == null) {
this.scheduleHelper = new MaintenanceScheduleHelper(maintenanceSchedule, maintenanceWindowDuration,
maintenanceWindowTimeZone);
this.scheduleHelper = new MaintenanceScheduleHelper(maintenanceSchedule);
}
return this.scheduleHelper;
}
@@ -290,7 +289,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
*
* @return the {@link Duration} of each maintenance window.
*/
Duration getMaintenanceWindowDuration() {
private Duration getMaintenanceWindowDuration() {
return Duration.parse(MaintenanceScheduleHelper.convertToISODuration(this.maintenanceWindowDuration));
}
@@ -302,7 +301,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
* @return the start time as {@link Optional<ZonedDateTime>}.
*/
public Optional<ZonedDateTime> getMaintenanceWindowStartTime() {
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.of(maintenanceWindowTimeZone));
final ZonedDateTime now = ZonedDateTime.now(ZoneOffset.of(maintenanceWindowTimeZone));
return getScheduler().nextExecution(now.minus(getMaintenanceWindowDuration()));
}
@@ -313,50 +312,21 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
*
* @return the end time of window as {@link Optional<ZonedDateTime>}.
*/
public Optional<ZonedDateTime> getMaintenanceWindowEndTime() {
if (getMaintenanceWindowStartTime().isPresent()) {
return Optional.of(getMaintenanceWindowStartTime().get().plus(getMaintenanceWindowDuration()));
}
return Optional.empty();
private Optional<ZonedDateTime> getMaintenanceWindowEndTime() {
return getMaintenanceWindowStartTime().map(start -> start.plus(getMaintenanceWindowDuration()));
}
/**
* The method checks whether the action has a maintenance schedule defined
* for it. A maintenance schedule defines a set of maintenance windows
* during which actual update can be performed. A valid schedule defines at
* least one maintenance window.
*
* @return true if action has a maintenance schedule, else false.
*/
@Override
public boolean hasMaintenanceSchedule() {
return this.maintenanceSchedule != null;
}
/**
* The method checks whether the maintenance schedule has already lapsed for
* the action, i.e. there are no more windows available for maintenance.
* Controller manager uses the method to check if the maintenance schedule
* has lapsed, and automatically cancels the action if it is lapsed.
*
* @return true if maintenance schedule has lapsed, else false.
*/
@Override
public boolean isMaintenanceScheduleLapsed() {
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.of(maintenanceWindowTimeZone));
final ZonedDateTime now = ZonedDateTime.now(ZoneOffset.of(maintenanceWindowTimeZone));
return !getScheduler().nextExecution(now.minus(getMaintenanceWindowDuration())).isPresent();
}
/**
* The method checks whether a maintenance window is available for the
* action to proceed. If it is available, a 'true' value is returned. The
* maintenance window is considered available: 1) If there is no maintenance
* schedule at all, in which case device can start update any time after
* download is finished; or 2) the current time is within a scheduled
* maintenance window start and end time.
*
* @return true if maintenance window is available, else false.
*/
@Override
public boolean isMaintenanceWindowAvailable() {
if (!hasMaintenanceSchedule()) {
@@ -368,10 +338,12 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
// available.
return false;
} else {
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.of(maintenanceWindowTimeZone));
if (this.getMaintenanceWindowStartTime().isPresent() && this.getMaintenanceWindowEndTime().isPresent()) {
return now.isAfter(this.getMaintenanceWindowStartTime().get())
&& now.isBefore(this.getMaintenanceWindowEndTime().get());
final ZonedDateTime now = ZonedDateTime.now(ZoneOffset.of(maintenanceWindowTimeZone));
final Optional<ZonedDateTime> start = getMaintenanceWindowStartTime();
final Optional<ZonedDateTime> end = getMaintenanceWindowEndTime();
if (start.isPresent() && end.isPresent()) {
return now.isAfter(start.get()) && now.isBefore(end.get());
} else {
return false;
}

View File

@@ -55,7 +55,8 @@ public class RemoteTenantAwareEventTest extends AbstractRemoteEventTest {
final Action action = actionRepository.save(generateAction);
final TargetAssignDistributionSetEvent assignmentEvent = new TargetAssignDistributionSetEvent(
action.getTenant(), dsA.getId(), Arrays.asList(action), serviceMatcher.getServiceId());
action.getTenant(), dsA.getId(), Arrays.asList(action), serviceMatcher.getServiceId(),
action.isMaintenanceWindowAvailable());
TargetAssignDistributionSetEvent underTest = (TargetAssignDistributionSetEvent) createProtoStuffEvent(
assignmentEvent);