Merge with master

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
kaizimmerm
2016-09-14 11:05:05 +02:00
9 changed files with 79 additions and 46 deletions

View File

@@ -14,6 +14,7 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
@@ -29,7 +30,6 @@ import org.eclipse.hawkbit.ddi.json.model.DdiControllerBase;
import org.eclipse.hawkbit.ddi.json.model.DdiPolling;
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.tenancy.TenantAware;
@@ -108,16 +108,18 @@ public final class DataConversionHelper {
return file;
}
static DdiControllerBase fromTarget(final Target target, final List<Action> actions,
static DdiControllerBase fromTarget(final Target target, final Optional<Action> action,
final String defaultControllerPollTime, final TenantAware tenantAware) {
final DdiControllerBase result = new DdiControllerBase(
new DdiConfig(new DdiPolling(defaultControllerPollTime)));
boolean addedUpdate = false;
boolean addedCancel = false;
final long countCancelingActions = actions.stream().filter(a -> a.getStatus() == Status.CANCELING).count();
for (final Action action : actions) {
if (countCancelingActions <= 0 && !action.isCancelingOrCanceled() && !addedUpdate) {
if (action.isPresent()) {
if (action.get().isCancelingOrCanceled()) {
result.add(linkTo(
methodOn(DdiRootController.class, tenantAware.getCurrentTenant()).getControllerCancelAction(
tenantAware.getCurrentTenant(), target.getControllerId(), action.get().getId()))
.withRel(DdiRestConstants.CANCEL_ACTION));
} else {
// we need to add the hashcode here of the actionWithStatus
// because the action might
// have changed from 'soft' to 'forced' type and we need to
@@ -125,15 +127,8 @@ public final class DataConversionHelper {
// response because of eTags.
result.add(linkTo(methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
.getControllerBasedeploymentAction(tenantAware.getCurrentTenant(), target.getControllerId(),
action.getId(), calculateEtag(action)))
action.get().getId(), calculateEtag(action.get())))
.withRel(DdiRestConstants.DEPLOYMENT_BASE_ACTION));
addedUpdate = true;
} else if (action.isCancelingOrCanceled() && !addedCancel) {
result.add(linkTo(
methodOn(DdiRootController.class, tenantAware.getCurrentTenant()).getControllerCancelAction(
tenantAware.getCurrentTenant(), target.getControllerId(), action.getId()))
.withRel(DdiRestConstants.CANCEL_ACTION));
addedCancel = true;
}
}

View File

@@ -131,7 +131,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
}
return new ResponseEntity<>(
DataConversionHelper.fromTarget(target, controllerManagement.findActionByTargetAndActive(target),
DataConversionHelper.fromTarget(target, controllerManagement.findOldestActiveActionByTarget(target),
controllerManagement.getPollingTime(), tenantAware),
HttpStatus.OK);
}

View File

@@ -13,6 +13,7 @@ import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.apache.commons.lang3.ArrayUtils;
@@ -32,6 +33,7 @@ import org.eclipse.hawkbit.dmf.json.model.ArtifactHash;
import org.eclipse.hawkbit.dmf.json.model.DownloadResponse;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource;
import org.eclipse.hawkbit.eventbus.event.CancelTargetAssignmentEvent;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.repository.ArtifactManagement;
@@ -41,7 +43,7 @@ import org.eclipse.hawkbit.repository.RepositoryConstants;
import org.eclipse.hawkbit.repository.eventbus.event.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
import org.eclipse.hawkbit.repository.exception.ToManyStatusEntriesException;
import org.eclipse.hawkbit.repository.exception.TooManyStatusEntriesException;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
@@ -155,7 +157,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
return handleAuthentifiactionMessage(message);
} catch (final IllegalArgumentException ex) {
throw new AmqpRejectAndDontRequeueException("Invalid message!", ex);
} catch (final TenantNotExistException | ToManyStatusEntriesException e) {
} catch (final TenantNotExistException | TooManyStatusEntriesException e) {
throw new AmqpRejectAndDontRequeueException(e);
} finally {
SecurityContextHolder.setContext(oldContext);
@@ -196,8 +198,8 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
}
} catch (final IllegalArgumentException ex) {
throw new AmqpRejectAndDontRequeueException("Invalid message!", ex);
} catch (final TenantNotExistException teex) {
throw new AmqpRejectAndDontRequeueException(teex);
} catch (final TenantNotExistException | TooManyStatusEntriesException e) {
throw new AmqpRejectAndDontRequeueException(e);
} finally {
SecurityContextHolder.setContext(oldContext);
}
@@ -344,18 +346,24 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
}
private void lookIfUpdateAvailable(final Target target) {
final List<Action> actions = controllerManagement.findActionByTargetAndActive(target);
if (actions.isEmpty()) {
final Optional<Action> action = controllerManagement.findOldestActiveActionByTarget(target);
if (!action.isPresent()) {
return;
}
// action are ordered by ASC
final Action action = actions.get(0);
final DistributionSet distributionSet = action.getDistributionSet();
if (action.get().isCancelingOrCanceled()) {
amqpMessageDispatcherService.targetCancelAssignmentToDistributionSet(
new CancelTargetAssignmentEvent(target.getOptLockRevision(), target.getTenant(),
target.getControllerId(), action.get().getId(), target.getTargetInfo().getAddress()));
return;
}
final DistributionSet distributionSet = action.get().getDistributionSet();
final List<SoftwareModule> softwareModuleList = controllerManagement
.findSoftwareModulesByDistributionSet(distributionSet);
final String targetSecurityToken = systemSecurityContext.runAsSystem(() -> target.getSecurityToken());
amqpMessageDispatcherService.targetAssignDistributionSet(new TargetAssignDistributionSetEvent(
target.getOptLockRevision(), target.getTenant(), target.getControllerId(), action.getId(),
target.getOptLockRevision(), target.getTenant(), target.getControllerId(), action.get().getId(),
softwareModuleList, target.getTargetInfo().getAddress(), targetSecurityToken));
}

View File

@@ -23,6 +23,7 @@ import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.eclipse.hawkbit.api.HostnameResolver;
import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
@@ -155,6 +156,7 @@ public class AmqpMessageHandlerServiceTest {
final ArgumentCaptor<URI> uriCaptor = ArgumentCaptor.forClass(URI.class);
when(controllerManagementMock.findOrRegisterTargetIfItDoesNotexist(targetIdCaptor.capture(),
uriCaptor.capture())).thenReturn(null);
when(controllerManagementMock.findOldestActiveActionByTarget(Matchers.any())).thenReturn(Optional.empty());
amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, "vHost");
@@ -362,9 +364,8 @@ public class AmqpMessageHandlerServiceTest {
when(controllerManagementMock.addUpdateActionStatus(Matchers.any())).thenReturn(action);
when(entityFactoryMock.generateActionStatus()).thenReturn(new JpaActionStatus());
// for the test the same action can be used
final List<Action> actionList = new ArrayList<>();
actionList.add(action);
when(controllerManagementMock.findActionByTargetAndActive(Matchers.any())).thenReturn(actionList);
when(controllerManagementMock.findOldestActiveActionByTarget(Matchers.any()))
.thenReturn(Optional.of(action));
final List<SoftwareModule> softwareModuleList = createSoftwareModuleList();
when(controllerManagementMock.findSoftwareModulesByDistributionSet(Matchers.any()))

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.validation.constraints.NotNull;
@@ -19,7 +20,7 @@ import org.eclipse.hawkbit.repository.eventbus.event.DownloadProgressEvent;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.ToManyAttributeEntriesException;
import org.eclipse.hawkbit.repository.exception.ToManyStatusEntriesException;
import org.eclipse.hawkbit.repository.exception.TooManyStatusEntriesException;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
@@ -95,7 +96,7 @@ public interface ControllerManagement {
*
* @throws EntityAlreadyExistsException
* if a given entity already exists
* @throws ToManyStatusEntriesException
* @throws TooManyStatusEntriesException
* if more than the allowed number of status entries are
* inserted
*/
@@ -111,7 +112,18 @@ public interface ControllerManagement {
* @return a list of actions assigned to given target which are active
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
List<Action> findActionByTargetAndActive(@NotNull Target target);
List<Action> findActiveActionByTarget(@NotNull Target target);
/**
* Retrieves oldest {@link Action} that is active and assigned to a
* {@link Target}.
*
* @param target
* the target to retrieve the actions from
* @return a list of actions assigned to given target which are active
*/
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
Optional<Action> findOldestActiveActionByTarget(@NotNull Target target);
/**
* Get the {@link Action} entity for given actionId with all lazy

View File

@@ -18,7 +18,7 @@ import org.eclipse.hawkbit.exception.AbstractServerRtException;
*
*
*/
public final class ToManyStatusEntriesException extends AbstractServerRtException {
public final class TooManyStatusEntriesException extends AbstractServerRtException {
/**
*
*/
@@ -28,7 +28,7 @@ public final class ToManyStatusEntriesException extends AbstractServerRtExceptio
* Creates a new FileUploadFailedException with
* {@link SpServerError#SP_REST_BODY_NOT_READABLE} error.
*/
public ToManyStatusEntriesException() {
public TooManyStatusEntriesException() {
super(SpServerError.SP_ACTION_STATUS_TO_MANY_ENTRIES);
}
@@ -36,7 +36,7 @@ public final class ToManyStatusEntriesException extends AbstractServerRtExceptio
* @param cause
* for the exception
*/
public ToManyStatusEntriesException(final Throwable cause) {
public TooManyStatusEntriesException(final Throwable cause) {
super(SpServerError.SP_ACTION_STATUS_TO_MANY_ENTRIES, cause);
}
@@ -44,7 +44,7 @@ public final class ToManyStatusEntriesException extends AbstractServerRtExceptio
* @param message
* of the error
*/
public ToManyStatusEntriesException(final String message) {
public TooManyStatusEntriesException(final String message) {
super(message, SpServerError.SP_ACTION_STATUS_TO_MANY_ENTRIES);
}
}

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
@@ -79,21 +80,31 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
Slice<Action> findByTarget(Pageable pageable, JpaTarget target);
/**
* Retrieves all {@link Action}s which are active and referring the given
* {@link Target} in a specified order. Loads also the lazy
* {@link Action#getDistributionSet()} field.
* Retrieves all {@link Action}s which are active and referring to the given
* {@link Target} order by ID ascending.
*
* @param pageable
* page parameters
* @param target
* the target to find assigned actions
* @param active
* the action active flag
* @return the found {@link Action}s
*/
@EntityGraph(value = "Action.ds", type = EntityGraphType.LOAD)
List<Action> findByTargetAndActiveOrderByIdAsc(final JpaTarget target, boolean active);
/**
* Retrieves the oldest {@link Action} that is active and referring to the
* given {@link Target}.
*
* @param target
* the target to find assigned actions
* @param active
* the action active flag
*
* @return the found {@link Action}
*/
@EntityGraph(value = "Action.ds", type = EntityGraphType.LOAD)
Optional<Action> findFirstByTargetAndActiveOrderByIdAsc(final JpaTarget target, boolean active);
/**
* Retrieves latest {@link UpdateAction} for given target and
* {@link SoftwareModule}.

View File

@@ -12,6 +12,7 @@ import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
@@ -26,7 +27,7 @@ import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.ToManyAttributeEntriesException;
import org.eclipse.hawkbit.repository.exception.ToManyStatusEntriesException;
import org.eclipse.hawkbit.repository.exception.TooManyStatusEntriesException;
import org.eclipse.hawkbit.repository.jpa.cache.CacheWriteNotify;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
@@ -156,10 +157,15 @@ public class JpaControllerManagement implements ControllerManagement {
}
@Override
public List<Action> findActionByTargetAndActive(final Target target) {
public List<Action> findActiveActionByTarget(final Target target) {
return actionRepository.findByTargetAndActiveOrderByIdAsc((JpaTarget) target, true);
}
@Override
public Optional<Action> findOldestActiveActionByTarget(final Target target) {
return actionRepository.findFirstByTargetAndActiveOrderByIdAsc((JpaTarget) target, true);
}
@Override
public List<SoftwareModule> findSoftwareModulesByDistributionSet(final DistributionSet distributionSet) {
return new ArrayList<>(softwareModuleRepository.findByAssignedTo((JpaDistributionSet) distributionSet));
@@ -326,7 +332,7 @@ public class JpaControllerManagement implements ControllerManagement {
LOG_DOS.error(
"Potential denial of service (DOS) attack identfied. More status entries in the system than permitted ({})!",
securityProperties.getDos().getMaxStatusEntriesPerAction());
throw new ToManyStatusEntriesException(
throw new TooManyStatusEntriesException(
String.valueOf(securityProperties.getDos().getMaxStatusEntriesPerAction()));
}
}

View File

@@ -96,7 +96,7 @@
<org.powermock.version>1.5.4</org.powermock.version>
<pl.pragmatists.version>1.0.2</pl.pragmatists.version>
<guava.version>19.0</guava.version>
<mariadb-java-client.version>1.4.3</mariadb-java-client.version>
<mariadb-java-client.version>1.5.2</mariadb-java-client.version>
<embedded-mongo.version>1.50.5</embedded-mongo.version>
<javax.el-api.version>2.2.4</javax.el-api.version>
<corn-cps.version>1.1.7</corn-cps.version>