Merge branch 'master' into
fix_introduce_consistent_button_position_in_change_dialogs Conflicts: hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/tag/CreateUpdateTagLayout.java hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/AddUpdateRolloutWindowLayout.java hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/RolloutListGrid.java Signed-off-by: Melanie Retter <melanie.retter@bosch-si.com>
This commit is contained in:
0
3rd-dependencies/listDeps.sh
Executable file → Normal file
0
3rd-dependencies/listDeps.sh
Executable file → Normal file
12
README.md
12
README.md
@@ -52,19 +52,19 @@ $ java -jar ./examples/hawkbit-mgmt-api-client/target/hawkbit-mgmt-api-client-#v
|
||||
* The master branch contains future development towards 0.2. We are currently focusing on:
|
||||
* Rollout Management for large scale rollouts.
|
||||
* Clustering capabilities for the update server.
|
||||
* Upgrade of Spring Boot and Vaadin depedencies.
|
||||
* Upgrade of Spring Boot and Vaadin dependencies.
|
||||
* And of course tons of usability improvements and bug fixes.
|
||||
|
||||
|
||||
# Modules
|
||||
`hawkbit-core` : core elements.
|
||||
`hawkbit-security-core` : core security elements.
|
||||
`hawkbit-security-integration` : security integration elements to integrate security into hawkbit.
|
||||
`hawkbit-artifact-repository-mongo` : artifact repository implementation to mongoDB.
|
||||
`hawkbit-core` : internal interfaces and utility classes..
|
||||
`hawkbit-security-core` : authentication and authorization.
|
||||
`hawkbit-security-integration` : authentication and authorization integrated with the APIs.
|
||||
`hawkbit-artifact-repository-mongo` : artifact repository implementation to MongoDB.
|
||||
`hawkbit-autoconfigure` : spring-boot auto-configuration.
|
||||
`hawkbit-dmf-api` : API for the Device Management Integration.
|
||||
`hawkbit-dmf-amqp` : AMQP endpoint implementation for the DMF API.
|
||||
`hawkbit-repository` : repository implemenation based on SQL for all meta-data.
|
||||
`hawkbit-repository` : repository implementation based on SQL for all meta-data.
|
||||
`hawkbit-http-security` : implementation for security filters for HTTP.
|
||||
`hawkbit-rest-api` : API classes for the REST Management API.
|
||||
`hawkbit-rest-resource` : HTTP REST endpoints for the Management and the Direct Device API.
|
||||
|
||||
@@ -22,14 +22,17 @@ import org.springframework.context.annotation.Import;
|
||||
@SpringBootApplication
|
||||
@Import({ RepositoryApplicationConfiguration.class })
|
||||
@EnableHawkbitManagedSecurityConfiguration
|
||||
// Exception squid:S1118 - Spring boot standard behavior
|
||||
@SuppressWarnings({ "squid:S1118" })
|
||||
public class Start {
|
||||
|
||||
/**
|
||||
* Main method to start the spring-boot application.
|
||||
*
|
||||
* @param args
|
||||
* the VM arguments.
|
||||
*/
|
||||
// Exception squid:S2095 - Spring boot standard behavior
|
||||
@SuppressWarnings({ "squid:S2095" })
|
||||
public static void main(final String[] args) {
|
||||
SpringApplication.run(Start.class, args);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
|
||||
Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
@@ -9,7 +8,8 @@
|
||||
http://www.eclipse.org/legal/epl-v10.html
|
||||
|
||||
-->
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||
<project
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
@@ -133,13 +133,17 @@
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.vaadin</groupId>
|
||||
<artifactId>vaadin-bom</artifactId>
|
||||
<version>7.6.3</version>
|
||||
<version>${vaadin.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.simulator;
|
||||
|
||||
import org.eclipse.hawkbit.simulator.UpdateStatus.ResponseStatus;
|
||||
|
||||
/**
|
||||
* The bean of a simulated device which can be stored in the
|
||||
* {@link DeviceSimulatorRepository} or shown in the UI.
|
||||
@@ -22,16 +24,15 @@ public abstract class AbstractSimulatedDevice {
|
||||
private Status status;
|
||||
private double progress;
|
||||
private String swversion = "unknown";
|
||||
private ResponseStatus responseStatus = ResponseStatus.SUCCESSFUL;
|
||||
private UpdateStatus updateStatus = new UpdateStatus(ResponseStatus.SUCCESSFUL, "Simulation complete!");
|
||||
private Protocol protocol = Protocol.DMF_AMQP;
|
||||
private String targetSecurityToken;
|
||||
|
||||
private int nextPollCounterSec;
|
||||
|
||||
/**
|
||||
* Enum definition of the protocol to be used for the simulated device.
|
||||
*
|
||||
* @author Michael Hirsch
|
||||
*
|
||||
*/
|
||||
public enum Protocol {
|
||||
/**
|
||||
@@ -69,24 +70,6 @@ public abstract class AbstractSimulatedDevice {
|
||||
ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* The status to response to the hawkbit update server if an simulated
|
||||
* update process should be respond with successful or failure update.
|
||||
*
|
||||
* @author Michael Hirsch
|
||||
*
|
||||
*/
|
||||
public enum ResponseStatus {
|
||||
/**
|
||||
* updated has been successful and response the successful update.
|
||||
*/
|
||||
SUCCESSFUL,
|
||||
/**
|
||||
* updated has been not successful and response the error update.
|
||||
*/
|
||||
ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* empty constructor.
|
||||
*/
|
||||
@@ -158,12 +141,12 @@ public abstract class AbstractSimulatedDevice {
|
||||
this.swversion = swversion;
|
||||
}
|
||||
|
||||
public ResponseStatus getResponseStatus() {
|
||||
return responseStatus;
|
||||
public UpdateStatus getUpdateStatus() {
|
||||
return updateStatus;
|
||||
}
|
||||
|
||||
public void setResponseStatus(final ResponseStatus responseStatus) {
|
||||
this.responseStatus = responseStatus;
|
||||
public void setUpdateStatus(final UpdateStatus updateStatus) {
|
||||
this.updateStatus = updateStatus;
|
||||
}
|
||||
|
||||
public Protocol getProtocol() {
|
||||
@@ -177,4 +160,13 @@ public abstract class AbstractSimulatedDevice {
|
||||
public void setNextPollCounterSec(final int nextPollDelayInSec) {
|
||||
this.nextPollCounterSec = nextPollDelayInSec;
|
||||
}
|
||||
|
||||
public String getTargetSecurityToken() {
|
||||
return targetSecurityToken;
|
||||
}
|
||||
|
||||
public void setTargetSecurityToken(final String targetSecurityToken) {
|
||||
this.targetSecurityToken = targetSecurityToken;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.simulator;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import org.eclipse.hawkbit.simulator.http.ControllerResource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -18,7 +16,7 @@ import com.jayway.jsonpath.JsonPath;
|
||||
import com.jayway.jsonpath.PathNotFoundException;
|
||||
|
||||
/**
|
||||
* @author Michael Hirsch
|
||||
* A simulated device using the DDI API of the hawkBit update server.
|
||||
*
|
||||
*/
|
||||
public class DDISimulatedDevice extends AbstractSimulatedDevice {
|
||||
@@ -26,12 +24,12 @@ public class DDISimulatedDevice extends AbstractSimulatedDevice {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DDISimulatedDevice.class);
|
||||
|
||||
private final int pollDelaySec;
|
||||
private final ScheduledExecutorService pollthreadpool;
|
||||
private final ControllerResource controllerResource;
|
||||
|
||||
private final DeviceSimulatorUpdater deviceUpdater;
|
||||
|
||||
private volatile boolean removed;
|
||||
private volatile Long currentActionId;
|
||||
private final DeviceSimulatorUpdater deviceUpdater;
|
||||
|
||||
/**
|
||||
* @param id
|
||||
@@ -42,18 +40,14 @@ public class DDISimulatedDevice extends AbstractSimulatedDevice {
|
||||
* the delay of the poll interval in sec
|
||||
* @param controllerResource
|
||||
* the http controller resource
|
||||
* @param pollthreadpool
|
||||
* the threadpool for polling endpoint
|
||||
* @param deviceUpdater
|
||||
* the service to update devices
|
||||
*/
|
||||
public DDISimulatedDevice(final String id, final String tenant, final int pollDelaySec,
|
||||
final ControllerResource controllerResource, final ScheduledExecutorService pollthreadpool,
|
||||
final DeviceSimulatorUpdater deviceUpdater) {
|
||||
final ControllerResource controllerResource, final DeviceSimulatorUpdater deviceUpdater) {
|
||||
super(id, tenant, Protocol.DDI_HTTP);
|
||||
this.pollDelaySec = pollDelaySec;
|
||||
this.controllerResource = controllerResource;
|
||||
this.pollthreadpool = pollthreadpool;
|
||||
this.deviceUpdater = deviceUpdater;
|
||||
setNextPollCounterSec(pollDelaySec);
|
||||
}
|
||||
@@ -76,27 +70,12 @@ public class DDISimulatedDevice extends AbstractSimulatedDevice {
|
||||
final String basePollJson = controllerResource.get(getTenant(), getId());
|
||||
try {
|
||||
final String href = JsonPath.parse(basePollJson).read("_links.deploymentBase.href");
|
||||
final long actionId = Long.parseLong(href.substring(href.lastIndexOf("/") + 1, href.indexOf("?")));
|
||||
final long actionId = Long.parseLong(href.substring(href.lastIndexOf('/') + 1, href.indexOf('?')));
|
||||
if (currentActionId == null) {
|
||||
final String deploymentJson = controllerResource.getDeployment(getTenant(), getId(), actionId);
|
||||
final String swVersion = JsonPath.parse(deploymentJson).read("deployment.chunks[0].version");
|
||||
currentActionId = actionId;
|
||||
deviceUpdater.startUpdate(getTenant(), getId(), actionId, swVersion, (device, actionId1) -> {
|
||||
switch (device.getResponseStatus()) {
|
||||
case SUCCESSFUL:
|
||||
controllerResource.postSuccessFeedback(getTenant(), getId(),
|
||||
actionId1);
|
||||
break;
|
||||
case ERROR:
|
||||
controllerResource.postErrorFeedback(getTenant(), getId(),
|
||||
actionId1);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException(
|
||||
"simulated device has an unknown response status + " + device.getResponseStatus());
|
||||
}
|
||||
currentActionId = null;
|
||||
});
|
||||
startDdiUpdate(actionId, swVersion);
|
||||
}
|
||||
} catch (final PathNotFoundException e) {
|
||||
// href might not be in the json response, so ignore
|
||||
@@ -106,4 +85,21 @@ public class DDISimulatedDevice extends AbstractSimulatedDevice {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void startDdiUpdate(final long actionId, final String swVersion) {
|
||||
deviceUpdater.startUpdate(getTenant(), getId(), actionId, swVersion, null, null, (device, actionId1) -> {
|
||||
switch (device.getUpdateStatus().getResponseStatus()) {
|
||||
case SUCCESSFUL:
|
||||
controllerResource.postSuccessFeedback(getTenant(), getId(), actionId1);
|
||||
break;
|
||||
case ERROR:
|
||||
controllerResource.postErrorFeedback(getTenant(), getId(), actionId1);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("simulated device has an unknown response status + "
|
||||
+ device.getUpdateStatus().getResponseStatus());
|
||||
}
|
||||
currentActionId = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,7 @@
|
||||
package org.eclipse.hawkbit.simulator;
|
||||
|
||||
/**
|
||||
* An simulated device using the DMF API of the hawkbit update server.
|
||||
*
|
||||
* @author Michael Hirsch
|
||||
*
|
||||
* A simulated device using the DMF API of the hawkBit update server.
|
||||
*/
|
||||
public class DMFSimulatedDevice extends AbstractSimulatedDevice {
|
||||
|
||||
|
||||
@@ -21,8 +21,6 @@ import com.vaadin.spring.annotation.EnableVaadin;
|
||||
/**
|
||||
* The main-method to start the Spring-Boot application.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableVaadin
|
||||
@@ -46,6 +44,8 @@ public class DeviceSimulator {
|
||||
* @param args
|
||||
* the args
|
||||
*/
|
||||
// Exception squid:S2095 - Spring boot standard behavior
|
||||
@SuppressWarnings({ "squid:S2095" })
|
||||
public static void main(final String[] args) {
|
||||
SpringApplication.run(DeviceSimulator.class, args);
|
||||
}
|
||||
|
||||
@@ -8,27 +8,56 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.simulator;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.DigestOutputStream;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.http.conn.ssl.SSLContextBuilder;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.eclipse.hawkbit.dmf.json.model.Artifact;
|
||||
import org.eclipse.hawkbit.dmf.json.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
|
||||
import org.eclipse.hawkbit.simulator.UpdateStatus.ResponseStatus;
|
||||
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
|
||||
import org.eclipse.hawkbit.simulator.event.InitUpdate;
|
||||
import org.eclipse.hawkbit.simulator.event.ProgressUpdate;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import com.google.common.io.ByteStreams;
|
||||
|
||||
/**
|
||||
* @author Michael Hirsch
|
||||
* Update simulation handler.
|
||||
*
|
||||
*/
|
||||
@Service
|
||||
public class DeviceSimulatorUpdater {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DeviceSimulatorUpdater.class);
|
||||
|
||||
private static final ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(4);
|
||||
|
||||
@@ -54,14 +83,18 @@ public class DeviceSimulatorUpdater {
|
||||
* @param actionId
|
||||
* the actionId from the hawkbit update server to start the
|
||||
* update.
|
||||
* @param swVersion
|
||||
* @param modules
|
||||
* the software module version from the hawkbit update server
|
||||
* @param swVersion
|
||||
* the software version as static value in case modules is null
|
||||
* @param targetSecurityToken
|
||||
* the target security token for download authentication
|
||||
* @param callback
|
||||
* the callback which gets called when the simulated update
|
||||
* process has been finished
|
||||
*/
|
||||
public void startUpdate(final String tenant, final String id, final long actionId, final String swVersion,
|
||||
final UpdaterCallback callback) {
|
||||
final List<SoftwareModule> modules, final String targetSecurityToken, final UpdaterCallback callback) {
|
||||
AbstractSimulatedDevice device = repository.get(tenant, id);
|
||||
|
||||
// plug and play - non existing device will be auto created
|
||||
@@ -70,14 +103,26 @@ public class DeviceSimulatorUpdater {
|
||||
}
|
||||
|
||||
device.setProgress(0.0);
|
||||
device.setSwversion(swVersion);
|
||||
|
||||
if (modules == null || modules.isEmpty()) {
|
||||
device.setSwversion(swVersion);
|
||||
} else {
|
||||
device.setSwversion(modules.stream().map(sm -> sm.getModuleVersion()).collect(Collectors.joining(", ")));
|
||||
}
|
||||
device.setTargetSecurityToken(targetSecurityToken);
|
||||
eventbus.post(new InitUpdate(device));
|
||||
|
||||
threadPool.schedule(new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus, callback),
|
||||
2_000, TimeUnit.MILLISECONDS);
|
||||
threadPool.schedule(
|
||||
new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus, callback, modules), 2_000,
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private static final class DeviceSimulatorUpdateThread implements Runnable {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final int MINIMUM_TOKENLENGTH_FOR_HINT = 6;
|
||||
|
||||
private static final Random rndSleep = new SecureRandom();
|
||||
|
||||
private final AbstractSimulatedDevice device;
|
||||
@@ -85,38 +130,205 @@ public class DeviceSimulatorUpdater {
|
||||
private final long actionId;
|
||||
private final EventBus eventbus;
|
||||
private final UpdaterCallback callback;
|
||||
private final List<SoftwareModule> modules;
|
||||
|
||||
private DeviceSimulatorUpdateThread(final AbstractSimulatedDevice device, final SpSenderService spSenderService,
|
||||
final long actionId, final EventBus eventbus, final UpdaterCallback callback) {
|
||||
final long actionId, final EventBus eventbus, final UpdaterCallback callback,
|
||||
final List<SoftwareModule> modules) {
|
||||
this.device = device;
|
||||
this.spSenderService = spSenderService;
|
||||
this.actionId = actionId;
|
||||
this.eventbus = eventbus;
|
||||
this.callback = callback;
|
||||
this.modules = modules;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (device.getProgress() <= 0 && modules != null) {
|
||||
device.setUpdateStatus(simulateDownloads(device.getTargetSecurityToken()));
|
||||
if (isErrorResponse(device.getUpdateStatus())) {
|
||||
callback.updateFinished(device, actionId);
|
||||
eventbus.post(new ProgressUpdate(device));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final double newProgress = device.getProgress() + 0.2;
|
||||
device.setProgress(newProgress);
|
||||
if (newProgress < 1.0) {
|
||||
threadPool.schedule(
|
||||
new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus, callback),
|
||||
new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus, callback, modules),
|
||||
rndSleep.nextInt(5_000), TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
callback.updateFinished(device, actionId);
|
||||
}
|
||||
eventbus.post(new ProgressUpdate(device));
|
||||
}
|
||||
|
||||
private UpdateStatus simulateDownloads(final String targetToken) {
|
||||
final List<UpdateStatus> status = new ArrayList<>();
|
||||
|
||||
LOGGER.info("Simulate downloads for {}", device.getId());
|
||||
|
||||
modules.forEach(module -> module.getArtifacts()
|
||||
.forEach(artifact -> handleArtifacts(targetToken, status, artifact)));
|
||||
|
||||
final UpdateStatus result = new UpdateStatus(ResponseStatus.SUCCESSFUL);
|
||||
result.getStatusMessages().add("Simulation complete!");
|
||||
status.forEach(download -> {
|
||||
result.getStatusMessages().addAll(download.getStatusMessages());
|
||||
if (isErrorResponse(download)) {
|
||||
result.setResponseStatus(ResponseStatus.ERROR);
|
||||
}
|
||||
});
|
||||
|
||||
LOGGER.info("Download simulations complete for {}", device.getId());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isErrorResponse(final UpdateStatus status) {
|
||||
if (status == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ResponseStatus.ERROR.equals(status.getResponseStatus());
|
||||
}
|
||||
|
||||
private static void handleArtifacts(final String targetToken, final List<UpdateStatus> status,
|
||||
final Artifact artifact) {
|
||||
artifact.getUrls().entrySet().forEach(entry -> {
|
||||
switch (entry.getKey()) {
|
||||
case HTTP:
|
||||
case HTTPS:
|
||||
status.add(downloadUrl(entry.getValue(), targetToken, artifact.getHashes().getSha1(),
|
||||
artifact.getSize()));
|
||||
break;
|
||||
default:
|
||||
// not supported yet
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static UpdateStatus downloadUrl(final String url, final String targetToken, final String sha1Hash,
|
||||
final long size) {
|
||||
LOGGER.debug("Downloading {} with token {}, expected sha1 hash {} and size {}", url,
|
||||
hideTokenDetails(targetToken), sha1Hash, size);
|
||||
|
||||
long overallread = 0;
|
||||
try {
|
||||
final CloseableHttpClient httpclient = createHttpClientThatAcceptsAllServerCerts();
|
||||
final HttpGet request = new HttpGet(url);
|
||||
request.addHeader(HttpHeaders.AUTHORIZATION, "TargetToken " + targetToken);
|
||||
|
||||
final String sha1HashResult;
|
||||
try (final CloseableHttpResponse response = httpclient.execute(request)) {
|
||||
|
||||
if (response.getStatusLine().getStatusCode() != HttpStatus.OK.value()) {
|
||||
final String message = wrongStatusCode(url, response);
|
||||
return new UpdateStatus(ResponseStatus.ERROR, message);
|
||||
}
|
||||
|
||||
if (response.getEntity().getContentLength() != size) {
|
||||
final String message = wrongContentLength(url, size, response);
|
||||
return new UpdateStatus(ResponseStatus.ERROR, message);
|
||||
}
|
||||
|
||||
final File tempFile = File.createTempFile("uploadFile", null);
|
||||
final MessageDigest md = MessageDigest.getInstance("SHA-1");
|
||||
|
||||
try (final DigestOutputStream dos = new DigestOutputStream(new FileOutputStream(tempFile), md)) {
|
||||
try (final BufferedOutputStream bdos = new BufferedOutputStream(dos)) {
|
||||
try (BufferedInputStream bis = new BufferedInputStream(response.getEntity().getContent())) {
|
||||
overallread = ByteStreams.copy(bis, bdos);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (tempFile != null && !tempFile.delete()) {
|
||||
LOGGER.error("Could not delete temporary file: {}", tempFile);
|
||||
}
|
||||
}
|
||||
|
||||
if (overallread != size) {
|
||||
final String message = incompleteRead(url, size, overallread);
|
||||
return new UpdateStatus(ResponseStatus.ERROR, message);
|
||||
}
|
||||
|
||||
sha1HashResult = BaseEncoding.base16().lowerCase().encode(md.digest());
|
||||
}
|
||||
|
||||
if (!sha1Hash.equalsIgnoreCase(sha1HashResult)) {
|
||||
final String message = wrongHash(url, sha1Hash, overallread, sha1HashResult);
|
||||
return new UpdateStatus(ResponseStatus.ERROR, message);
|
||||
}
|
||||
|
||||
} catch (IOException | KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
|
||||
LOGGER.error("Failed to download {} with {}", url, e.getMessage());
|
||||
return new UpdateStatus(ResponseStatus.ERROR, "Failed to download " + url + ": " + e.getMessage());
|
||||
}
|
||||
|
||||
final String message = "Downloaded " + url + " (" + overallread + " bytes)";
|
||||
LOGGER.debug(message);
|
||||
return new UpdateStatus(ResponseStatus.SUCCESSFUL, message);
|
||||
}
|
||||
|
||||
private static String hideTokenDetails(final String targetToken) {
|
||||
if (targetToken.isEmpty()) {
|
||||
return "<EMTPTY!>";
|
||||
}
|
||||
|
||||
if (targetToken.length() <= MINIMUM_TOKENLENGTH_FOR_HINT) {
|
||||
return "***";
|
||||
}
|
||||
|
||||
return targetToken.substring(0, 2) + "***"
|
||||
+ targetToken.substring(targetToken.length() - 2, targetToken.length());
|
||||
}
|
||||
|
||||
private static String wrongHash(final String url, final String sha1Hash, final long overallread,
|
||||
final String sha1HashResult) {
|
||||
final String message = "Download " + url + " failed with SHA1 hash missmatch (Expected: " + sha1Hash
|
||||
+ " but got: " + sha1HashResult + ") (" + overallread + " bytes)";
|
||||
LOGGER.error(message);
|
||||
return message;
|
||||
}
|
||||
|
||||
private static String incompleteRead(final String url, final long size, final long overallread) {
|
||||
final String message = "Download " + url + " is incomplete (Expected: " + size + " but got: " + overallread
|
||||
+ ")";
|
||||
LOGGER.error(message);
|
||||
return message;
|
||||
}
|
||||
|
||||
private static String wrongContentLength(final String url, final long size,
|
||||
final CloseableHttpResponse response) {
|
||||
final String message = "Download " + url + " has wrong content length (Expected: " + size + " but got: "
|
||||
+ response.getEntity().getContentLength() + ")";
|
||||
LOGGER.error(message);
|
||||
return message;
|
||||
}
|
||||
|
||||
private static String wrongStatusCode(final String url, final CloseableHttpResponse response) {
|
||||
final String message = "Download " + url + " failed (" + response.getStatusLine().getStatusCode() + ")";
|
||||
LOGGER.error(message);
|
||||
return message;
|
||||
}
|
||||
|
||||
private static CloseableHttpClient createHttpClientThatAcceptsAllServerCerts()
|
||||
throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
|
||||
final SSLContextBuilder builder = new SSLContextBuilder();
|
||||
builder.loadTrustMaterial(null, (chain, authType) -> true);
|
||||
final SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build());
|
||||
return HttpClients.custom().setSSLSocketFactory(sslsf).build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback interface which is called when the simulated update process has
|
||||
* been finished and the caller of starting the simulated update process can
|
||||
* send the result to the hawkbit update server back.
|
||||
*
|
||||
* @author Michael Hirsch
|
||||
*
|
||||
* send the result back to the hawkBit update server.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface UpdaterCallback {
|
||||
|
||||
@@ -26,9 +26,6 @@ import com.google.common.eventbus.EventBus;
|
||||
/**
|
||||
* Poll time trigger which executes the {@link DDISimulatedDevice#poll()} every
|
||||
* second.
|
||||
*
|
||||
* @author Michael Hirsch
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class NextPollTimeController {
|
||||
@@ -59,16 +56,15 @@ public class NextPollTimeController {
|
||||
|
||||
devices.forEach(device -> {
|
||||
int nextCounter = device.getNextPollCounterSec() - 1;
|
||||
if (nextCounter < 0) {
|
||||
if (device instanceof DDISimulatedDevice) {
|
||||
try {
|
||||
pollService.submit(() -> ((DDISimulatedDevice) device).poll());
|
||||
} catch (final IllegalStateException e) {
|
||||
LOGGER.trace("Device could not be polled", e);
|
||||
}
|
||||
nextCounter = ((DDISimulatedDevice) device).getPollDelaySec();
|
||||
if (nextCounter < 0 && device instanceof DDISimulatedDevice) {
|
||||
try {
|
||||
pollService.submit(() -> ((DDISimulatedDevice) device).poll());
|
||||
} catch (final IllegalStateException e) {
|
||||
LOGGER.trace("Device could not be polled", e);
|
||||
}
|
||||
nextCounter = ((DDISimulatedDevice) device).getPollDelaySec();
|
||||
}
|
||||
|
||||
device.setNextPollCounterSec(nextCounter);
|
||||
});
|
||||
eventBus.post(new NextPollCounterUpdate(devices));
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
package org.eclipse.hawkbit.simulator;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
|
||||
import org.eclipse.hawkbit.simulator.http.ControllerResource;
|
||||
@@ -24,15 +22,9 @@ import feign.Logger;
|
||||
/**
|
||||
* The simulated device factory to create either {@link DMFSimulatedDevice} or
|
||||
* {@link DDISimulatedDevice#}.
|
||||
*
|
||||
* @author Michael Hirsch
|
||||
*
|
||||
*/
|
||||
@Service
|
||||
public class SimulatedDeviceFactory {
|
||||
|
||||
private static final ScheduledExecutorService pollThreadPool = Executors.newScheduledThreadPool(4);
|
||||
|
||||
@Autowired
|
||||
private DeviceSimulatorUpdater deviceUpdater;
|
||||
|
||||
@@ -47,7 +39,8 @@ public class SimulatedDeviceFactory {
|
||||
* the protocol of the device
|
||||
* @return the created simulated device
|
||||
*/
|
||||
public AbstractSimulatedDevice createSimulatedDevice(final String id, final String tenant, final Protocol protocol) {
|
||||
public AbstractSimulatedDevice createSimulatedDevice(final String id, final String tenant,
|
||||
final Protocol protocol) {
|
||||
return createSimulatedDevice(id, tenant, protocol, 30, null, null);
|
||||
}
|
||||
|
||||
@@ -80,7 +73,7 @@ public class SimulatedDeviceFactory {
|
||||
final ControllerResource controllerResource = Feign.builder().logger(new Logger.ErrorLogger())
|
||||
.requestInterceptor(new GatewayTokenInterceptor(gatewayToken)).logLevel(Logger.Level.BASIC)
|
||||
.target(ControllerResource.class, baseEndpoint.toString());
|
||||
return new DDISimulatedDevice(id, tenant, pollDelaySec, controllerResource, pollThreadPool, deviceUpdater);
|
||||
return new DDISimulatedDevice(id, tenant, pollDelaySec, controllerResource, deviceUpdater);
|
||||
default:
|
||||
throw new IllegalArgumentException("Protocol " + protocol + " unknown");
|
||||
}
|
||||
|
||||
@@ -86,7 +86,10 @@ public class SimulationController {
|
||||
final String deviceId = name + i;
|
||||
repository.add(deviceFactory.createSimulatedDevice(deviceId, tenant, protocol, pollDelay, new URL(endpoint),
|
||||
gatewayToken));
|
||||
spSenderService.createOrUpdateThing(tenant, deviceId);
|
||||
|
||||
if (protocol == Protocol.DMF_AMQP) {
|
||||
spSenderService.createOrUpdateThing(tenant, deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
return ResponseEntity.ok("Updated " + amount + " DMF connected targets!");
|
||||
|
||||
@@ -11,10 +11,12 @@ package org.eclipse.hawkbit.simulator;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
|
||||
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -24,6 +26,7 @@ import org.springframework.stereotype.Component;
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
@ConditionalOnProperty(prefix = "hawkbit.device.simulator", name = "autostart", matchIfMissing = true)
|
||||
public class SimulatorStartup implements ApplicationListener<ContextRefreshedEvent> {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(SimulatorStartup.class);
|
||||
|
||||
@@ -52,7 +55,9 @@ public class SimulatorStartup implements ApplicationListener<ContextRefreshedEve
|
||||
LOGGER.error("Creation of simulated device at startup failed.", e);
|
||||
}
|
||||
|
||||
spSenderService.createOrUpdateThing(autostart.getTenant(), deviceId);
|
||||
if (autostart.getApi() == Protocol.DMF_AMQP) {
|
||||
spSenderService.createOrUpdateThing(autostart.getTenant(), deviceId);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* 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.simulator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Update status of the simulated update.
|
||||
*
|
||||
*/
|
||||
public class UpdateStatus {
|
||||
private ResponseStatus responseStatus;
|
||||
private List<String> statusMessages;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param responseStatus
|
||||
* of the update
|
||||
*/
|
||||
public UpdateStatus(final ResponseStatus responseStatus) {
|
||||
this.responseStatus = responseStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor including status message.
|
||||
*
|
||||
* @param responseStatus
|
||||
* of the update
|
||||
* @param message
|
||||
* of the update status
|
||||
*/
|
||||
public UpdateStatus(final ResponseStatus responseStatus, final String message) {
|
||||
this(responseStatus);
|
||||
statusMessages = new ArrayList<>();
|
||||
statusMessages.add(message);
|
||||
}
|
||||
|
||||
public ResponseStatus getResponseStatus() {
|
||||
return responseStatus;
|
||||
}
|
||||
|
||||
public void setResponseStatus(final ResponseStatus responseStatus) {
|
||||
this.responseStatus = responseStatus;
|
||||
}
|
||||
|
||||
public List<String> getStatusMessages() {
|
||||
if (statusMessages == null) {
|
||||
statusMessages = new ArrayList<>();
|
||||
}
|
||||
|
||||
return statusMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* The status to response to the hawkBit update server if an simulated
|
||||
* update process should be respond with successful or failure update.
|
||||
*/
|
||||
public enum ResponseStatus {
|
||||
/**
|
||||
* Update has been successful and response the successful update.
|
||||
*/
|
||||
SUCCESSFUL,
|
||||
|
||||
/**
|
||||
* Update has been not successful and response the error update.
|
||||
*/
|
||||
ERROR;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -39,6 +39,12 @@ public class AmqpProperties {
|
||||
*/
|
||||
private String deadLetterExchange = "simulator.deadletter";
|
||||
|
||||
/**
|
||||
* Message time to live (ttl) for the deadletter queue. Default ttl is 1
|
||||
* hour.
|
||||
*/
|
||||
private int deadLetterTtl = 60_000;
|
||||
|
||||
public String getReceiverConnectorQueueFromSp() {
|
||||
return receiverConnectorQueueFromSp;
|
||||
}
|
||||
@@ -70,4 +76,12 @@ public class AmqpProperties {
|
||||
public void setSenderForSpExchange(final String senderForSpExchange) {
|
||||
this.senderForSpExchange = senderForSpExchange;
|
||||
}
|
||||
|
||||
public int getDeadLetterTtl() {
|
||||
return deadLetterTtl;
|
||||
}
|
||||
|
||||
public void setDeadLetterTtl(final int deadLetterTtl) {
|
||||
this.deadLetterTtl = deadLetterTtl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.messaging.handler.annotation.Header;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
/**
|
||||
* Handle all incoming Messages from hawkBit update server.
|
||||
*
|
||||
@@ -88,6 +90,8 @@ public class SpReceiverService extends ReceiverService {
|
||||
if (eventHeader == null) {
|
||||
logAndThrowMessageError(message, "Event Topic is not set");
|
||||
}
|
||||
// Exception squid:S2259 - Checked before
|
||||
@SuppressWarnings({ "squid:S2259" })
|
||||
final EventTopic eventTopic = EventTopic.valueOf(eventHeader.toString());
|
||||
switch (eventTopic) {
|
||||
case DOWNLOAD_AND_INSTALL:
|
||||
@@ -109,7 +113,7 @@ public class SpReceiverService extends ReceiverService {
|
||||
final Long actionId = convertMessage(message, Long.class);
|
||||
|
||||
final SimulatedUpdate update = new SimulatedUpdate(tenant, thingId, actionId);
|
||||
spSenderService.finishUpdateProcess(update, "Simulation canceled");
|
||||
spSenderService.finishUpdateProcess(update, Lists.newArrayList("Simulation canceled"));
|
||||
}
|
||||
|
||||
private void handleUpdateProcess(final Message message, final String thingId) {
|
||||
@@ -120,19 +124,20 @@ public class SpReceiverService extends ReceiverService {
|
||||
final DownloadAndUpdateRequest downloadAndUpdateRequest = convertMessage(message,
|
||||
DownloadAndUpdateRequest.class);
|
||||
final Long actionId = downloadAndUpdateRequest.getActionId();
|
||||
final String targetSecurityToken = downloadAndUpdateRequest.getTargetSecurityToken();
|
||||
|
||||
deviceUpdater.startUpdate(tenant, thingId, actionId,
|
||||
downloadAndUpdateRequest.getSoftwareModules().get(0).getModuleVersion(), (device, actionId1) -> {
|
||||
switch (device.getResponseStatus()) {
|
||||
deviceUpdater.startUpdate(tenant, thingId, actionId, null, downloadAndUpdateRequest.getSoftwareModules(),
|
||||
targetSecurityToken, (device, actionId1) -> {
|
||||
switch (device.getUpdateStatus().getResponseStatus()) {
|
||||
case SUCCESSFUL:
|
||||
spSenderService.finishUpdateProcess(
|
||||
new SimulatedUpdate(device.getTenant(), device.getId(), actionId1),
|
||||
"Simulation complete!");
|
||||
device.getUpdateStatus().getStatusMessages());
|
||||
break;
|
||||
case ERROR:
|
||||
spSenderService.finishUpdateProcessWithError(
|
||||
new SimulatedUpdate(device.getTenant(), device.getId(), actionId1),
|
||||
"Simulation complete with error!");
|
||||
device.getUpdateStatus().getStatusMessages());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.simulator.amqp;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings;
|
||||
@@ -23,13 +24,9 @@ import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Sender service to send message to SP.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Sender service to send messages to update server.
|
||||
*/
|
||||
@Service
|
||||
public class SpSenderService extends SenderService {
|
||||
@@ -59,8 +56,9 @@ public class SpSenderService extends SenderService {
|
||||
* @param description
|
||||
* a description according the update process
|
||||
*/
|
||||
public void finishUpdateProcess(final SimulatedUpdate update, final String description) {
|
||||
final Message updateResultMessage = createUpdateResultMessage(update, ActionStatus.FINISHED, description);
|
||||
public void finishUpdateProcess(final SimulatedUpdate update, final List<String> updateResultMessages) {
|
||||
final Message updateResultMessage = createUpdateResultMessage(update, ActionStatus.FINISHED,
|
||||
updateResultMessages);
|
||||
sendMessage(spExchange, updateResultMessage);
|
||||
}
|
||||
|
||||
@@ -72,9 +70,9 @@ public class SpSenderService extends SenderService {
|
||||
* @param messageDescription
|
||||
* a description according the update process
|
||||
*/
|
||||
public void finishUpdateProcessWithError(final SimulatedUpdate update, final String messageDescription) {
|
||||
sendErrorgMessage(update, messageDescription);
|
||||
LOGGER.debug("Update process finished with error \"{}\" reported by thing {}", messageDescription,
|
||||
public void finishUpdateProcessWithError(final SimulatedUpdate update, final List<String> updateResultMessages) {
|
||||
sendErrorgMessage(update, updateResultMessages);
|
||||
LOGGER.debug("Update process finished with error \"{}\" reported by thing {}", updateResultMessages,
|
||||
update.getThingId());
|
||||
}
|
||||
|
||||
@@ -88,8 +86,8 @@ public class SpSenderService extends SenderService {
|
||||
* @param actionId
|
||||
* the ID of the action for the error message
|
||||
*/
|
||||
public void sendErrorMessage(final String tenant, final String messageDescription, final Long actionId) {
|
||||
final Message message = createActionStatusMessage(tenant, ActionStatus.ERROR, messageDescription, actionId);
|
||||
public void sendErrorMessage(final String tenant, final List<String> updateResultMessages, final Long actionId) {
|
||||
final Message message = createActionStatusMessage(tenant, ActionStatus.ERROR, updateResultMessages, actionId);
|
||||
sendMessage(spExchange, message);
|
||||
}
|
||||
|
||||
@@ -101,8 +99,8 @@ public class SpSenderService extends SenderService {
|
||||
* @param warningMessage
|
||||
* a warning description
|
||||
*/
|
||||
public void sendWarningMessage(final SimulatedUpdate update, final String warningMessage) {
|
||||
final Message message = createActionStatusMessage(update, warningMessage, ActionStatus.WARNING);
|
||||
public void sendWarningMessage(final SimulatedUpdate update, final List<String> updateResultMessages) {
|
||||
final Message message = createActionStatusMessage(update, updateResultMessages, ActionStatus.WARNING);
|
||||
sendMessage(spExchange, message);
|
||||
}
|
||||
|
||||
@@ -119,8 +117,8 @@ public class SpSenderService extends SenderService {
|
||||
* the cached value
|
||||
*/
|
||||
public void sendActionStatusMessage(final String tenant, final ActionStatus actionStatus,
|
||||
final String actionMessage, final Long actionId) {
|
||||
final Message message = createActionStatusMessage(tenant, actionStatus, actionMessage, actionId);
|
||||
final List<String> updateResultMessages, final Long actionId) {
|
||||
final Message message = createActionStatusMessage(tenant, actionStatus, updateResultMessages, actionId);
|
||||
sendMessage(message);
|
||||
|
||||
}
|
||||
@@ -162,11 +160,11 @@ public class SpSenderService extends SenderService {
|
||||
*
|
||||
* @param context
|
||||
* the current context
|
||||
* @param messageDescription
|
||||
* a description according the update process
|
||||
* @param updateResultMessages
|
||||
* a list of descriptions according the update process
|
||||
*/
|
||||
private void sendErrorgMessage(final SimulatedUpdate update, final String messageDescription) {
|
||||
final Message message = createActionStatusMessage(update, messageDescription, ActionStatus.ERROR);
|
||||
private void sendErrorgMessage(final SimulatedUpdate update, final List<String> updateResultMessages) {
|
||||
final Message message = createActionStatusMessage(update, updateResultMessages, ActionStatus.ERROR);
|
||||
sendMessage(spExchange, message);
|
||||
}
|
||||
|
||||
@@ -183,7 +181,7 @@ public class SpSenderService extends SenderService {
|
||||
* the cacheValue value
|
||||
*/
|
||||
private Message createActionStatusMessage(final String tenant, final ActionStatus actionStatus,
|
||||
final String actionMessage, final Long actionId) {
|
||||
final List<String> updateResultMessages, final Long actionId) {
|
||||
final MessageProperties messageProperties = new MessageProperties();
|
||||
final Map<String, Object> headers = messageProperties.getHeaders();
|
||||
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus();
|
||||
@@ -192,15 +190,14 @@ public class SpSenderService extends SenderService {
|
||||
headers.put(MessageHeaderKey.TENANT, tenant);
|
||||
headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
|
||||
headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON);
|
||||
if (!StringUtils.isEmpty(actionMessage)) {
|
||||
actionUpdateStatus.getMessage().add(actionMessage);
|
||||
}
|
||||
actionUpdateStatus.getMessage().addAll(updateResultMessages);
|
||||
|
||||
actionUpdateStatus.setActionId(actionId);
|
||||
return convertMessage(actionUpdateStatus, messageProperties);
|
||||
}
|
||||
|
||||
private Message createUpdateResultMessage(final SimulatedUpdate cacheValue, final ActionStatus actionStatus,
|
||||
final String updateResultMessage) {
|
||||
final List<String> updateResultMessages) {
|
||||
final MessageProperties messageProperties = new MessageProperties();
|
||||
final Map<String, Object> headers = messageProperties.getHeaders();
|
||||
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus();
|
||||
@@ -209,14 +206,14 @@ public class SpSenderService extends SenderService {
|
||||
headers.put(MessageHeaderKey.TENANT, cacheValue.getTenant());
|
||||
headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
|
||||
headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON);
|
||||
actionUpdateStatus.getMessage().add(updateResultMessage);
|
||||
actionUpdateStatus.getMessage().addAll(updateResultMessages);
|
||||
actionUpdateStatus.setActionId(cacheValue.getActionId());
|
||||
return convertMessage(actionUpdateStatus, messageProperties);
|
||||
}
|
||||
|
||||
private Message createActionStatusMessage(final SimulatedUpdate update, final String messageDescription,
|
||||
private Message createActionStatusMessage(final SimulatedUpdate update, final List<String> updateResultMessages,
|
||||
final ActionStatus status) {
|
||||
return createActionStatusMessage(update.getTenant(), status, messageDescription, update.getActionId());
|
||||
return createActionStatusMessage(update.getTenant(), status, updateResultMessages, update.getActionId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,8 +13,6 @@ import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
|
||||
/**
|
||||
* Event definition object which is published if the simulated device updated
|
||||
* its update progress.
|
||||
*
|
||||
* @author Michael Hirsch
|
||||
*
|
||||
*/
|
||||
public class ProgressUpdate {
|
||||
|
||||
@@ -8,21 +8,19 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.simulator.ui;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
|
||||
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
|
||||
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.ResponseStatus;
|
||||
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Status;
|
||||
import org.eclipse.hawkbit.simulator.DeviceSimulatorRepository;
|
||||
import org.eclipse.hawkbit.simulator.SimulatedDeviceFactory;
|
||||
import org.eclipse.hawkbit.simulator.UpdateStatus.ResponseStatus;
|
||||
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
|
||||
import org.eclipse.hawkbit.simulator.event.InitUpdate;
|
||||
import org.eclipse.hawkbit.simulator.event.NextPollCounterUpdate;
|
||||
import org.eclipse.hawkbit.simulator.event.ProgressUpdate;
|
||||
import org.eclipse.hawkbit.simulator.ui.GenerateDialog.GenerateDialogCallback;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
@@ -52,13 +50,27 @@ import com.vaadin.ui.renderers.ProgressBarRenderer;
|
||||
* Vaadin view which allows to generate devices through the DMF API and show the
|
||||
* current simulated devices in a grid with their current status and update
|
||||
* progress.
|
||||
*
|
||||
* @author Michael Hirsch
|
||||
*
|
||||
*/
|
||||
@SpringView(name = "")
|
||||
public class SimulatorView extends VerticalLayout implements View {
|
||||
|
||||
private static final String NEXT_POLL_COUNTER_SEC_COL = "nextPollCounterSec";
|
||||
|
||||
private static final String RESPONSE_STATUS_COL = "responseStatus";
|
||||
|
||||
private static final String PROTOCOL_COL = "protocol";
|
||||
|
||||
private static final String TENANT_COL = "tenant";
|
||||
|
||||
private static final String PROGRESS_COL = "progress";
|
||||
|
||||
private static final String SWVERSION_COL = "swversion";
|
||||
|
||||
private static final String STATUS_COL = "status";
|
||||
|
||||
private static final String ID_COL = "id";
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Autowired
|
||||
@@ -79,6 +91,7 @@ public class SimulatorView extends VerticalLayout implements View {
|
||||
|
||||
private BeanContainer<String, AbstractSimulatedDevice> beanContainer;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void enter(final ViewChangeEvent event) {
|
||||
eventbus.register(this);
|
||||
@@ -91,7 +104,7 @@ public class SimulatorView extends VerticalLayout implements View {
|
||||
createToolbar();
|
||||
|
||||
beanContainer = new BeanContainer<>(AbstractSimulatedDevice.class);
|
||||
beanContainer.setBeanIdProperty("id");
|
||||
beanContainer.setBeanIdProperty(ID_COL);
|
||||
|
||||
grid.setSizeFull();
|
||||
grid.setCellStyleGenerator(new CellStyleGenerator() {
|
||||
@@ -100,28 +113,28 @@ public class SimulatorView extends VerticalLayout implements View {
|
||||
|
||||
@Override
|
||||
public String getStyle(final CellReference cellReference) {
|
||||
return cellReference.getPropertyId().equals("status") ? "centeralign" : null;
|
||||
return cellReference.getPropertyId().equals(STATUS_COL) ? "centeralign" : null;
|
||||
}
|
||||
});
|
||||
|
||||
grid.setSelectionMode(SelectionMode.NONE);
|
||||
grid.setContainerDataSource(beanContainer);
|
||||
grid.appendHeaderRow().getCell("responseStatus").setComponent(responseComboBox);
|
||||
grid.setColumnOrder("id", "status", "swversion", "progress", "tenant", "protocol", "responseStatus",
|
||||
"nextPollCounterSec");
|
||||
grid.appendHeaderRow().getCell(RESPONSE_STATUS_COL).setComponent(responseComboBox);
|
||||
grid.setColumnOrder(ID_COL, STATUS_COL, SWVERSION_COL, PROGRESS_COL, TENANT_COL, PROTOCOL_COL,
|
||||
RESPONSE_STATUS_COL, NEXT_POLL_COUNTER_SEC_COL);
|
||||
// header widths
|
||||
grid.getColumn("status").setMaximumWidth(80);
|
||||
grid.getColumn("protocol").setMaximumWidth(180);
|
||||
grid.getColumn("responseStatus").setMaximumWidth(240);
|
||||
grid.getColumn("nextPollCounterSec").setMaximumWidth(210);
|
||||
grid.getColumn(STATUS_COL).setMaximumWidth(80);
|
||||
grid.getColumn(PROTOCOL_COL).setMaximumWidth(180);
|
||||
grid.getColumn(RESPONSE_STATUS_COL).setMaximumWidth(240);
|
||||
grid.getColumn(NEXT_POLL_COUNTER_SEC_COL).setMaximumWidth(210);
|
||||
|
||||
grid.getColumn("nextPollCounterSec").setHeaderCaption("Next Poll in (sec)");
|
||||
grid.getColumn("swversion").setHeaderCaption("SW Version");
|
||||
grid.getColumn("responseStatus").setHeaderCaption("Response Update Status");
|
||||
grid.getColumn("progress").setRenderer(new ProgressBarRenderer());
|
||||
grid.getColumn("protocol").setConverter(createProtocolConverter());
|
||||
grid.getColumn("status").setRenderer(new HtmlRenderer(), createStatusConverter());
|
||||
grid.removeColumn("tenant");
|
||||
grid.getColumn(NEXT_POLL_COUNTER_SEC_COL).setHeaderCaption("Next Poll in (sec)");
|
||||
grid.getColumn(SWVERSION_COL).setHeaderCaption("SW Version");
|
||||
grid.getColumn(RESPONSE_STATUS_COL).setHeaderCaption("Response Update Status");
|
||||
grid.getColumn(PROGRESS_COL).setRenderer(new ProgressBarRenderer());
|
||||
grid.getColumn(PROTOCOL_COL).setConverter(createProtocolConverter());
|
||||
grid.getColumn(STATUS_COL).setRenderer(new HtmlRenderer(), createStatusConverter());
|
||||
grid.removeColumn(TENANT_COL);
|
||||
|
||||
// grid combobox
|
||||
responseComboBox.setItemIcon(ResponseStatus.SUCCESSFUL, FontAwesome.CHECK_CIRCLE);
|
||||
@@ -129,8 +142,8 @@ public class SimulatorView extends VerticalLayout implements View {
|
||||
responseComboBox.setNullSelectionAllowed(false);
|
||||
responseComboBox.setValue(ResponseStatus.SUCCESSFUL);
|
||||
responseComboBox.addValueChangeListener(valueChangeEvent -> {
|
||||
beanContainer.getItemIds().forEach(itemId -> beanContainer.getItem(itemId).getItemProperty("responseStatus")
|
||||
.setValue(valueChangeEvent.getProperty().getValue()));
|
||||
beanContainer.getItemIds().forEach(itemId -> beanContainer.getItem(itemId)
|
||||
.getItemProperty(RESPONSE_STATUS_COL).setValue(valueChangeEvent.getProperty().getValue()));
|
||||
});
|
||||
|
||||
// add all components
|
||||
@@ -141,7 +154,7 @@ public class SimulatorView extends VerticalLayout implements View {
|
||||
setExpandRatio(grid, 1.0F);
|
||||
|
||||
// load beans
|
||||
repository.getAll().forEach(device -> beanContainer.addBean(device));
|
||||
repository.getAll().forEach(beanContainer::addBean);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -150,21 +163,16 @@ public class SimulatorView extends VerticalLayout implements View {
|
||||
eventbus.unregister(this);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Subscribe
|
||||
public void pollCounterUpdate(final NextPollCounterUpdate update) {
|
||||
final List<AbstractSimulatedDevice> devices = update.getDevices();
|
||||
this.getUI().access(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
devices.forEach(device -> {
|
||||
final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId());
|
||||
if (item != null) {
|
||||
item.getItemProperty("nextPollCounterSec").setValue(device.getNextPollCounterSec());
|
||||
}
|
||||
});
|
||||
this.getUI().access(() -> devices.forEach(device -> {
|
||||
final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId());
|
||||
if (item != null) {
|
||||
item.getItemProperty(NEXT_POLL_COUNTER_SEC_COL).setValue(device.getNextPollCounterSec());
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,21 +181,19 @@ public class SimulatorView extends VerticalLayout implements View {
|
||||
* @param update
|
||||
* the update event posted on the event bus
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Subscribe
|
||||
public void initUpdate(final InitUpdate update) {
|
||||
final AbstractSimulatedDevice device = update.getDevice();
|
||||
this.getUI().access(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId());
|
||||
if (item != null) {
|
||||
item.getItemProperty("progress").setValue(device.getProgress());
|
||||
item.getItemProperty("status").setValue(Status.PEDNING);
|
||||
item.getItemProperty("swversion").setValue(device.getSwversion());
|
||||
}
|
||||
|
||||
this.getUI().access(() -> {
|
||||
final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId());
|
||||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
item.getItemProperty(PROGRESS_COL).setValue(device.getProgress());
|
||||
item.getItemProperty(STATUS_COL).setValue(Status.PEDNING);
|
||||
item.getItemProperty(SWVERSION_COL).setValue(device.getSwversion());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -197,35 +203,37 @@ public class SimulatorView extends VerticalLayout implements View {
|
||||
* @param update
|
||||
* the update event posted on the event bus
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Subscribe
|
||||
public void progessUpdate(final ProgressUpdate update) {
|
||||
final AbstractSimulatedDevice device = update.getDevice();
|
||||
this.getUI().access(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId());
|
||||
if (item != null) {
|
||||
item.getItemProperty("progress").setValue(device.getProgress());
|
||||
if (device.getProgress() >= 1) {
|
||||
switch (device.getResponseStatus()) {
|
||||
case SUCCESSFUL:
|
||||
item.getItemProperty("status").setValue(Status.FINISH);
|
||||
break;
|
||||
case ERROR:
|
||||
item.getItemProperty("status").setValue(Status.ERROR);
|
||||
break;
|
||||
default:
|
||||
item.getItemProperty("status").setValue(Status.UNKNWON);
|
||||
}
|
||||
} else {
|
||||
item.getItemProperty("status").setValue(Status.PEDNING);
|
||||
}
|
||||
}
|
||||
|
||||
this.getUI().access(() -> {
|
||||
final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId());
|
||||
if (item != null) {
|
||||
item.getItemProperty(PROGRESS_COL).setValue(device.getProgress());
|
||||
setStatusColumn(device, item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void setStatusColumn(final AbstractSimulatedDevice device, final BeanItem<AbstractSimulatedDevice> item) {
|
||||
if (device.getProgress() >= 1) {
|
||||
switch (device.getUpdateStatus().getResponseStatus()) {
|
||||
case SUCCESSFUL:
|
||||
item.getItemProperty(STATUS_COL).setValue(Status.FINISH);
|
||||
break;
|
||||
case ERROR:
|
||||
item.getItemProperty(STATUS_COL).setValue(Status.ERROR);
|
||||
break;
|
||||
default:
|
||||
item.getItemProperty(STATUS_COL).setValue(Status.UNKNWON);
|
||||
}
|
||||
} else {
|
||||
item.getItemProperty(STATUS_COL).setValue(Status.PEDNING);
|
||||
}
|
||||
}
|
||||
|
||||
private void createToolbar() {
|
||||
final Button createDevicesButton = new Button("generate...");
|
||||
createDevicesButton.setIcon(FontAwesome.GEARS);
|
||||
@@ -246,18 +254,15 @@ public class SimulatorView extends VerticalLayout implements View {
|
||||
}
|
||||
|
||||
private void openGenerateDialog() {
|
||||
UI.getCurrent().addWindow(new GenerateDialog(new GenerateDialogCallback() {
|
||||
@Override
|
||||
public void okButton(final String namePrefix, final String tenant, final int amount, final int pollDelay,
|
||||
final URL basePollUrl, final String gatewayToken, final Protocol protocol) {
|
||||
for (int index = 0; index < amount; index++) {
|
||||
final String deviceId = namePrefix + index;
|
||||
beanContainer.addBean(repository.add(deviceFactory.createSimulatedDevice(deviceId,
|
||||
tenant.toLowerCase(), protocol, pollDelay, basePollUrl, gatewayToken)));
|
||||
spSenderService.createOrUpdateThing(tenant, deviceId);
|
||||
}
|
||||
}
|
||||
}));
|
||||
UI.getCurrent().addWindow(
|
||||
new GenerateDialog((namePrefix, tenant, amount, pollDelay, basePollUrl, gatewayToken, protocol) -> {
|
||||
for (int index = 0; index < amount; index++) {
|
||||
final String deviceId = namePrefix + index;
|
||||
beanContainer.addBean(repository.add(deviceFactory.createSimulatedDevice(deviceId,
|
||||
tenant.toLowerCase(), protocol, pollDelay, basePollUrl, gatewayToken)));
|
||||
spSenderService.createOrUpdateThing(tenant, deviceId);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private Converter<String, Protocol> createProtocolConverter() {
|
||||
|
||||
@@ -4,16 +4,20 @@ The hawkBit example application is a standalone spring-boot application with an
|
||||
We have have described several options for you to get access to the example.
|
||||
|
||||
## Try out the example application in our hawkBit sandbox on Bluemix
|
||||
- try out Management UI https://hawkbit.eu-gb.mybluemix.net/UI
|
||||
- try out Management API https://hawkbit.eu-gb.mybluemix.net/rest/v1/targets (don't forget basic auth header)
|
||||
- try out DDI API https://hawkbit.eu-gb.mybluemix.net/DEFAULT/controller/v1/MYTESTDEVICE
|
||||
- try out Management UI https://hawkbit.eu-gb.mybluemix.net/UI (username: admin, passwd: admin)
|
||||
- try out Management API https://hawkbit.eu-gb.mybluemix.net/rest/v1/targets (don't forget basic auth header; username: admin, passwd: admin)
|
||||
- try out DDI API https://hawkbit.eu-gb.mybluemix.net/DEFAULT/controller/v1/MYTESTDEVICE (authentication disabled)
|
||||
|
||||
## On your own workstation
|
||||
### Run
|
||||
```
|
||||
java -jar examples/hawkbit-example-app/target/hawkbit-example-app-*-SNAPSHOT.jar
|
||||
```
|
||||
|
||||
_(Note: you have to add the JDBC driver also to your class path if you intend to use another database than H2.)_
|
||||
|
||||
Or:
|
||||
|
||||
```
|
||||
run org eclipse.hawkbit.app.Start
|
||||
```
|
||||
|
||||
@@ -26,6 +26,8 @@ import org.springframework.context.annotation.Import;
|
||||
@EnableHawkbitManagedSecurityConfiguration
|
||||
@EnableRestResources
|
||||
@EnableDirectDeviceApi
|
||||
// Exception squid:S1118 - Spring boot standard behavior
|
||||
@SuppressWarnings({ "squid:S1118" })
|
||||
public class Start {
|
||||
|
||||
/**
|
||||
@@ -34,6 +36,8 @@ public class Start {
|
||||
* @param args
|
||||
* the VM arguments.
|
||||
*/
|
||||
// Exception squid:S2095 - Spring boot standard behavior
|
||||
@SuppressWarnings({ "squid:S2095" })
|
||||
public static void main(final String[] args) {
|
||||
SpringApplication.run(Start.class, args);
|
||||
}
|
||||
|
||||
@@ -8,3 +8,9 @@
|
||||
#
|
||||
|
||||
vaadin.servlet.productionMode=true
|
||||
|
||||
hawkbit.artifact.url.coap.enabled=false
|
||||
hawkbit.artifact.url.http.enabled=false
|
||||
hawkbit.artifact.url.https.enabled=true
|
||||
hawkbit.artifact.url.https.pattern={protocol}://{hostname}/{tenant}/controller/v1/{targetId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}
|
||||
hawkbit.artifact.url.https.hostname=hawkbit.eu-gb.mybluemix.net
|
||||
@@ -7,21 +7,28 @@
|
||||
# http://www.eclipse.org/legal/epl-v10.html
|
||||
#
|
||||
|
||||
# DDI authentication configuration
|
||||
hawkbit.server.ddi.security.authentication.anonymous.enabled=true
|
||||
hawkbit.server.ddi.security.authentication.targettoken.enabled=false
|
||||
hawkbit.server.ddi.security.authentication.gatewaytoken.enabled=false
|
||||
hawkbit.server.ddi.security.authentication.targettoken.enabled=true
|
||||
hawkbit.server.ddi.security.authentication.gatewaytoken.enabled=true
|
||||
|
||||
spring.profiles.active=amqp
|
||||
# Download URL generation config
|
||||
hawkbit.artifact.url.coap.enabled=false
|
||||
hawkbit.artifact.url.http.enabled=true
|
||||
hawkbit.artifact.url.http.port=8080
|
||||
hawkbit.artifact.url.https.enabled=false
|
||||
|
||||
## Vaadin configuration
|
||||
vaadin.servlet.productionMode=false
|
||||
|
||||
## Configuration for RabbitMQ integration
|
||||
## Configuration for DMF/RabbitMQ integration
|
||||
spring.profiles.active=amqp
|
||||
spring.rabbitmq.username=guest
|
||||
spring.rabbitmq.password=guest
|
||||
spring.rabbitmq.virtualHost=/
|
||||
spring.rabbitmq.host=localhost
|
||||
spring.rabbitmq.port=5672
|
||||
hawkbit.dmf.rabbitmq.deadLetterQueue=dmf_connector_deadletter
|
||||
hawkbit.dmf.rabbitmq.deadLetterQueue=dmf_connector_deadletter_ttl
|
||||
hawkbit.dmf.rabbitmq.deadLetterExchange=dmf.connector.deadletter
|
||||
hawkbit.dmf.rabbitmq.receiverQueue=dmf_receiver
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
<artifactId>hawkbit-mgmt-api-client</artifactId>
|
||||
<name>hawkBit Management API example client</name>
|
||||
<name>hawkBit :: Management API example client</name>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
@@ -18,8 +18,9 @@ import com.google.common.collect.Lists;
|
||||
/**
|
||||
* Builder pattern for building {@link DistributionSetRequestBodyPost}.
|
||||
*/
|
||||
// Exception squid:S1701 - builder pattern
|
||||
@SuppressWarnings({ "squid:S1701" })
|
||||
public class DistributionSetBuilder {
|
||||
|
||||
private String name;
|
||||
private String version;
|
||||
private String type;
|
||||
|
||||
@@ -21,8 +21,9 @@ import com.google.common.collect.Lists;
|
||||
* Builder pattern for building {@link DistributionSetTypeRequestBodyPost}.
|
||||
*
|
||||
*/
|
||||
// Exception squid:S1701 - builder pattern
|
||||
@SuppressWarnings({ "squid:S1701" })
|
||||
public class DistributionSetTypeBuilder {
|
||||
|
||||
private String key;
|
||||
private String name;
|
||||
private final List<SoftwareModuleTypeAssigmentRest> mandatorymodules = Lists.newArrayList();
|
||||
|
||||
@@ -17,6 +17,8 @@ import org.eclipse.hawkbit.rest.resource.model.rollout.RolloutRestRequestBody;
|
||||
* Builder pattern for building {@link RolloutRestRequestBody}.
|
||||
*
|
||||
*/
|
||||
// Exception squid:S1701 - builder pattern
|
||||
@SuppressWarnings({ "squid:S1701" })
|
||||
public class RolloutBuilder {
|
||||
|
||||
private String name;
|
||||
|
||||
@@ -21,6 +21,8 @@ import com.google.common.collect.Lists;
|
||||
* Builder pattern for building {@link SoftwareModuleRequestBodyPost}.
|
||||
*
|
||||
*/
|
||||
// Exception squid:S1701 - builder pattern
|
||||
@SuppressWarnings({ "squid:S1701" })
|
||||
public class SoftwareModuleBuilder {
|
||||
|
||||
private String name;
|
||||
|
||||
@@ -21,6 +21,8 @@ import com.google.common.collect.Lists;
|
||||
* Builder pattern for building {@link SoftwareModuleRequestBodyPost}.
|
||||
*
|
||||
*/
|
||||
// Exception squid:S1701 - builder pattern
|
||||
@SuppressWarnings({ "squid:S1701" })
|
||||
public class SoftwareModuleTypeBuilder {
|
||||
|
||||
private String key;
|
||||
|
||||
@@ -19,6 +19,8 @@ import com.google.common.collect.Lists;
|
||||
* Builder pattern for building {@link TagRequestBodyPut}.
|
||||
*
|
||||
*/
|
||||
// Exception squid:S1701 - builder pattern
|
||||
@SuppressWarnings({ "squid:S1701" })
|
||||
public class TagBuilder {
|
||||
|
||||
private String name;
|
||||
|
||||
@@ -21,6 +21,8 @@ import com.google.common.collect.Lists;
|
||||
* Builder pattern for building {@link TargetRequestBody}.
|
||||
*
|
||||
*/
|
||||
// Exception squid:S1701 - builder pattern
|
||||
@SuppressWarnings({ "squid:S1701" })
|
||||
public class TargetBuilder {
|
||||
|
||||
private String controllerId;
|
||||
|
||||
@@ -8,11 +8,14 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.artifact.repository;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.security.DigestOutputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
@@ -122,7 +125,11 @@ public class ArtifactStore implements ArtifactRepository {
|
||||
LOGGER.debug("storing file {} of content {}", filename, contentType);
|
||||
tempFile = File.createTempFile("uploadFile", null);
|
||||
try (final FileOutputStream os = new FileOutputStream(tempFile)) {
|
||||
return store(content, contentType, os, tempFile, hash);
|
||||
try (BufferedOutputStream bos = new BufferedOutputStream(os)) {
|
||||
try (BufferedInputStream bis = new BufferedInputStream(content)) {
|
||||
return store(content, contentType, bos, tempFile, hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (final IOException | MongoException e1) {
|
||||
throw new ArtifactStoreException(e1.getMessage(), e1);
|
||||
@@ -162,7 +169,7 @@ public class ArtifactStore implements ArtifactRepository {
|
||||
|
||||
}
|
||||
|
||||
private DbArtifact store(final InputStream content, final String contentType, final FileOutputStream os,
|
||||
private DbArtifact store(final InputStream content, final String contentType, final OutputStream os,
|
||||
final File tempFile, final DbArtifactHash hash) {
|
||||
final GridFsArtifact storedArtifact;
|
||||
try {
|
||||
@@ -185,7 +192,8 @@ public class ArtifactStore implements ArtifactRepository {
|
||||
throw new ArtifactStoreException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
if (hash != null && hash.getMd5() != null && !storedArtifact.getHashes().getMd5().equals(hash.getMd5())) {
|
||||
if (hash != null && hash.getMd5() != null
|
||||
&& !storedArtifact.getHashes().getMd5().equalsIgnoreCase(hash.getMd5())) {
|
||||
throw new HashNotMatchException("The given md5 hash " + hash.getMd5()
|
||||
+ " not matching the calculated md5 hash " + storedArtifact.getHashes().getMd5(),
|
||||
HashNotMatchException.MD5);
|
||||
@@ -195,14 +203,14 @@ public class ArtifactStore implements ArtifactRepository {
|
||||
|
||||
}
|
||||
|
||||
private static String computeSHA1Hash(final InputStream stream, final FileOutputStream os,
|
||||
final String providedSHA1Sum) throws NoSuchAlgorithmException, IOException {
|
||||
private static String computeSHA1Hash(final InputStream stream, final OutputStream os, final String providedSHA1Sum)
|
||||
throws NoSuchAlgorithmException, IOException {
|
||||
String sha1Hash;
|
||||
// compute digest
|
||||
final MessageDigest md = MessageDigest.getInstance("SHA-1");
|
||||
final DigestOutputStream dos = new DigestOutputStream(os, md);
|
||||
ByteStreams.copy(stream, dos);
|
||||
dos.close();
|
||||
try (final DigestOutputStream dos = new DigestOutputStream(os, md)) {
|
||||
ByteStreams.copy(stream, dos);
|
||||
}
|
||||
sha1Hash = BaseEncoding.base16().lowerCase().encode(md.digest());
|
||||
if (providedSHA1Sum != null && !providedSHA1Sum.equalsIgnoreCase(sha1Hash)) {
|
||||
throw new HashNotMatchException(
|
||||
|
||||
@@ -71,6 +71,8 @@ public class MongoConfiguration extends AbstractMongoConfiguration {
|
||||
@Override
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
// Closed by pre-destroy
|
||||
@SuppressWarnings({ "squid:S2095" })
|
||||
public Mongo mongo() throws UnknownHostException {
|
||||
final MongoClientURI uri = new MongoClientURI(properties.getUri(), createBuilderOutOfOptions(options));
|
||||
|
||||
|
||||
@@ -42,6 +42,6 @@ hawkbit.controller.minPollingTime=00:00:30
|
||||
|
||||
|
||||
# Configuration for RabbitMQ integration
|
||||
hawkbit.dmf.rabbitmq.deadLetterQueue=dmf_connector_deadletter
|
||||
hawkbit.dmf.rabbitmq.deadLetterQueue=dmf_connector_deadletter_ttl
|
||||
hawkbit.dmf.rabbitmq.deadLetterExchange=dmf.connector.deadletter
|
||||
hawkbit.dmf.rabbitmq.receiverQueue=dmf_receiver
|
||||
3
hawkbit-core/README.md
Normal file
3
hawkbit-core/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# hawkBit Core
|
||||
|
||||
Various internal interfaces and utility classes.
|
||||
@@ -22,11 +22,10 @@ import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
* This resource bundles using specified basenames, to resource loading. This
|
||||
* MessageSource implementation supports more than 1 properties file with the
|
||||
* same name. All properties files will be merged.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class DistributedResourceBundleMessageSource extends ReloadableResourceBundleMessageSource {
|
||||
// Exception squid:S2387 - Follows our upper case convention
|
||||
@SuppressWarnings({ "squid:S2387" })
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DistributedResourceBundleMessageSource.class);
|
||||
private static final String PROPERTIES_SUFFIX = ".properties";
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
@@ -13,7 +13,6 @@ package org.eclipse.hawkbit.api;
|
||||
* URLs to specific artifacts.
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ArtifactUrlHandler {
|
||||
|
||||
/**
|
||||
@@ -34,4 +33,11 @@ public interface ArtifactUrlHandler {
|
||||
*/
|
||||
String getUrl(String controllerId, final Long softwareModuleId, final String filename, final String sha1Hash,
|
||||
final UrlProtocol protocol);
|
||||
|
||||
/**
|
||||
* @param protocol
|
||||
* to check support for
|
||||
* @return <code>true</code> of the handler supports given protocol.
|
||||
*/
|
||||
boolean protocolSupported(UrlProtocol protocol);
|
||||
}
|
||||
|
||||
@@ -78,6 +78,12 @@ public class ArtifactUrlHandlerProperties {
|
||||
* @return the pattern to build the URL.
|
||||
*/
|
||||
String getPattern();
|
||||
|
||||
/**
|
||||
* @return <code>true</code> if the {@link ProtocolProperties} is
|
||||
* enabled.
|
||||
*/
|
||||
boolean isEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,6 +99,20 @@ public class ArtifactUrlHandlerProperties {
|
||||
*/
|
||||
private String pattern = "{protocol}://{hostname}:{port}/{tenant}/controller/v1/{targetId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}";
|
||||
|
||||
/**
|
||||
* Enables HTTP URI generation in DDI and DMF.
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(final boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHostname() {
|
||||
return hostname;
|
||||
@@ -143,6 +163,20 @@ public class ArtifactUrlHandlerProperties {
|
||||
*/
|
||||
private String pattern = "{protocol}://{hostname}:{port}/{tenant}/controller/v1/{targetId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}";
|
||||
|
||||
/**
|
||||
* Enables HTTPS URI generation in DDI and DMF.
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(final boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHostname() {
|
||||
return hostname;
|
||||
@@ -193,6 +227,20 @@ public class ArtifactUrlHandlerProperties {
|
||||
*/
|
||||
private String pattern = "{protocol}://{ip}:{port}/fw/{tenant}/{targetId}/sha1/{artifactSHA1}";
|
||||
|
||||
/**
|
||||
* Enables CoAP URI generation in DMF.
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(final boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHostname() {
|
||||
return hostname;
|
||||
|
||||
@@ -84,4 +84,15 @@ public class PropertyBasedArtifactUrlHandler implements ArtifactUrlHandler {
|
||||
return replaceMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean protocolSupported(final UrlProtocol protocol) {
|
||||
final String protocolString = protocol.name().toLowerCase();
|
||||
final ProtocolProperties properties = urlHandlerProperties.getProperties(protocolString);
|
||||
if (properties == null || properties.getPattern() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return properties.isEnabled();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@ public class TenantConfigurationPollingDurationValidator implements TenantConfig
|
||||
}
|
||||
|
||||
@Override
|
||||
// Exception squid:S1166 - Hide origin exception
|
||||
@SuppressWarnings({ "squid:S1166" })
|
||||
public void validate(final Object tenantConfigurationObject) {
|
||||
TenantConfigurationValidator.super.validate(tenantConfigurationObject);
|
||||
final String tenantConfigurationString = (String) tenantConfigurationObject;
|
||||
|
||||
@@ -8,9 +8,6 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.amqp;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings;
|
||||
import org.springframework.amqp.core.Binding;
|
||||
import org.springframework.amqp.core.BindingBuilder;
|
||||
@@ -18,6 +15,7 @@ import org.springframework.amqp.core.FanoutExchange;
|
||||
import org.springframework.amqp.core.Queue;
|
||||
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
|
||||
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
||||
import org.springframework.amqp.rabbit.core.RabbitAdmin;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
|
||||
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
||||
@@ -31,15 +29,31 @@ import org.springframework.context.annotation.Bean;
|
||||
* {@code amqp} to use a AMQP for communication with SP enabled devices.
|
||||
*
|
||||
*/
|
||||
@EnableConfigurationProperties(AmqpProperties.class)
|
||||
@EnableConfigurationProperties({ AmqpProperties.class, AmqpDeadletterProperties.class })
|
||||
public class AmqpConfiguration {
|
||||
|
||||
@Autowired
|
||||
protected AmqpProperties amqpProperties;
|
||||
|
||||
@Autowired
|
||||
protected AmqpDeadletterProperties amqpDeadletterProperties;
|
||||
|
||||
@Autowired
|
||||
private ConnectionFactory connectionFactory;
|
||||
|
||||
/**
|
||||
* Create a {@link RabbitAdmin} and ignore declaration exceptions.
|
||||
* {@link RabbitAdmin#setIgnoreDeclarationExceptions(boolean)}
|
||||
*
|
||||
* @return the bean
|
||||
*/
|
||||
@Bean
|
||||
public RabbitAdmin rabbitAdmin() {
|
||||
final RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
|
||||
rabbitAdmin.setIgnoreDeclarationExceptions(true);
|
||||
return rabbitAdmin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to set the Jackson2JsonMessageConverter.
|
||||
*
|
||||
@@ -59,7 +73,8 @@ public class AmqpConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
public Queue receiverQueue() {
|
||||
return new Queue(amqpProperties.getReceiverQueue(), true, false, false, getDeadLetterExchangeArgs());
|
||||
return new Queue(amqpProperties.getReceiverQueue(), true, false, false,
|
||||
amqpDeadletterProperties.getDeadLetterExchangeArgs(amqpProperties.getDeadLetterExchange()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,7 +94,7 @@ public class AmqpConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
public Queue deadLetterQueue() {
|
||||
return new Queue(amqpProperties.getDeadLetterQueue());
|
||||
return amqpDeadletterProperties.createDeadletterQueue(amqpProperties.getDeadLetterQueue());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,10 +164,4 @@ public class AmqpConfiguration {
|
||||
return containerFactory;
|
||||
}
|
||||
|
||||
private Map<String, Object> getDeadLetterExchangeArgs() {
|
||||
final Map<String, Object> args = new HashMap<>();
|
||||
args.put("x-dead-letter-exchange", amqpProperties.getDeadLetterExchange());
|
||||
return args;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
|
||||
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
|
||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.security.CoapAnonymousPreAuthenticatedFilter;
|
||||
import org.eclipse.hawkbit.security.ControllerPreAuthenticateSecurityTokenFilter;
|
||||
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedAnonymousDownload;
|
||||
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedAnonymousFilter;
|
||||
@@ -97,7 +96,6 @@ public class AmqpControllerAuthentfication {
|
||||
filterChain.add(anonymousDownloadFilter);
|
||||
|
||||
filterChain.add(new ControllerPreAuthenticatedAnonymousFilter(ddiSecruityProperties));
|
||||
filterChain.add(new CoapAnonymousPreAuthenticatedFilter());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.amqp;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.amqp.core.Queue;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Bean which holds the necessary properties for configuring the AMQP deadletter
|
||||
* queue.
|
||||
*/
|
||||
@ConfigurationProperties("hawkbit.dmf.rabbitmq.deadLetter")
|
||||
public class AmqpDeadletterProperties {
|
||||
|
||||
/**
|
||||
* Message time to live (ttl) for the deadletter queue. Default ttl is 3
|
||||
* weeks.
|
||||
*/
|
||||
private long ttl = Duration.ofDays(21).toMillis();
|
||||
|
||||
/**
|
||||
* Return the deadletter arguments.
|
||||
*
|
||||
* @param exchange
|
||||
* the deadletter exchange
|
||||
* @return map which holds the properties
|
||||
*/
|
||||
public Map<String, Object> getDeadLetterExchangeArgs(final String exchange) {
|
||||
final Map<String, Object> args = new HashMap<>();
|
||||
args.put("x-dead-letter-exchange", exchange);
|
||||
return args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a deadletter queue with ttl for messages
|
||||
*
|
||||
* @param queueName
|
||||
* the deadlette queue name
|
||||
* @return the deadletter queue
|
||||
*/
|
||||
public Queue createDeadletterQueue(final String queueName) {
|
||||
return new Queue(queueName, true, false, false, getTTLArgs());
|
||||
}
|
||||
|
||||
private Map<String, Object> getTTLArgs() {
|
||||
final Map<String, Object> args = new HashMap<>();
|
||||
args.put("x-message-ttl", getTtl());
|
||||
return args;
|
||||
}
|
||||
|
||||
public long getTtl() {
|
||||
return ttl;
|
||||
}
|
||||
|
||||
public void setTtl(final long ttl) {
|
||||
this.ttl = ttl;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -82,6 +82,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
.getSoftwareModules();
|
||||
final DownloadAndUpdateRequest downloadAndUpdateRequest = new DownloadAndUpdateRequest();
|
||||
downloadAndUpdateRequest.setActionId(targetAssignDistributionSetEvent.getActionId());
|
||||
downloadAndUpdateRequest.setTargetSecurityToken(targetAssignDistributionSetEvent.getTargetToken());
|
||||
|
||||
for (final org.eclipse.hawkbit.repository.model.SoftwareModule softwareModule : modules) {
|
||||
final SoftwareModule amqpSoftwareModule = convertToAmqpSoftwareModule(controllerId, softwareModule);
|
||||
@@ -154,18 +155,26 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
private Artifact convertArtifact(final String targetId, final LocalArtifact localArtifact) {
|
||||
final Artifact artifact = new Artifact();
|
||||
|
||||
artifact.getUrls().put(Artifact.UrlProtocol.COAP,
|
||||
artifactUrlHandler.getUrl(targetId, localArtifact.getSoftwareModule().getId(),
|
||||
localArtifact.getFilename(), localArtifact.getSha1Hash(), UrlProtocol.COAP));
|
||||
artifact.getUrls().put(Artifact.UrlProtocol.HTTP,
|
||||
artifactUrlHandler.getUrl(targetId, localArtifact.getSoftwareModule().getId(),
|
||||
localArtifact.getFilename(), localArtifact.getSha1Hash(), UrlProtocol.HTTP));
|
||||
artifact.getUrls().put(Artifact.UrlProtocol.HTTPS,
|
||||
artifactUrlHandler.getUrl(targetId, localArtifact.getSoftwareModule().getId(),
|
||||
localArtifact.getFilename(), localArtifact.getSha1Hash(), UrlProtocol.HTTPS));
|
||||
if (artifactUrlHandler.protocolSupported(UrlProtocol.COAP)) {
|
||||
artifact.getUrls().put(Artifact.UrlProtocol.COAP,
|
||||
artifactUrlHandler.getUrl(targetId, localArtifact.getSoftwareModule().getId(),
|
||||
localArtifact.getFilename(), localArtifact.getSha1Hash(), UrlProtocol.COAP));
|
||||
}
|
||||
|
||||
if (artifactUrlHandler.protocolSupported(UrlProtocol.HTTP)) {
|
||||
artifact.getUrls().put(Artifact.UrlProtocol.HTTP,
|
||||
artifactUrlHandler.getUrl(targetId, localArtifact.getSoftwareModule().getId(),
|
||||
localArtifact.getFilename(), localArtifact.getSha1Hash(), UrlProtocol.HTTP));
|
||||
}
|
||||
|
||||
if (artifactUrlHandler.protocolSupported(UrlProtocol.HTTPS)) {
|
||||
artifact.getUrls().put(Artifact.UrlProtocol.HTTPS,
|
||||
artifactUrlHandler.getUrl(targetId, localArtifact.getSoftwareModule().getId(),
|
||||
localArtifact.getFilename(), localArtifact.getSha1Hash(), UrlProtocol.HTTPS));
|
||||
}
|
||||
|
||||
artifact.setFilename(localArtifact.getFilename());
|
||||
artifact.setHashes(new ArtifactHash(localArtifact.getSha1Hash(), null));
|
||||
artifact.setHashes(new ArtifactHash(localArtifact.getSha1Hash(), localArtifact.getMd5Hash()));
|
||||
artifact.setSize(localArtifact.getSize());
|
||||
return artifact;
|
||||
}
|
||||
|
||||
@@ -166,20 +166,12 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
final LocalArtifact localArtifact = findLocalArtifactByFileResource(fileResource);
|
||||
|
||||
if (localArtifact == null) {
|
||||
LOG.info("target {} requested file resource {} which does not exists to download",
|
||||
secruityToken.getControllerId(), fileResource);
|
||||
throw new EntityNotFoundException();
|
||||
}
|
||||
|
||||
// check action for this download purposes, the method will throw an
|
||||
// EntityNotFoundException in case the controller is not allowed to
|
||||
// download this file because it's not assigned to an action and not
|
||||
// assigned to this controller. Otherwise no controllerId is set =
|
||||
// anonymous download
|
||||
if (secruityToken.getControllerId() != null) {
|
||||
final Action action = controllerManagement.getActionForDownloadByTargetAndSoftwareModule(
|
||||
secruityToken.getControllerId(), localArtifact.getSoftwareModule());
|
||||
LOG.info("Found action for download authentication request action: {}, resource: {}", action,
|
||||
secruityToken.getFileResource());
|
||||
}
|
||||
checkIfArtifactIsAssignedToTarget(secruityToken, localArtifact);
|
||||
|
||||
final Artifact artifact = convertDbArtifact(artifactManagement.loadLocalArtifactBinary(localArtifact));
|
||||
if (artifact == null) {
|
||||
@@ -213,6 +205,35 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
return getMessageConverter().toMessage(authentificationResponse, messageProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* check action for this download purposes, the method will throw an
|
||||
* EntityNotFoundException in case the controller is not allowed to download
|
||||
* this file because it's not assigned to an action and not assigned to this
|
||||
* controller. Otherwise no controllerId is set = anonymous download
|
||||
*
|
||||
* @param secruityToken
|
||||
* the security token which holds the target ID to check on
|
||||
* @param localArtifact
|
||||
* the local artifact to verify if the given target is allowed to
|
||||
* download this artifact
|
||||
*/
|
||||
private void checkIfArtifactIsAssignedToTarget(final TenantSecurityToken secruityToken,
|
||||
final LocalArtifact localArtifact) {
|
||||
final String controllerId = secruityToken.getControllerId();
|
||||
if (controllerId == null) {
|
||||
LOG.info("anonymous download no authentication check for artifact {}", localArtifact);
|
||||
return;
|
||||
}
|
||||
LOG.debug("no anonymous download request, doing authentication check for target {} and artifact {}",
|
||||
controllerId, localArtifact);
|
||||
if (!controllerManagement.hasTargetArtifactAssigned(controllerId, localArtifact)) {
|
||||
LOG.info("target {} tried to download artifact {} which is not assigned to the target", controllerId,
|
||||
localArtifact);
|
||||
throw new EntityNotFoundException();
|
||||
}
|
||||
LOG.info("download security check for target {} and artifact {} granted", controllerId, localArtifact);
|
||||
}
|
||||
|
||||
private LocalArtifact findLocalArtifactByFileResource(final FileResource fileResource) {
|
||||
if (fileResource.getSha1() != null) {
|
||||
return artifactManagement.findFirstLocalArtifactsBySHA1(fileResource.getSha1());
|
||||
@@ -284,7 +305,8 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
final List<SoftwareModule> softwareModuleList = controllerManagement
|
||||
.findSoftwareModulesByDistributionSet(distributionSet);
|
||||
eventBus.post(new TargetAssignDistributionSetEvent(target.getOptLockRevision(), target.getTenant(),
|
||||
target.getControllerId(), action.getId(), softwareModuleList, target.getTargetInfo().getAddress()));
|
||||
target.getControllerId(), action.getId(), softwareModuleList, target.getTargetInfo().getAddress(),
|
||||
target.getSecurityToken()));
|
||||
|
||||
}
|
||||
|
||||
@@ -315,9 +337,8 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
final Action action = checkActionExist(message, actionUpdateStatus);
|
||||
|
||||
final ActionStatus actionStatus = new ActionStatus();
|
||||
final List<String> messageText = actionUpdateStatus.getMessage();
|
||||
final String messageString = String.join(", ", messageText);
|
||||
actionStatus.addMessage(messageString);
|
||||
actionUpdateStatus.getMessage().forEach(actionStatus::addMessage);
|
||||
|
||||
actionStatus.setAction(action);
|
||||
actionStatus.setOccurredAt(System.currentTimeMillis());
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ public class AmqpProperties {
|
||||
/**
|
||||
* DMF API dead letter queue.
|
||||
*/
|
||||
private String deadLetterQueue = "dmf_connector_deadletter";
|
||||
private String deadLetterQueue = "dmf_connector_deadletter_ttl";
|
||||
|
||||
/**
|
||||
* DMF API dead letter exchange.
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.amqp;
|
||||
|
||||
import static org.fest.assertions.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.any;
|
||||
@@ -23,6 +23,7 @@ import static org.mockito.Mockito.when;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.hawkbit.AbstractIntegrationTestWithMongoDB;
|
||||
import org.eclipse.hawkbit.TestDataUtil;
|
||||
@@ -58,6 +59,12 @@ import ru.yandex.qatools.allure.annotations.Stories;
|
||||
@Stories("AmqpMessage Dispatcher Service Test")
|
||||
public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWithMongoDB {
|
||||
|
||||
private static final String TENANT = "default";
|
||||
|
||||
private static final URI AMQP_URI = IpUtil.createAmqpUri("vHost", "mytest");
|
||||
|
||||
private static final String TEST_TOKEN = "testToken";
|
||||
|
||||
private AmqpMessageDispatcherService amqpMessageDispatcherService;
|
||||
|
||||
private RabbitTemplate rabbitTemplate;
|
||||
@@ -89,8 +96,7 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWit
|
||||
@Description("Verfies that download and install event with no software modul works")
|
||||
public void testSendDownloadRequesWithEmptySoftwareModules() {
|
||||
final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent(
|
||||
1L, "default", CONTROLLER_ID, 1l, new ArrayList<SoftwareModule>(),
|
||||
IpUtil.createAmqpUri("vHost", "mytest"));
|
||||
1L, TENANT, CONTROLLER_ID, 1L, new ArrayList<SoftwareModule>(), AMQP_URI, TEST_TOKEN);
|
||||
amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent);
|
||||
final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress());
|
||||
final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage);
|
||||
@@ -104,7 +110,7 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWit
|
||||
final DistributionSet dsA = TestDataUtil.generateDistributionSet("", softwareManagement,
|
||||
distributionSetManagement);
|
||||
final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent(
|
||||
1L, "default", CONTROLLER_ID, 1l, dsA.getModules(), IpUtil.createAmqpUri("vHost", "mytest"));
|
||||
1L, TENANT, CONTROLLER_ID, 1L, dsA.getModules(), AMQP_URI, TEST_TOKEN);
|
||||
amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent);
|
||||
final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress());
|
||||
final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage);
|
||||
@@ -143,7 +149,7 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWit
|
||||
Mockito.when(rabbitTemplate.convertSendAndReceive(any())).thenReturn(receivedList);
|
||||
|
||||
final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent(
|
||||
1L, "default", CONTROLLER_ID, 1l, dsA.getModules(), IpUtil.createAmqpUri("vHost", "mytest"));
|
||||
1L, TENANT, CONTROLLER_ID, 1L, dsA.getModules(), AMQP_URI, TEST_TOKEN);
|
||||
amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent);
|
||||
final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress());
|
||||
final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage);
|
||||
@@ -154,7 +160,19 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWit
|
||||
if (!softwareModule.getModuleId().equals(module.getId())) {
|
||||
continue;
|
||||
}
|
||||
assertFalse("The software module artifacts should not be empty", softwareModule.getArtifacts().isEmpty());
|
||||
assertThat(softwareModule.getArtifacts().size()).isEqualTo(module.getArtifacts().size()).isGreaterThan(0);
|
||||
|
||||
module.getArtifacts().forEach(dbArtifact -> {
|
||||
final Optional<org.eclipse.hawkbit.dmf.json.model.Artifact> found = softwareModule.getArtifacts()
|
||||
.stream().filter(dmfartifact -> dmfartifact.getFilename()
|
||||
.equals(((LocalArtifact) dbArtifact).getFilename()))
|
||||
.findFirst();
|
||||
|
||||
assertTrue("The artifact should exist in message", found.isPresent());
|
||||
assertThat(found.get().getSize()).isEqualTo(dbArtifact.getSize());
|
||||
assertThat(found.get().getHashes().getMd5()).isEqualTo(dbArtifact.getMd5Hash());
|
||||
assertThat(found.get().getHashes().getSha1()).isEqualTo(dbArtifact.getSha1Hash());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +180,7 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWit
|
||||
@Description("Verfies that send cancel event works")
|
||||
public void testSendCancelRequest() {
|
||||
final CancelTargetAssignmentEvent cancelTargetAssignmentDistributionSetEvent = new CancelTargetAssignmentEvent(
|
||||
1L, "default", CONTROLLER_ID, 1l, IpUtil.createAmqpUri("vHost", "mytest"));
|
||||
1L, TENANT, CONTROLLER_ID, 1L, AMQP_URI);
|
||||
amqpMessageDispatcherService
|
||||
.targetCancelAssignmentToDistributionSet(cancelTargetAssignmentDistributionSetEvent);
|
||||
final Message sendMessage = createArgumentCapture(cancelTargetAssignmentDistributionSetEvent.getTargetAdress());
|
||||
@@ -187,13 +205,12 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWit
|
||||
downloadAndUpdateRequest.getActionId(), Long.valueOf(1));
|
||||
assertEquals("The topic of the event shuold contain DOWNLOAD_AND_INSTALL", EventTopic.DOWNLOAD_AND_INSTALL,
|
||||
sendMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.TOPIC));
|
||||
assertEquals("Security token of target", downloadAndUpdateRequest.getTargetSecurityToken(), TEST_TOKEN);
|
||||
|
||||
return downloadAndUpdateRequest;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sendMessage
|
||||
*/
|
||||
private void assertEventMessage(final Message sendMessage) {
|
||||
assertNotNull("The message should not be null", sendMessage);
|
||||
|
||||
|
||||
@@ -313,11 +313,10 @@ public class AmqpMessageHandlerServiceTest {
|
||||
|
||||
// mock
|
||||
final LocalArtifact localArtifactMock = mock(LocalArtifact.class);
|
||||
final Action actionMock = mock(Action.class);
|
||||
final DbArtifact dbArtifactMock = mock(DbArtifact.class);
|
||||
when(artifactManagementMock.findFirstLocalArtifactsBySHA1(anyString())).thenReturn(localArtifactMock);
|
||||
when(controllerManagementMock.getActionForDownloadByTargetAndSoftwareModule(anyObject(), anyObject()))
|
||||
.thenReturn(actionMock);
|
||||
when(controllerManagementMock.hasTargetArtifactAssigned(securityToken.getControllerId(), localArtifactMock))
|
||||
.thenReturn(true);
|
||||
when(artifactManagementMock.loadLocalArtifactBinary(localArtifactMock)).thenReturn(dbArtifactMock);
|
||||
when(dbArtifactMock.getArtifactId()).thenReturn("artifactId");
|
||||
when(dbArtifactMock.getSize()).thenReturn(1L);
|
||||
@@ -334,20 +333,22 @@ public class AmqpMessageHandlerServiceTest {
|
||||
assertThat(downloadResponse.getResponseCode()).as("Message body response code is wrong")
|
||||
.isEqualTo(HttpStatus.OK.value());
|
||||
assertThat(downloadResponse.getArtifact().getSize()).as("Wrong artifact size in message body").isEqualTo(1L);
|
||||
assertThat(downloadResponse.getArtifact().getHashes().getSha1()).as("Wrong sha1 hash").isEqualTo("sha1");
|
||||
assertThat(downloadResponse.getArtifact().getHashes().getMd5()).as("Wrong md5 hash").isEqualTo("md5");
|
||||
assertThat(downloadResponse.getDownloadUrl()).as("download url is wrong")
|
||||
.startsWith("http://localhost/api/v1/downloadserver/downloadId/");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests TODO")
|
||||
public void lookupNextUpdateActionAfterFinished() throws IllegalArgumentException, IllegalAccessException {
|
||||
public void lookupNextUpdateActionAfterFinished() throws IllegalAccessException {
|
||||
|
||||
// Mock
|
||||
final Action action = createActionWithTarget(22L, Status.FINISHED);
|
||||
when(controllerManagementMock.findActionWithDetails(Matchers.any())).thenReturn(action);
|
||||
when(controllerManagementMock.addUpdateActionStatus(Matchers.any(), Matchers.any())).thenReturn(action);
|
||||
// for the test the same action can be used
|
||||
final List<Action> actionList = new ArrayList<Action>();
|
||||
final List<Action> actionList = new ArrayList<>();
|
||||
actionList.add(action);
|
||||
when(controllerManagementMock.findActionByTargetAndActive(Matchers.any())).thenReturn(actionList);
|
||||
|
||||
@@ -373,6 +374,8 @@ public class AmqpMessageHandlerServiceTest {
|
||||
|
||||
assertThat(targetAssignDistributionSetEvent.getControllerId()).as("event has wrong controller id")
|
||||
.isEqualTo("target1");
|
||||
assertThat(targetAssignDistributionSetEvent.getTargetToken()).as("targetoken not filled correctly")
|
||||
.isEqualTo(action.getTarget().getSecurityToken());
|
||||
assertThat(targetAssignDistributionSetEvent.getActionId()).as("event has wrong action id").isEqualTo(22L);
|
||||
assertThat(targetAssignDistributionSetEvent.getSoftwareModules()).as("event has wrong sofware modules")
|
||||
.isEqualTo(softwareModuleList);
|
||||
@@ -380,7 +383,7 @@ public class AmqpMessageHandlerServiceTest {
|
||||
}
|
||||
|
||||
private ActionUpdateStatus createActionUpdateStatus(final ActionStatus status) {
|
||||
return createActionUpdateStatus(status, 2l);
|
||||
return createActionUpdateStatus(status, 2L);
|
||||
}
|
||||
|
||||
private ActionUpdateStatus createActionUpdateStatus(final ActionStatus status, final Long id) {
|
||||
@@ -405,15 +408,14 @@ public class AmqpMessageHandlerServiceTest {
|
||||
}
|
||||
|
||||
private List<SoftwareModule> createSoftwareModuleList() {
|
||||
final List<SoftwareModule> softwareModuleList = new ArrayList<SoftwareModule>();
|
||||
final List<SoftwareModule> softwareModuleList = new ArrayList<>();
|
||||
final SoftwareModule softwareModule = new SoftwareModule();
|
||||
softwareModule.setId(777L);
|
||||
softwareModuleList.add(softwareModule);
|
||||
return softwareModuleList;
|
||||
}
|
||||
|
||||
private Action createActionWithTarget(final Long targetId, final Status status)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
private Action createActionWithTarget(final Long targetId, final Status status) throws IllegalAccessException {
|
||||
// is needed for the creation of targets
|
||||
initalizeSecurityTokenGenerator();
|
||||
|
||||
@@ -428,7 +430,7 @@ public class AmqpMessageHandlerServiceTest {
|
||||
return action;
|
||||
}
|
||||
|
||||
private void initalizeSecurityTokenGenerator() throws IllegalArgumentException, IllegalAccessException {
|
||||
private void initalizeSecurityTokenGenerator() throws IllegalAccessException {
|
||||
final SecurityTokenGeneratorHolder instance = SecurityTokenGeneratorHolder.getInstance();
|
||||
final Field[] fields = instance.getClass().getDeclaredFields();
|
||||
for (final Field field : fields) {
|
||||
|
||||
@@ -52,6 +52,8 @@ public class BaseAmqpServiceTest {
|
||||
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus();
|
||||
actionUpdateStatus.setActionId(1L);
|
||||
actionUpdateStatus.setSoftwareModuleId(2L);
|
||||
actionUpdateStatus.getMessage().add("Message 1");
|
||||
actionUpdateStatus.getMessage().add("Message 2");
|
||||
|
||||
final Message message = rabbitTemplate.getMessageConverter().toMessage(actionUpdateStatus,
|
||||
new MessageProperties());
|
||||
|
||||
@@ -13,10 +13,6 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* JSON representation of artifact hash.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class ArtifactHash {
|
||||
|
||||
|
||||
@@ -19,15 +19,16 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
/**
|
||||
* JSON representation of download and update request.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class DownloadAndUpdateRequest {
|
||||
@JsonProperty
|
||||
private Long actionId;
|
||||
|
||||
@JsonProperty
|
||||
private String targetSecurityToken;
|
||||
|
||||
@JsonProperty
|
||||
private final List<SoftwareModule> softwareModules = new LinkedList<>();
|
||||
|
||||
@@ -39,6 +40,14 @@ public class DownloadAndUpdateRequest {
|
||||
this.actionId = correlator;
|
||||
}
|
||||
|
||||
public String getTargetSecurityToken() {
|
||||
return targetSecurityToken;
|
||||
}
|
||||
|
||||
public void setTargetSecurityToken(final String targetSecurityToken) {
|
||||
this.targetSecurityToken = targetSecurityToken;
|
||||
}
|
||||
|
||||
public List<SoftwareModule> getSoftwareModules() {
|
||||
return softwareModules;
|
||||
}
|
||||
|
||||
@@ -26,8 +26,6 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
public class TenantSecurityToken {
|
||||
|
||||
public static final String AUTHORIZATION_HEADER = "Authorization";
|
||||
public static final String COAP_AUTHORIZATION_HEADER = "Coap-Authorization";
|
||||
public static final String COAP_TOKEN_VALUE = "CoapToken";
|
||||
|
||||
@JsonProperty
|
||||
private final String tenant;
|
||||
|
||||
@@ -11,9 +11,6 @@ package org.eclipse.hawkbit;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.transaction.Transaction;
|
||||
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.persistence.config.PersistenceUnitProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -38,38 +35,12 @@ public class MultiTenantJpaTransactionManager extends JpaTransactionManager {
|
||||
protected void doBegin(final Object transaction, final TransactionDefinition definition) {
|
||||
super.doBegin(transaction, definition);
|
||||
|
||||
final EntityManagerHolder emHolder = (EntityManagerHolder) TransactionSynchronizationManager
|
||||
.getResource(getEntityManagerFactory());
|
||||
final EntityManager em = emHolder.getEntityManager();
|
||||
|
||||
if (notTenantManagement(definition) && notCurrentTenantKeyGenerator(definition)
|
||||
&& notRolloutScheduler(definition) && notGetOrCreateTenantMetadata(definition)) {
|
||||
|
||||
final String currentTenant = tenantAware.getCurrentTenant();
|
||||
if (currentTenant == null) {
|
||||
throw new TenantNotExistException("Tenant Unknown. Canceling transaction.");
|
||||
}
|
||||
|
||||
final String currentTenant = tenantAware.getCurrentTenant();
|
||||
if (currentTenant != null) {
|
||||
final EntityManagerHolder emHolder = (EntityManagerHolder) TransactionSynchronizationManager
|
||||
.getResource(getEntityManagerFactory());
|
||||
final EntityManager em = emHolder.getEntityManager();
|
||||
em.setProperty(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, currentTenant.toUpperCase());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean notGetOrCreateTenantMetadata(final TransactionDefinition definition) {
|
||||
return !definition.getName()
|
||||
.startsWith(SystemManagement.class.getCanonicalName() + ".getOrCreateTenantMetadata");
|
||||
}
|
||||
|
||||
private boolean notRolloutScheduler(final TransactionDefinition definition) {
|
||||
return !definition.getName().startsWith(RolloutManagement.class.getCanonicalName() + ".rolloutScheduler");
|
||||
}
|
||||
|
||||
private boolean notCurrentTenantKeyGenerator(final TransactionDefinition definition) {
|
||||
return !definition.getName()
|
||||
.startsWith(SystemManagement.class.getCanonicalName() + ".currentTenantKeyGenerator");
|
||||
}
|
||||
|
||||
private boolean notTenantManagement(final TransactionDefinition definition) {
|
||||
return !definition.getName().startsWith(SystemManagement.class.getCanonicalName() + ".deleteTenant")
|
||||
&& !definition.getName().startsWith(SystemManagement.class.getCanonicalName() + ".findTenants");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,11 @@ import org.eclipse.hawkbit.repository.model.helper.AfterTransactionCommitExecuto
|
||||
import org.eclipse.hawkbit.repository.model.helper.CacheManagerHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.SecurityTokenGeneratorHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.SystemManagementHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.SystemSecurityContextHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.TenantAwareHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
|
||||
import org.eclipse.hawkbit.security.SecurityTokenGenerator;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
|
||||
@@ -48,14 +51,24 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess
|
||||
@EnableAutoConfiguration
|
||||
public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
|
||||
/**
|
||||
* @return the {@link SystemSecurityContext} singleton bean which make it
|
||||
* accessible in beans which cannot access the service directly,
|
||||
* e.g. JPA entities.
|
||||
*/
|
||||
@Bean
|
||||
public SystemSecurityContextHolder systemSecurityContextHolder() {
|
||||
return SystemSecurityContextHolder.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link TenantConfigurationManagement} singleton bean which
|
||||
* make it accessible in beans which cannot access the service
|
||||
* directly, e.g. JPA entities.
|
||||
*/
|
||||
@Bean
|
||||
public TenantConfigurationManagement tenantConfigurationManagement() {
|
||||
return TenantConfigurationManagement.getInstance();
|
||||
public TenantConfigurationManagementHolder tenantConfigurationManagementHolder() {
|
||||
return TenantConfigurationManagementHolder.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -90,6 +90,8 @@ public class ExceptionMappingAspectHandler implements Ordered {
|
||||
+ " || execution( * org.eclipse.hawkbit.controller.*.*(..)) "
|
||||
+ " || execution( * org.eclipse.hawkbit.rest.resource.*.*(..)) "
|
||||
+ " || execution( * org.eclipse.hawkbit.service.*.*(..)) )", throwing = "ex")
|
||||
// Exception squid:S00112 - Is aspectJ proxy
|
||||
@SuppressWarnings({ "squid:S00112" })
|
||||
public void catchAndWrapJpaExceptionsService(final Exception ex) throws Throwable {
|
||||
LOG.trace("exception occured", ex);
|
||||
Exception translatedAccessException = translateEclipseLinkExceptionIfPossible(ex);
|
||||
|
||||
@@ -20,9 +20,9 @@ import org.eclipse.hawkbit.eventbus.event.TargetDeletedEvent;
|
||||
import org.eclipse.hawkbit.eventbus.event.TargetInfoUpdateEvent;
|
||||
import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor;
|
||||
import org.eclipse.hawkbit.repository.TargetRepository;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetInfo;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -67,6 +67,8 @@ public class EntityChangeEventListener {
|
||||
* {@link ProceedingJoinPoint#proceed()}
|
||||
*/
|
||||
@Around("execution(* org.eclipse.hawkbit.repository.TargetInfoRepository.save(..))")
|
||||
// Exception squid:S00112 - Is aspectJ proxy
|
||||
@SuppressWarnings({ "squid:S00112" })
|
||||
public Object targetCreated(final ProceedingJoinPoint joinpoint) throws Throwable {
|
||||
final boolean isNew = isTargetInfoNew(joinpoint.getArgs()[0]);
|
||||
final Object result = joinpoint.proceed();
|
||||
@@ -92,6 +94,8 @@ public class EntityChangeEventListener {
|
||||
* {@link ProceedingJoinPoint#proceed()}
|
||||
*/
|
||||
@Around("execution(* org.eclipse.hawkbit.repository.TargetRepository.deleteByIdIn(..))")
|
||||
// Exception squid:S00112 - Is aspectJ proxy
|
||||
@SuppressWarnings({ "squid:S00112" })
|
||||
public Object targetDeletedById(final ProceedingJoinPoint joinpoint) throws Throwable {
|
||||
final String currentTenant = tenantAware.getCurrentTenant();
|
||||
final Object result = joinpoint.proceed();
|
||||
@@ -111,8 +115,9 @@ public class EntityChangeEventListener {
|
||||
* in case exception happens in the
|
||||
* {@link ProceedingJoinPoint#proceed()}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Around("execution(* org.eclipse.hawkbit.repository.TargetRepository.delete(..))")
|
||||
// Exception squid:S00112 - Is aspectJ proxy
|
||||
@SuppressWarnings({ "squid:S00112", "unchecked" })
|
||||
public Object targetDeleted(final ProceedingJoinPoint joinpoint) throws Throwable {
|
||||
final String currentTenant = tenantAware.getCurrentTenant();
|
||||
final Object result = joinpoint.proceed();
|
||||
|
||||
@@ -16,8 +16,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
/**
|
||||
* Event that gets sent when a distribution set gets assigned to a target.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class TargetAssignDistributionSetEvent extends AbstractEvent {
|
||||
|
||||
@@ -25,6 +23,7 @@ public class TargetAssignDistributionSetEvent extends AbstractEvent {
|
||||
private final String controllerId;
|
||||
private final Long actionId;
|
||||
private final URI targetAdress;
|
||||
private final String targetToken;
|
||||
|
||||
/**
|
||||
* Creates a new {@link TargetAssignDistributionSetEvent}.
|
||||
@@ -41,14 +40,18 @@ public class TargetAssignDistributionSetEvent extends AbstractEvent {
|
||||
* the software modules which have been assigned to the target
|
||||
* @param targetAdress
|
||||
* the targetAdress of the target
|
||||
* @param targetToken
|
||||
* the authentication token of the target
|
||||
*/
|
||||
public TargetAssignDistributionSetEvent(final long revision, final String tenant, final String controllerId,
|
||||
final Long actionId, final Collection<SoftwareModule> softwareModules, final URI targetAdress) {
|
||||
final Long actionId, final Collection<SoftwareModule> softwareModules, final URI targetAdress,
|
||||
final String targetToken) {
|
||||
super(revision, tenant);
|
||||
this.controllerId = controllerId;
|
||||
this.actionId = actionId;
|
||||
this.softwareModules = softwareModules;
|
||||
this.targetAdress = targetAdress;
|
||||
this.targetToken = targetToken;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,4 +80,7 @@ public class TargetAssignDistributionSetEvent extends AbstractEvent {
|
||||
return targetAdress;
|
||||
}
|
||||
|
||||
public String getTargetToken() {
|
||||
return targetToken;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ public class AfterTransactionCommitDefaultServiceExecutor extends TransactionSyn
|
||||
private static final ThreadLocal<List<Runnable>> THREAD_LOCAL_RUNNABLES = new ThreadLocal<>();
|
||||
|
||||
@Override
|
||||
// Exception squid:S1217 - Is aspectJ proxy
|
||||
@SuppressWarnings({ "squid:S1217" })
|
||||
public void afterCommit() {
|
||||
final List<Runnable> afterCommitRunnables = THREAD_LOCAL_RUNNABLES.get();
|
||||
LOGGER.debug("Transaction successfully committed, executing {} runnables", afterCommitRunnables.size());
|
||||
@@ -60,6 +62,7 @@ public class AfterTransactionCommitDefaultServiceExecutor extends TransactionSyn
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "squid:S1217" })
|
||||
public void afterCompletion(final int status) {
|
||||
final String transactionStatus = status == STATUS_COMMITTED ? "COMMITTED" : "ROLLEDBACK";
|
||||
LOGGER.debug("Transaction completed after commit with status {}", transactionStatus);
|
||||
|
||||
@@ -29,13 +29,14 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link Action} repository.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface ActionRepository extends BaseEntityRepository<Action, Long>, JpaSpecificationExecutor<Action> {
|
||||
/**
|
||||
* Retrieves an Action with all lazy attributes.
|
||||
@@ -172,7 +173,7 @@ public interface ActionRepository extends BaseEntityRepository<Action, Long>, Jp
|
||||
* active
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Query("UPDATE Action a SET a.active = false WHERE a IN :keySet AND a.target IN :targetsIds")
|
||||
void setToInactive(@Param("keySet") List<Action> keySet, @Param("targetsIds") List<Long> targetsIds);
|
||||
|
||||
@@ -191,7 +192,7 @@ public interface ActionRepository extends BaseEntityRepository<Action, Long>, Jp
|
||||
* the current status of the actions which are affected
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Query("UPDATE Action a SET a.status = :statusToSet WHERE a.target IN :targetsIds AND a.active = :active AND a.status = :currentStatus AND a.distributionSet.requiredMigrationStep = false")
|
||||
void switchStatus(@Param("statusToSet") Action.Status statusToSet, @Param("targetsIds") List<Long> targetIds,
|
||||
@Param("active") boolean active, @Param("currentStatus") Action.Status currentStatus);
|
||||
@@ -211,7 +212,7 @@ public interface ActionRepository extends BaseEntityRepository<Action, Long>, Jp
|
||||
* the current status of the actions which are affected
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Query("UPDATE Action a SET a.status = :statusToSet WHERE a.rollout = :rollout AND a.active = :active AND a.status = :currentStatus")
|
||||
void switchStatus(@Param("statusToSet") Action.Status statusToSet, @Param("rollout") Rollout rollout,
|
||||
@Param("active") boolean active, @Param("currentStatus") Action.Status currentStatus);
|
||||
@@ -386,6 +387,4 @@ public interface ActionRepository extends BaseEntityRepository<Action, Long>, Jp
|
||||
@Query("SELECT NEW org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus(a.rolloutGroup.id, a.status , COUNT(a.target)) FROM Action a WHERE a.rolloutGroup.id IN ?1 GROUP BY a.rolloutGroup.id, a.status")
|
||||
List<TotalTargetCountActionStatus> getStatusCountByRolloutGroupId(List<Long> rolloutGroupId);
|
||||
|
||||
// Asha-ends here
|
||||
|
||||
}
|
||||
|
||||
@@ -16,13 +16,14 @@ import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.EntityGraph;
|
||||
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link ActionStatus} repository.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface ActionStatusRepository
|
||||
extends BaseEntityRepository<ActionStatus, Long>, JpaSpecificationExecutor<ActionStatus> {
|
||||
|
||||
|
||||
@@ -43,17 +43,15 @@ import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.hateoas.Identifiable;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* service for {@link Artifact} management operations.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Service for {@link Artifact} management operations.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Validated
|
||||
@Service
|
||||
public class ArtifactManagement {
|
||||
@@ -108,7 +106,7 @@ public class ArtifactManagement {
|
||||
* if check against provided SHA1 checksum failed
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
|
||||
public LocalArtifact createLocalArtifact(@NotNull final InputStream stream, @NotNull final Long moduleId,
|
||||
@NotEmpty final String filename, final String providedMd5Sum, final String providedSha1Sum,
|
||||
@@ -138,7 +136,7 @@ public class ArtifactManagement {
|
||||
return storeArtifactMetadata(softwareModule, filename, result, existing);
|
||||
}
|
||||
|
||||
private LocalArtifact checkForExistingArtifact(final String filename, final boolean overrideExisting,
|
||||
private static LocalArtifact checkForExistingArtifact(final String filename, final boolean overrideExisting,
|
||||
final SoftwareModule softwareModule) {
|
||||
if (softwareModule.getLocalArtifactByFilename(filename).isPresent()) {
|
||||
if (overrideExisting) {
|
||||
@@ -222,9 +220,7 @@ public class ArtifactManagement {
|
||||
artifact.setSize(result.getSize());
|
||||
|
||||
LOG.debug("storing new artifact into repository {}", artifact);
|
||||
final LocalArtifact artifactPersisted = localArtifactRepository.save(artifact);
|
||||
|
||||
return artifactPersisted;
|
||||
return localArtifactRepository.save(artifact);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -242,7 +238,7 @@ public class ArtifactManagement {
|
||||
* @return created {@link ExternalArtifactProvider}
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
|
||||
public ExternalArtifactProvider createExternalArtifactProvider(@NotEmpty final String name,
|
||||
final String description, @NotNull final String basePath, final String defaultUrlSuffix) {
|
||||
@@ -268,16 +264,13 @@ public class ArtifactManagement {
|
||||
* if {@link SoftwareModule} with given ID does not exist
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
|
||||
public ExternalArtifact createExternalArtifact(@NotNull final ExternalArtifactProvider externalRepository,
|
||||
final String urlSuffix, @NotNull final Long moduleId) {
|
||||
|
||||
final SoftwareModule module = getModuleAndThrowExceptionIfThatFails(moduleId);
|
||||
final ExternalArtifact result = externalArtifactRepository
|
||||
.save(new ExternalArtifact(externalRepository, urlSuffix, module));
|
||||
|
||||
return result;
|
||||
return externalArtifactRepository.save(new ExternalArtifact(externalRepository, urlSuffix, module));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -290,7 +283,7 @@ public class ArtifactManagement {
|
||||
*
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
|
||||
public void deleteLocalArtifact(@NotNull final Long id) {
|
||||
final LocalArtifact existing = localArtifactRepository.findOne(id);
|
||||
@@ -313,7 +306,7 @@ public class ArtifactManagement {
|
||||
* the related local artifact
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
|
||||
public void deleteGridFsArtifact(@NotNull final LocalArtifact existing) {
|
||||
if (existing == null) {
|
||||
@@ -324,7 +317,7 @@ public class ArtifactManagement {
|
||||
for (final LocalArtifact lArtifact : localArtifactRepository
|
||||
.findByGridFsFileName(existing.getGridFsFileName())) {
|
||||
if (!lArtifact.getSoftwareModule().isDeleted()
|
||||
&& lArtifact.getSoftwareModule().getId() != existing.getSoftwareModule().getId()) {
|
||||
&& Long.compare(lArtifact.getSoftwareModule().getId(), existing.getSoftwareModule().getId()) != 0) {
|
||||
artifactIsOnlyUsedByOneSoftwareModule = false;
|
||||
break;
|
||||
}
|
||||
@@ -350,7 +343,7 @@ public class ArtifactManagement {
|
||||
*
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
|
||||
public void deleteExternalArtifact(@NotNull final Long id) {
|
||||
final ExternalArtifact existing = externalArtifactRepository.findOne(id);
|
||||
@@ -425,17 +418,17 @@ public class ArtifactManagement {
|
||||
* @param filename
|
||||
* of the artifact
|
||||
* @param overrideExisting
|
||||
* to <code>true</code> if the artifact binary can be overdiden
|
||||
* to <code>true</code> if the artifact binary can be overridden
|
||||
* if it already exists
|
||||
* @param contentType
|
||||
* the contentType of the file
|
||||
*
|
||||
* @return uploaded {@link LocalArtifact}
|
||||
*
|
||||
* @throw ArtifactUploadFailedException if upload failes
|
||||
* @throw ArtifactUploadFailedException if upload fails
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
public LocalArtifact createLocalArtifact(final InputStream inputStream, final Long moduleId, final String filename,
|
||||
final boolean overrideExisting, final String contentType) {
|
||||
@@ -461,7 +454,7 @@ public class ArtifactManagement {
|
||||
* @throw ArtifactUploadFailedException if upload failes
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
public LocalArtifact createLocalArtifact(final InputStream inputStream, final Long moduleId, final String filename,
|
||||
final boolean overrideExisting) {
|
||||
|
||||
@@ -14,21 +14,19 @@ import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.repository.NoRepositoryBean;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Command repository operations for all {@link TenantAwareBaseEntity}s.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param <T>
|
||||
* type if the entity type
|
||||
* @param <I>
|
||||
* of the entity type
|
||||
*/
|
||||
@NoRepositoryBean
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface BaseEntityRepository<T extends TenantAwareBaseEntity, I extends Serializable>
|
||||
extends PagingAndSortingRepository<T, I> {
|
||||
|
||||
@@ -39,7 +37,7 @@ public interface BaseEntityRepository<T extends TenantAwareBaseEntity, I extends
|
||||
* to delete data from
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
void deleteByTenantIgnoreCase(String tenant);
|
||||
|
||||
}
|
||||
|
||||
@@ -28,12 +28,14 @@ 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.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.LocalArtifact;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetInfo;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.model.Target_;
|
||||
import org.eclipse.hawkbit.repository.model.TenantConfiguration;
|
||||
import org.eclipse.hawkbit.repository.specifications.ActionSpecifications;
|
||||
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
@@ -44,6 +46,7 @@ import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
@@ -54,13 +57,15 @@ import org.springframework.validation.annotation.Validated;
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Validated
|
||||
@Service
|
||||
public class ControllerManagement {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ControllerManagement.class);
|
||||
private static final Logger LOG_DOS = LoggerFactory.getLogger("server-security.dos");
|
||||
|
||||
public static final String SERVER_MESSAGE_PREFIX = "Update Server: ";
|
||||
|
||||
@Autowired
|
||||
private EntityManager entityManager;
|
||||
|
||||
@@ -129,7 +134,7 @@ public class ControllerManagement {
|
||||
* if target with given ID could not be found
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
public Target updateLastTargetQuery(@NotEmpty final String targetid, final URI address) {
|
||||
final Target target = targetRepository.findByControllerId(targetid);
|
||||
@@ -166,6 +171,31 @@ public class ControllerManagement {
|
||||
return action.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given target has currently or has even been assigned to the
|
||||
* given artifact through the action history list. This can e.g. indicate if
|
||||
* a target is allowed to download a given artifact because it has currently
|
||||
* assigned or had ever been assigned to the target and so it's visible to a
|
||||
* specific target e.g. for downloading.
|
||||
*
|
||||
* @param targetId
|
||||
* the ID of the target to check
|
||||
* @param localArtifact
|
||||
* the artifact to verify if the given target had even been
|
||||
* assigned to
|
||||
* @return {@code true} if the given target has currently or had ever a
|
||||
* relation to the given artifact through the action history,
|
||||
* otherwise {@code false}
|
||||
*/
|
||||
public boolean hasTargetArtifactAssigned(@NotNull final String targetId,
|
||||
@NotNull final LocalArtifact localArtifact) {
|
||||
final Target target = targetRepository.findByControllerId(targetId);
|
||||
if (target == null) {
|
||||
return false;
|
||||
}
|
||||
return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(target, localArtifact)) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the time of the last time the controller has been connected to
|
||||
* the server.
|
||||
@@ -178,7 +208,7 @@ public class ControllerManagement {
|
||||
*
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
public TargetInfo updateLastTargetQuery(@NotNull final TargetInfo target, final URI address) {
|
||||
return updateTargetStatus(target, null, System.currentTimeMillis(), address);
|
||||
@@ -235,7 +265,7 @@ public class ControllerManagement {
|
||||
* @return target reference
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
public Target findOrRegisterTargetIfItDoesNotexist(@NotEmpty final String targetid, final URI address) {
|
||||
final Specification<Target> spec = (targetRoot, query, cb) -> cb.equal(targetRoot.get(Target_.controllerId),
|
||||
@@ -271,7 +301,7 @@ public class ControllerManagement {
|
||||
* @return the updated TargetInfo
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
public TargetInfo updateTargetStatus(@NotNull final TargetInfo targetInfo, final TargetUpdateStatus status,
|
||||
final Long lastTargetQuery, final URI address) {
|
||||
@@ -300,7 +330,7 @@ public class ControllerManagement {
|
||||
*
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
public Action addCancelActionStatus(@NotNull final ActionStatus actionStatus, final Action action) {
|
||||
|
||||
@@ -314,13 +344,14 @@ public class ControllerManagement {
|
||||
break;
|
||||
case CANCELED:
|
||||
case FINISHED:
|
||||
// in case of successful cancelation we also report the success at
|
||||
// in case of successful cancellation we also report the success at
|
||||
// the canceled action itself.
|
||||
actionStatus.addMessage("Cancelation completion is finished sucessfully.");
|
||||
actionStatus.addMessage(
|
||||
ControllerManagement.SERVER_MESSAGE_PREFIX + "Cancellation completion is finished sucessfully.");
|
||||
deploymentManagement.successCancellation(action);
|
||||
break;
|
||||
case RETRIEVED:
|
||||
actionStatus.addMessage("Cancelation request retrieved");
|
||||
actionStatus.addMessage(ControllerManagement.SERVER_MESSAGE_PREFIX + "Cancellation request retrieved.");
|
||||
break;
|
||||
default:
|
||||
}
|
||||
@@ -346,7 +377,7 @@ public class ControllerManagement {
|
||||
* inserted
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
public Action addUpdateActionStatus(@NotNull final ActionStatus actionStatus, final Action action) {
|
||||
|
||||
@@ -454,7 +485,7 @@ public class ControllerManagement {
|
||||
*/
|
||||
@Modifying
|
||||
@NotNull
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
public Target updateControllerAttributes(@NotEmpty final String targetid, @NotNull final Map<String, String> data) {
|
||||
final Target target = targetRepository.findByControllerId(targetid);
|
||||
@@ -492,7 +523,7 @@ public class ControllerManagement {
|
||||
* {@link Status#RETRIEVED}
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
public Action registerRetrieved(final Action action, final String message) {
|
||||
return handleRegisterRetrieved(action, message);
|
||||
@@ -556,7 +587,7 @@ public class ControllerManagement {
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public void addActionStatusMessage(final ActionStatus statusMessage) {
|
||||
actionStatusRepository.save(statusMessage);
|
||||
}
|
||||
@@ -573,7 +604,7 @@ public class ControllerManagement {
|
||||
* @return the security context of the target, in case no target exists for
|
||||
* the given controllerId {@code null} is returned
|
||||
*/
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public String getSecurityTokenByControllerId(final String controllerId) {
|
||||
final Target target = targetRepository.findByControllerId(controllerId);
|
||||
return target != null ? target.getSecurityToken() : null;
|
||||
|
||||
@@ -67,6 +67,7 @@ import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
@@ -78,7 +79,7 @@ import com.google.common.eventbus.EventBus;
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Validated
|
||||
@Service
|
||||
public class DeploymentManagement {
|
||||
@@ -133,7 +134,7 @@ public class DeploymentManagement {
|
||||
* {@link SoftwareModuleType} are not assigned as define by the
|
||||
* {@link DistributionSetType}. *
|
||||
*/
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
|
||||
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
|
||||
@@ -167,7 +168,7 @@ public class DeploymentManagement {
|
||||
* {@link DistributionSetType}.
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
|
||||
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
|
||||
public DistributionSetAssignmentResult assignDistributionSet(@NotNull final Long dsID,
|
||||
@@ -195,9 +196,12 @@ public class DeploymentManagement {
|
||||
* {@link DistributionSetType}.
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
|
||||
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
|
||||
// Exception squid:S2095: see
|
||||
// https://jira.sonarsource.com/browse/SONARJAVA-1478
|
||||
@SuppressWarnings({ "squid:S2095" })
|
||||
public DistributionSetAssignmentResult assignDistributionSet(@NotNull final Long dsID, final ActionType actionType,
|
||||
final long forcedTimestamp, @NotEmpty final String... targetIDs) {
|
||||
return assignDistributionSet(dsID, Arrays.stream(targetIDs)
|
||||
@@ -219,7 +223,7 @@ public class DeploymentManagement {
|
||||
* {@link DistributionSetType}.
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
|
||||
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
|
||||
public DistributionSetAssignmentResult assignDistributionSet(@NotNull final Long dsID,
|
||||
@@ -243,8 +247,8 @@ public class DeploymentManagement {
|
||||
* a list of all targets and their action type
|
||||
* @param rollout
|
||||
* the rollout for this assignment
|
||||
* @param rolloutgroup
|
||||
* the rolloutgroup for this assignment
|
||||
* @param rolloutGroup
|
||||
* the rollout group for this assignment
|
||||
* @return the assignment result
|
||||
*
|
||||
* @throw IncompleteDistributionSetException if mandatory
|
||||
@@ -252,7 +256,7 @@ public class DeploymentManagement {
|
||||
* {@link DistributionSetType}.
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
|
||||
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
|
||||
public DistributionSetAssignmentResult assignDistributionSet(@NotNull final Long dsID,
|
||||
@@ -276,8 +280,8 @@ public class DeploymentManagement {
|
||||
* a list of all targets and their action type
|
||||
* @param rollout
|
||||
* the rollout for this assignment
|
||||
* @param rolloutgroup
|
||||
* the rolloutgroup for this assignment
|
||||
* @param rolloutGroup
|
||||
* the rollout group for this assignment
|
||||
* @return the assignment result
|
||||
*
|
||||
* @throw IncompleteDistributionSetException if mandatory
|
||||
@@ -389,8 +393,8 @@ public class DeploymentManagement {
|
||||
softwareModules));
|
||||
}
|
||||
|
||||
private Action createTargetAction(final Map<String, TargetWithActionType> targetsWithActionMap, final Target target,
|
||||
final DistributionSet set, final Rollout rollout, final RolloutGroup rolloutGroup) {
|
||||
private static Action createTargetAction(final Map<String, TargetWithActionType> targetsWithActionMap,
|
||||
final Target target, final DistributionSet set, final Rollout rollout, final RolloutGroup rolloutGroup) {
|
||||
final Action actionForTarget = new Action();
|
||||
final TargetWithActionType targetWithActionType = targetsWithActionMap.get(target.getControllerId());
|
||||
actionForTarget.setActionType(targetWithActionType.getActionType());
|
||||
@@ -421,13 +425,14 @@ public class DeploymentManagement {
|
||||
afterCommit.afterCommit(() -> {
|
||||
eventBus.post(new TargetInfoUpdateEvent(target.getTargetInfo()));
|
||||
eventBus.post(new TargetAssignDistributionSetEvent(target.getOptLockRevision(), target.getTenant(),
|
||||
target.getControllerId(), actionId, softwareModules, target.getTargetInfo().getAddress()));
|
||||
target.getControllerId(), actionId, softwareModules, target.getTargetInfo().getAddress(),
|
||||
target.getSecurityToken()));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes {@link UpdateAction}s that are no longer necessary and sends
|
||||
* cancelations to the controller.
|
||||
* cancellations to the controller.
|
||||
*
|
||||
* @param myTarget
|
||||
* to override {@link UpdateAction}s
|
||||
@@ -512,7 +517,7 @@ public class DeploymentManagement {
|
||||
* action
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||
public Action cancelAction(@NotNull final Action action, @NotNull final Target target) {
|
||||
LOG.debug("cancelAction({}, {})", action, target);
|
||||
@@ -569,7 +574,7 @@ public class DeploymentManagement {
|
||||
* in case the given action is not active
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||
public Action forceQuitAction(@NotNull final Action action) {
|
||||
final Action mergedAction = entityManager.merge(action);
|
||||
@@ -614,7 +619,7 @@ public class DeploymentManagement {
|
||||
* the rolloutgroup for this action
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||
public void createScheduledAction(final List<Target> targets, final DistributionSet distributionSet,
|
||||
final ActionType actionType, final long forcedTime, final Rollout rollout,
|
||||
@@ -648,7 +653,7 @@ public class DeploymentManagement {
|
||||
* @return the action which has been started
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
public Action startScheduledAction(@NotNull final Action action) {
|
||||
@@ -911,7 +916,7 @@ public class DeploymentManagement {
|
||||
* @return the updated or the found {@link TargetAction}
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||
public Action forceTargetAction(final Long actionId) {
|
||||
final Action action = actionRepository.findOne(actionId);
|
||||
@@ -923,7 +928,7 @@ public class DeploymentManagement {
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieves all the {@link ActionStatus} entries of the given
|
||||
* Retrieves all the {@link ActionStatus} entries of the given
|
||||
* {@link Action} and {@link Target}.
|
||||
*
|
||||
* @param pageReq
|
||||
@@ -981,11 +986,11 @@ public class DeploymentManagement {
|
||||
* @param rollout
|
||||
* the rollout the actions belong to
|
||||
* @param rolloutGroupParent
|
||||
* the parent rolloutgroup the actions should reference
|
||||
* the parent rollout group the actions should reference
|
||||
* @param actionStatus
|
||||
* the status the actions have
|
||||
* @return the actions referring a specific rollout and a specific parent
|
||||
* rolloutgroup in a specific status
|
||||
* rollout group in a specific status
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
|
||||
@@ -59,6 +59,7 @@ import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
@@ -69,7 +70,7 @@ import com.google.common.eventbus.EventBus;
|
||||
* Business facade for managing the {@link DistributionSet}s.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Validated
|
||||
@Service
|
||||
public class DistributionSetManagement {
|
||||
@@ -142,7 +143,7 @@ public class DistributionSetManagement {
|
||||
* the assignment outcome.
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@NotNull
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
public DistributionSetTagAssignmentResult toggleTagAssignment(@NotEmpty final List<DistributionSet> sets,
|
||||
@@ -164,7 +165,7 @@ public class DistributionSetManagement {
|
||||
* the assignment outcome.
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@NotNull
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
public DistributionSetTagAssignmentResult toggleTagAssignment(@NotEmpty final Collection<Long> dsIds,
|
||||
@@ -229,7 +230,7 @@ public class DistributionSetManagement {
|
||||
* @throw DataDependencyViolationException in case of illegal update
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
public DistributionSet updateDistributionSet(@NotNull final DistributionSet ds) {
|
||||
checkNotNull(ds.getId());
|
||||
@@ -254,7 +255,7 @@ public class DistributionSetManagement {
|
||||
* to delete
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
|
||||
public void deleteDistributionSet(@NotNull final DistributionSet set) {
|
||||
deleteDistributionSet(set.getId());
|
||||
@@ -269,7 +270,7 @@ public class DistributionSetManagement {
|
||||
* to be deleted
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
|
||||
public void deleteDistributionSet(@NotEmpty final Long... distributionSetIDs) {
|
||||
final List<Long> toHardDelete = new ArrayList<>();
|
||||
@@ -310,7 +311,7 @@ public class DistributionSetManagement {
|
||||
* {@link SoftwareModule}s.
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
|
||||
public DistributionSet createDistributionSet(@NotNull final DistributionSet dSet) {
|
||||
prepareDsSave(dSet);
|
||||
@@ -344,7 +345,7 @@ public class DistributionSetManagement {
|
||||
* {@link SoftwareModule}s.
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
|
||||
public List<DistributionSet> createDistributionSets(@NotNull final Iterable<DistributionSet> distributionSets) {
|
||||
for (final DistributionSet ds : distributionSets) {
|
||||
@@ -366,7 +367,7 @@ public class DistributionSetManagement {
|
||||
* @return the updated {@link DistributionSet}.
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
public DistributionSet assignSoftwareModules(@NotNull final DistributionSet ds,
|
||||
final Set<SoftwareModule> softwareModules) {
|
||||
@@ -388,7 +389,7 @@ public class DistributionSetManagement {
|
||||
* @return the updated {@link DistributionSet}.
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
public DistributionSet unassignSoftwareModule(@NotNull final DistributionSet ds,
|
||||
final SoftwareModule softwareModule) {
|
||||
@@ -413,7 +414,7 @@ public class DistributionSetManagement {
|
||||
* s while the DS type is already in use.
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
public DistributionSetType updateDistributionSetType(@NotNull final DistributionSetType dsType) {
|
||||
checkNotNull(dsType.getId());
|
||||
@@ -715,7 +716,7 @@ public class DistributionSetManagement {
|
||||
* @return created {@link Entity}
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
|
||||
public DistributionSetType createDistributionSetType(@NotNull final DistributionSetType type) {
|
||||
if (type.getId() != null) {
|
||||
@@ -732,7 +733,7 @@ public class DistributionSetManagement {
|
||||
* to delete
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
|
||||
public void deleteDistributionSetType(@NotNull final DistributionSetType type) {
|
||||
|
||||
@@ -755,7 +756,7 @@ public class DistributionSetManagement {
|
||||
* in case the meta data entry already exists for the specific
|
||||
* key
|
||||
*/
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
public DistributionSetMetadata createDistributionSetMetadata(@NotNull final DistributionSetMetadata metadata) {
|
||||
@@ -780,7 +781,7 @@ public class DistributionSetManagement {
|
||||
* in case one of the meta data entry already exists for the
|
||||
* specific key
|
||||
*/
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
public List<DistributionSetMetadata> createDistributionSetMetadata(
|
||||
@@ -802,7 +803,7 @@ public class DistributionSetManagement {
|
||||
* in case the meta data entry does not exists and cannot be
|
||||
* updated
|
||||
*/
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
public DistributionSetMetadata updateDistributionSetMetadata(@NotNull final DistributionSetMetadata metadata) {
|
||||
@@ -820,7 +821,7 @@ public class DistributionSetManagement {
|
||||
* @param id
|
||||
* the ID of the distribution set meta data to delete
|
||||
*/
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
public void deleteDistributionSetMetadata(@NotNull final DsMetadataCompositeKey id) {
|
||||
@@ -913,7 +914,7 @@ public class DistributionSetManagement {
|
||||
* @return created {@link Entity}
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
|
||||
public List<DistributionSetType> createDistributionSetTypes(@NotNull final Collection<DistributionSetType> types) {
|
||||
return types.stream().map(this::createDistributionSetType).collect(Collectors.toList());
|
||||
@@ -926,7 +927,7 @@ public class DistributionSetManagement {
|
||||
* @param softwareModules
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
public void checkDistributionSetAlreadyUse(final DistributionSet distributionSet) {
|
||||
checkDistributionSetSoftwareModulesIsAllowedToModify(distributionSet);
|
||||
@@ -992,14 +993,14 @@ public class DistributionSetManagement {
|
||||
}
|
||||
}
|
||||
|
||||
private Boolean isDSWithNoTagSelected(final DistributionSetFilter distributionSetFilter) {
|
||||
private static Boolean isDSWithNoTagSelected(final DistributionSetFilter distributionSetFilter) {
|
||||
if (distributionSetFilter.getSelectDSWithNoTag() != null && distributionSetFilter.getSelectDSWithNoTag()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Boolean isTagsSelected(final DistributionSetFilter distributionSetFilter) {
|
||||
private static Boolean isTagsSelected(final DistributionSetFilter distributionSetFilter) {
|
||||
if (distributionSetFilter.getTagNames() != null && !distributionSetFilter.getTagNames().isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
@@ -1033,7 +1034,7 @@ public class DistributionSetManagement {
|
||||
}
|
||||
}
|
||||
|
||||
private void throwMetadataKeyAlreadyExists(final String metadataKey) {
|
||||
private static void throwMetadataKeyAlreadyExists(final String metadataKey) {
|
||||
throw new EntityAlreadyExistsException("Metadata entry with key '" + metadataKey + "' already exists");
|
||||
}
|
||||
|
||||
@@ -1048,7 +1049,7 @@ public class DistributionSetManagement {
|
||||
* @return list of assigned ds
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@NotNull
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||
public List<DistributionSet> assignTag(@NotEmpty final Collection<Long> dsIds,
|
||||
@@ -1077,7 +1078,7 @@ public class DistributionSetManagement {
|
||||
* @return list of unassigned ds
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@NotNull
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||
public List<DistributionSet> unAssignAllDistributionSetsByTag(@NotNull final DistributionSetTag tag) {
|
||||
@@ -1095,7 +1096,7 @@ public class DistributionSetManagement {
|
||||
* @return the unassigned ds or <null> if no ds is unassigned
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||
public DistributionSet unAssignTag(@NotNull final Long dsId, @NotNull final DistributionSetTag distributionSetTag) {
|
||||
final List<DistributionSet> allDs = findDistributionSetListWithDetails(Arrays.asList(dsId));
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.DsMetadataCompositeKey;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
@@ -20,7 +21,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface DistributionSetMetadataRepository
|
||||
extends PagingAndSortingRepository<DistributionSetMetadata, DsMetadataCompositeKey>,
|
||||
JpaSpecificationExecutor<DistributionSetMetadata> {
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
@@ -29,7 +30,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface DistributionSetRepository
|
||||
extends BaseEntityRepository<DistributionSet, Long>, JpaSpecificationExecutor<DistributionSet> {
|
||||
|
||||
@@ -50,7 +51,7 @@ public interface DistributionSetRepository
|
||||
* to be deleted
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Query("update DistributionSet d set d.deleted = 1 where d.id in :ids")
|
||||
void deleteDistributionSet(@Param("ids") Long... ids);
|
||||
|
||||
@@ -62,7 +63,7 @@ public interface DistributionSetRepository
|
||||
* @return number of affected/deleted records
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
|
||||
@Query("DELETE FROM DistributionSet d WHERE d.id IN ?1")
|
||||
int deleteByIdIn(Collection<Long> ids);
|
||||
@@ -82,7 +83,7 @@ public interface DistributionSetRepository
|
||||
* yet to an {@link UpdateAction}, i.e. unused.
|
||||
*
|
||||
* @param ids
|
||||
* to searcgh for
|
||||
* to search for
|
||||
* @return
|
||||
*/
|
||||
@Query("select ac.distributionSet.id from Action ac where ac.distributionSet.id in :ids")
|
||||
|
||||
@@ -15,15 +15,14 @@ import org.eclipse.hawkbit.repository.model.DistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link TargetTag} repository.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface DistributionSetTagRepository
|
||||
extends BaseEntityRepository<DistributionSetTag, Long>, JpaSpecificationExecutor<DistributionSetTag> {
|
||||
/**
|
||||
@@ -34,7 +33,7 @@ public interface DistributionSetTagRepository
|
||||
* @return 1 if tag was deleted
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
Long deleteByName(final String tagName);
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,16 +14,14 @@ import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link PagingAndSortingRepository} for {@link DistributionSetType}.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface DistributionSetTypeRepository
|
||||
extends BaseEntityRepository<DistributionSetType, Long>, JpaSpecificationExecutor<DistributionSetType> {
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
import javax.transaction.Transactional;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.TargetInfo;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
@@ -21,16 +20,16 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Custom repository implementation as standard spring repository fails as of
|
||||
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=415027 .
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Service
|
||||
@Transactional
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public class EclipseLinkTargetInfoRepository implements TargetInfoRepository {
|
||||
|
||||
@Autowired
|
||||
@@ -38,6 +37,7 @@ public class EclipseLinkTargetInfoRepository implements TargetInfoRepository {
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public void setTargetUpdateStatus(final TargetUpdateStatus status, final List<Long> targets) {
|
||||
final Query query = entityManager.createQuery(
|
||||
"update TargetInfo ti set ti.updateStatus = :status where ti.targetId in :targets and ti.updateStatus != :status");
|
||||
@@ -48,6 +48,7 @@ public class EclipseLinkTargetInfoRepository implements TargetInfoRepository {
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true)
|
||||
public <S extends TargetInfo> S save(final S entity) {
|
||||
|
||||
@@ -61,6 +62,7 @@ public class EclipseLinkTargetInfoRepository implements TargetInfoRepository {
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true)
|
||||
public void deleteByTargetIdIn(final Collection<Long> targetIDs) {
|
||||
final javax.persistence.Query query = entityManager
|
||||
|
||||
@@ -9,16 +9,14 @@
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.ExternalArtifactProvider;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Repository for {@link ExternalArtifactProvider}.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface ExternalArtifactProviderRepository extends BaseEntityRepository<ExternalArtifactProvider, Long> {
|
||||
|
||||
}
|
||||
|
||||
@@ -11,15 +11,14 @@ package org.eclipse.hawkbit.repository;
|
||||
import org.eclipse.hawkbit.repository.model.ExternalArtifact;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link ExternalArtifact} repository.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface ExternalArtifactRepository extends BaseEntityRepository<ExternalArtifact, Long> {
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,13 +15,14 @@ import org.eclipse.hawkbit.repository.model.LocalArtifact;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link LocalArtifact} repository.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface LocalArtifactRepository extends BaseEntityRepository<LocalArtifact, Long> {
|
||||
|
||||
/**
|
||||
|
||||
@@ -48,14 +48,15 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* Service layer for generating SP reportings.
|
||||
* Service layer for generating hawkBit reports.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Validated
|
||||
@Service
|
||||
public class ReportManagement {
|
||||
@@ -404,7 +405,7 @@ public class ReportManagement {
|
||||
return innerOuterReport;
|
||||
}
|
||||
|
||||
private final class InnerOuter {
|
||||
private static final class InnerOuter {
|
||||
final DSName name;
|
||||
long count;
|
||||
final List<InnerOuter> outer;
|
||||
@@ -433,9 +434,6 @@ public class ReportManagement {
|
||||
/**
|
||||
* Object contains the name and the id of an entity.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
private static final class DSName {
|
||||
|
||||
@@ -510,9 +508,6 @@ public class ReportManagement {
|
||||
* Return DateTypes.
|
||||
*/
|
||||
public static final class DateTypes implements Serializable {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final PerMonth PER_MONTH = new PerMonth();
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
@@ -52,7 +53,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
*/
|
||||
@Validated
|
||||
@Service
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public class RolloutGroupManagement {
|
||||
|
||||
@Autowired
|
||||
@@ -189,7 +190,7 @@ public class RolloutGroupManagement {
|
||||
final ListJoin<Target, RolloutTargetGroup> rolloutTargetJoin = root.join(Target_.rolloutTargetGroup);
|
||||
return criteriaBuilder.and(specification.toPredicate(root, query, criteriaBuilder),
|
||||
criteriaBuilder.equal(rolloutTargetJoin.get(RolloutTargetGroup_.rolloutGroup), rolloutGroup));
|
||||
} , page);
|
||||
}, page);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,7 +214,7 @@ public class RolloutGroupManagement {
|
||||
return targetRepository.findByActionsRolloutGroup(rolloutGroup, page);
|
||||
}
|
||||
|
||||
private boolean isRolloutStatusReady(final RolloutGroup rolloutGroup) {
|
||||
private static boolean isRolloutStatusReady(final RolloutGroup rolloutGroup) {
|
||||
return rolloutGroup != null && RolloutStatus.READY.equals(rolloutGroup.getRollout().getStatus());
|
||||
}
|
||||
|
||||
@@ -259,5 +260,4 @@ public class RolloutGroupManagement {
|
||||
.collect(Collectors.toList());
|
||||
return new PageImpl<>(targetWithActionStatus, pageRequest, totalCount);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,13 @@ import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* The repository interface for the {@link RolloutGroup} model.
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface RolloutGroupRepository
|
||||
extends BaseEntityRepository<RolloutGroup, Long>, JpaSpecificationExecutor<RolloutGroup> {
|
||||
|
||||
|
||||
@@ -58,6 +58,8 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.DefaultTransactionDefinition;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
@@ -72,7 +74,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
@Validated
|
||||
@Service
|
||||
@EnableScheduling
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public class RolloutManagement {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RolloutManagement.class);
|
||||
|
||||
@@ -134,7 +136,7 @@ public class RolloutManagement {
|
||||
|
||||
/**
|
||||
* Retrieves all rollouts.
|
||||
*
|
||||
*
|
||||
* @param page
|
||||
* the page request to sort and limit the result
|
||||
* @return a page of found rollouts
|
||||
@@ -146,7 +148,7 @@ public class RolloutManagement {
|
||||
|
||||
/**
|
||||
* Retrieves all rollouts found by the given specification.
|
||||
*
|
||||
*
|
||||
* @param specification
|
||||
* the specification to filter rollouts
|
||||
* @param page
|
||||
@@ -163,7 +165,7 @@ public class RolloutManagement {
|
||||
|
||||
/**
|
||||
* Retrieves a specific rollout by its ID.
|
||||
*
|
||||
*
|
||||
* @param rolloutId
|
||||
* the ID of the rollout to retrieve
|
||||
* @return the founded rollout or {@code null} if rollout with given ID does
|
||||
@@ -180,13 +182,13 @@ public class RolloutManagement {
|
||||
* which are effected by this rollout to create. The targets will then be
|
||||
* split up into groups. The size of the groups can be defined in the
|
||||
* {@code groupSize} parameter.
|
||||
*
|
||||
*
|
||||
* The rollout is not started. Only the preparation of the rollout is done,
|
||||
* persisting and creating all the necessary groups. The Rollout and the
|
||||
* groups are persisted in {@link RolloutStatus#READY} and
|
||||
* {@link RolloutGroupStatus#READY} so they can be started
|
||||
* {@link #startRollout(Rollout)}.
|
||||
*
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout entity to create
|
||||
* @param amountGroup
|
||||
@@ -195,11 +197,11 @@ public class RolloutManagement {
|
||||
* the rolloutgroup conditions and actions which should be
|
||||
* applied for each {@link RolloutGroup}
|
||||
* @return the persisted rollout.
|
||||
*
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* in case the given groupSize is zero or lower.
|
||||
*/
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
|
||||
public Rollout createRollout(final Rollout rollout, final int amountGroup,
|
||||
@@ -215,22 +217,22 @@ public class RolloutManagement {
|
||||
* will be done synchronously and will be returned. The targets will then be
|
||||
* split up into groups. The size of the groups can be defined in the
|
||||
* {@code groupSize} parameter.
|
||||
*
|
||||
*
|
||||
* The creation of the rollout groups is executed asynchronously due it
|
||||
* might take some time to split up the targets into groups. The creation of
|
||||
* the {@link RolloutGroup} is published as event
|
||||
* {@link RolloutGroupCreatedEvent}.
|
||||
*
|
||||
*
|
||||
* The rollout is in status {@link RolloutStatus#CREATING} until all rollout
|
||||
* groups has been created and the targets are split up, then the rollout
|
||||
* will change the status to {@link RolloutStatus#READY}.
|
||||
*
|
||||
*
|
||||
* The rollout is not started. Only the preparation of the rollout is done,
|
||||
* persisting and creating all the necessary groups. The Rollout and the
|
||||
* groups are persisted in {@link RolloutStatus#READY} and
|
||||
* {@link RolloutGroupStatus#READY} so they can be started
|
||||
* {@link #startRollout(Rollout)}.
|
||||
*
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout to be created
|
||||
* @param amountGroup
|
||||
@@ -242,7 +244,7 @@ public class RolloutManagement {
|
||||
* @return the created rollout entity in state
|
||||
* {@link RolloutStatus#CREATING}
|
||||
*/
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
|
||||
public Rollout createRolloutAsync(final Rollout rollout, final int amountGroup,
|
||||
@@ -259,13 +261,7 @@ public class RolloutManagement {
|
||||
entityManager.flush();
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
|
||||
def.setName("creatingRollout");
|
||||
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
|
||||
new TransactionTemplate(txManager, def).execute(status -> {
|
||||
createRolloutGroups(amountGroup, conditions, savedRollout);
|
||||
return null;
|
||||
});
|
||||
createRolloutGroupsInNewTransaction(amountGroup, conditions, savedRollout);
|
||||
} finally {
|
||||
creatingRollouts.remove(savedRollout.getName());
|
||||
}
|
||||
@@ -280,7 +276,7 @@ public class RolloutManagement {
|
||||
return rolloutRepository.save(rollout);
|
||||
}
|
||||
|
||||
private void verifyRolloutGroupParameter(final int amountGroup) {
|
||||
private static void verifyRolloutGroupParameter(final int amountGroup) {
|
||||
if (amountGroup <= 0) {
|
||||
throw new IllegalArgumentException("the amountGroup must be greater than zero");
|
||||
} else if (amountGroup > 500) {
|
||||
@@ -288,13 +284,22 @@ public class RolloutManagement {
|
||||
}
|
||||
}
|
||||
|
||||
private Rollout createRolloutGroupsInNewTransaction(final int amountOfGroups,
|
||||
final RolloutGroupConditions conditions, final Rollout savedRollout) {
|
||||
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
|
||||
def.setName("creatingRollout");
|
||||
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
|
||||
return new TransactionTemplate(txManager, def)
|
||||
.execute(status -> createRolloutGroups(amountOfGroups, conditions, savedRollout));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for creating rollout groups and calculating group sizes. Group
|
||||
* sizes are calculated by dividing the total count of targets through the
|
||||
* amount of given groups. In same cases this will lead to less rollout
|
||||
* groups than given by client.
|
||||
*
|
||||
* @param amountGroup
|
||||
*
|
||||
* @param amountOfGroups
|
||||
* the amount of groups
|
||||
* @param conditions
|
||||
* the rollout group conditions
|
||||
@@ -302,17 +307,17 @@ public class RolloutManagement {
|
||||
* the rollout
|
||||
* @return the rollout with created groups
|
||||
*/
|
||||
private Rollout createRolloutGroups(final int amountGroup, final RolloutGroupConditions conditions,
|
||||
private Rollout createRolloutGroups(final int amountOfGroups, final RolloutGroupConditions conditions,
|
||||
final Rollout savedRollout) {
|
||||
int pageIndex = 0;
|
||||
int groupIndex = 0;
|
||||
final Long totalCount = savedRollout.getTotalTargets();
|
||||
final int groupSize = (int) Math.ceil((double) totalCount / (double) amountGroup);
|
||||
final int groupSize = (int) Math.ceil((double) totalCount / (double) amountOfGroups);
|
||||
// validate if the amount of groups that will be created are the amount
|
||||
// of groups that the client what's to have created.
|
||||
int amountGroupValidated = amountGroup;
|
||||
int amountGroupValidated = amountOfGroups;
|
||||
final int amountGroupCreation = (int) (Math.ceil((double) totalCount / (double) groupSize));
|
||||
if (amountGroupCreation == (amountGroup - 1)) {
|
||||
if (amountGroupCreation == (amountOfGroups - 1)) {
|
||||
amountGroupValidated--;
|
||||
}
|
||||
RolloutGroup lastSavedGroup = null;
|
||||
@@ -356,17 +361,19 @@ public class RolloutManagement {
|
||||
* for each affected target in the rollout. The actions of the first group
|
||||
* will be started immediately {@link RolloutGroupStatus#RUNNING} as the
|
||||
* other groups will be {@link RolloutGroupStatus#SCHEDULED} state.
|
||||
*
|
||||
*
|
||||
* The rollout itself will be then also in {@link RolloutStatus#RUNNING}.
|
||||
*
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout to be started
|
||||
*
|
||||
*
|
||||
* @return started rollout
|
||||
*
|
||||
* @throws RolloutIllegalStateException
|
||||
* if given rollout is not in {@link RolloutStatus#READY}. Only
|
||||
* ready rollouts can be started.
|
||||
*/
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
@@ -383,12 +390,14 @@ public class RolloutManagement {
|
||||
* actions of the first group will be started immediately
|
||||
* {@link RolloutGroupStatus#RUNNING} as the other groups will be
|
||||
* {@link RolloutGroupStatus#SCHEDULED} state.
|
||||
*
|
||||
*
|
||||
* The rollout itself will be then also in {@link RolloutStatus#RUNNING}.
|
||||
*
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout to be started
|
||||
*
|
||||
*
|
||||
* @return the started rollout
|
||||
*
|
||||
* @throws RolloutIllegalStateException
|
||||
* if given rollout is not in {@link RolloutStatus#READY}. Only
|
||||
* ready rollouts can be started.
|
||||
@@ -456,19 +465,19 @@ public class RolloutManagement {
|
||||
* {@link RolloutGroupStatus#SCHEDULED} will not be started and keep in
|
||||
* {@link RolloutGroupStatus#SCHEDULED} state until the rollout is
|
||||
* {@link RolloutManagement#resumeRollout(Rollout)}.
|
||||
*
|
||||
*
|
||||
* Switching the rollout status to {@link RolloutStatus#PAUSED} is
|
||||
* sufficient due the {@link #checkRunningRollouts(long)} will not check
|
||||
* this rollout anymore.
|
||||
*
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout to be paused.
|
||||
*
|
||||
*
|
||||
* @throws RolloutIllegalStateException
|
||||
* if given rollout is not in {@link RolloutStatus#RUNNING}.
|
||||
* Only running rollouts can be paused.
|
||||
*/
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
@@ -491,14 +500,14 @@ public class RolloutManagement {
|
||||
* Resumes a paused rollout. The rollout switches back to
|
||||
* {@link RolloutStatus#RUNNING} state which is then picked up again by the
|
||||
* {@link #checkRunningRollouts(long)}.
|
||||
*
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout to be resumed
|
||||
* @throws RolloutIllegalStateException
|
||||
* if given rollout is not in {@link RolloutStatus#PAUSED}. Only
|
||||
* paused rollouts can be resumed.
|
||||
*/
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
@@ -516,7 +525,7 @@ public class RolloutManagement {
|
||||
* Checking running rollouts. Rollouts which are checked updating the
|
||||
* {@link Rollout#setLastCheck(long)} to indicate that the current instance
|
||||
* is handling the specific rollout. This code should run as system-code.
|
||||
*
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* SystemSecurityContext.runAsSystem(new Callable<Void>() {
|
||||
@@ -526,21 +535,21 @@ public class RolloutManagement {
|
||||
* });
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* This method is attend to be called by a scheduler.
|
||||
* {@link RolloutScheduler}. And must be running in an transaction so it's
|
||||
* splitted from the scheduler.
|
||||
*
|
||||
*
|
||||
* Rollouts which are currently running are investigated, by means the
|
||||
* error- and finish condition of running groups in this rollout are
|
||||
* evaluated.
|
||||
*
|
||||
*
|
||||
* @param delayBetweenChecks
|
||||
* the time in milliseconds of the delay between the further and
|
||||
* this check. This check is only applied if the last check is
|
||||
* less than (lastcheck-delay).
|
||||
*/
|
||||
@Transactional
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
@@ -723,7 +732,7 @@ public class RolloutManagement {
|
||||
|
||||
/**
|
||||
* Count rollouts by specified filter text.
|
||||
*
|
||||
*
|
||||
* @param searchText
|
||||
* name or description
|
||||
* @return total count rollouts for specified filter text.
|
||||
@@ -745,7 +754,7 @@ public class RolloutManagement {
|
||||
|
||||
/**
|
||||
* * Retrieves a specific rollout by its ID.
|
||||
*
|
||||
*
|
||||
* @param pageable
|
||||
* the page request to sort and limit the result
|
||||
* @param searchText
|
||||
@@ -763,7 +772,7 @@ public class RolloutManagement {
|
||||
|
||||
/**
|
||||
* Retrieves a specific rollout by its name.
|
||||
*
|
||||
*
|
||||
* @param rolloutName
|
||||
* the name of the rollout to retrieve
|
||||
* @return the founded rollout or {@code null} if rollout with given name
|
||||
@@ -776,14 +785,14 @@ public class RolloutManagement {
|
||||
|
||||
/**
|
||||
* Update rollout details.
|
||||
*
|
||||
*
|
||||
* @param rollout
|
||||
* rollout to be updated
|
||||
*
|
||||
*
|
||||
* @return Rollout updated rollout
|
||||
*/
|
||||
@NotNull
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
|
||||
public Rollout updateRollout(@NotNull final Rollout rollout) {
|
||||
@@ -793,7 +802,7 @@ public class RolloutManagement {
|
||||
|
||||
/**
|
||||
* Get count of targets in different status in rollout.
|
||||
*
|
||||
*
|
||||
* @param page
|
||||
* the page request to sort and limit the result
|
||||
* @return a list of rollouts with details of targets count for different
|
||||
@@ -845,7 +854,7 @@ public class RolloutManagement {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkIfRolloutCanStarted(final Rollout rollout, final Rollout mergedRollout) {
|
||||
private static void checkIfRolloutCanStarted(final Rollout rollout, final Rollout mergedRollout) {
|
||||
if (!(RolloutStatus.READY.equals(mergedRollout.getStatus()))) {
|
||||
throw new RolloutIllegalStateException("Rollout can only be started in state ready but current state is "
|
||||
+ rollout.getStatus().name().toLowerCase());
|
||||
@@ -855,7 +864,7 @@ public class RolloutManagement {
|
||||
/***
|
||||
* Get finished percentage details for a specified group which is in running
|
||||
* state.
|
||||
*
|
||||
*
|
||||
* @param rolloutId
|
||||
* the ID of the {@link Rollout}
|
||||
* @param rolloutGroup
|
||||
|
||||
@@ -18,12 +18,13 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* The repository interface for the {@link Rollout} model.
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface RolloutRepository extends BaseEntityRepository<Rollout, Long>, JpaSpecificationExecutor<Rollout> {
|
||||
|
||||
/**
|
||||
@@ -40,7 +41,7 @@ public interface RolloutRepository extends BaseEntityRepository<Rollout, Long>,
|
||||
* @return the count of the updated rows. Zero if no row has been updated
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Query("UPDATE Rollout r SET r.lastCheck = :lastCheck WHERE r.lastCheck < (:lastCheck - :delay) AND r.status=:status")
|
||||
int updateLastCheck(@Param("lastCheck") final long lastCheck, @Param("delay") final long delay,
|
||||
@Param("status") final RolloutStatus status);
|
||||
|
||||
@@ -12,11 +12,14 @@ import org.eclipse.hawkbit.repository.model.RolloutTargetGroup;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutTargetGroupId;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
*
|
||||
* Spring data repository for {@link RolloutTargetGroup}.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface RolloutTargetGroupRepository
|
||||
extends CrudRepository<RolloutTargetGroup, RolloutTargetGroupId>, JpaSpecificationExecutor<RolloutTargetGroup> {
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
@@ -62,7 +63,7 @@ import com.google.common.collect.Sets;
|
||||
* Business facade for managing {@link SoftwareModule}s.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Validated
|
||||
@Service
|
||||
public class SoftwareManagement {
|
||||
@@ -108,7 +109,7 @@ public class SoftwareManagement {
|
||||
* of {@link SoftwareModule#getId()} is <code>null</code>
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
public SoftwareModule updateSoftwareModule(@NotNull final SoftwareModule sm) {
|
||||
checkNotNull(sm.getId());
|
||||
@@ -138,7 +139,7 @@ public class SoftwareManagement {
|
||||
* @return updated {@link Entity}
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
public SoftwareModuleType updateSoftwareModuleType(@NotNull final SoftwareModuleType sm) {
|
||||
checkNotNull(sm.getId());
|
||||
@@ -283,7 +284,7 @@ public class SoftwareManagement {
|
||||
* is the {@link SoftwareModule} to be deleted
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
|
||||
public void deleteSoftwareModule(@NotNull final SoftwareModule bsm) {
|
||||
|
||||
@@ -314,10 +315,10 @@ public class SoftwareManagement {
|
||||
* Deletes {@link SoftwareModule}s which is any if the given ids.
|
||||
*
|
||||
* @param ids
|
||||
* of the Software Moduels to be deleted
|
||||
* of the Software Modules to be deleted
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
|
||||
public void deleteSoftwareModules(@NotNull final Iterable<Long> ids) {
|
||||
final List<SoftwareModule> swModulesToDelete = softwareModuleRepository.findByIdIn(ids);
|
||||
@@ -579,7 +580,7 @@ public class SoftwareManagement {
|
||||
return new SliceImpl<>(resultList);
|
||||
}
|
||||
|
||||
private List<Specification<SoftwareModule>> buildSpecificationList(final String searchText,
|
||||
private static List<Specification<SoftwareModule>> buildSpecificationList(final String searchText,
|
||||
final SoftwareModuleType type) {
|
||||
final List<Specification<SoftwareModule>> specList = new ArrayList<>();
|
||||
if (!Strings.isNullOrEmpty(searchText)) {
|
||||
@@ -700,7 +701,7 @@ public class SoftwareManagement {
|
||||
* @return created {@link Entity}
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
|
||||
public SoftwareModuleType createSoftwareModuleType(@NotNull final SoftwareModuleType type) {
|
||||
if (type.getId() != null) {
|
||||
@@ -718,7 +719,7 @@ public class SoftwareManagement {
|
||||
* @return created {@link Entity}
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
|
||||
public List<SoftwareModuleType> createSoftwareModuleType(@NotNull final Collection<SoftwareModuleType> types) {
|
||||
return types.stream().map(this::createSoftwareModuleType).collect(Collectors.toList());
|
||||
@@ -731,7 +732,7 @@ public class SoftwareManagement {
|
||||
* to delete
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
|
||||
public void deleteSoftwareModuleType(@NotNull final SoftwareModuleType type) {
|
||||
|
||||
@@ -785,7 +786,7 @@ public class SoftwareManagement {
|
||||
* in case the meta data entry already exists for the specific
|
||||
* key
|
||||
*/
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
public SoftwareModuleMetadata createSoftwareModuleMetadata(@NotNull final SoftwareModuleMetadata metadata) {
|
||||
@@ -810,7 +811,7 @@ public class SoftwareManagement {
|
||||
* in case one of the meta data entry already exists for the
|
||||
* specific key
|
||||
*/
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
public List<SoftwareModuleMetadata> createSoftwareModuleMetadata(
|
||||
@@ -832,7 +833,7 @@ public class SoftwareManagement {
|
||||
* in case the meta data entry does not exists and cannot be
|
||||
* updated
|
||||
*/
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
public SoftwareModuleMetadata updateSoftwareModuleMetadata(@NotNull final SoftwareModuleMetadata metadata) {
|
||||
@@ -851,7 +852,7 @@ public class SoftwareManagement {
|
||||
* @param id
|
||||
* the ID of the software module meta data to delete
|
||||
*/
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
public void deleteSoftwareModuleMetadata(@NotNull final SwMetadataCompositeKey id) {
|
||||
@@ -925,7 +926,7 @@ public class SoftwareManagement {
|
||||
}
|
||||
}
|
||||
|
||||
private void throwMetadataKeyAlreadyExists(final String metadataKey) {
|
||||
private static void throwMetadataKeyAlreadyExists(final String metadataKey) {
|
||||
throw new EntityAlreadyExistsException("Metadata entry with key '" + metadataKey + "' already exists");
|
||||
}
|
||||
|
||||
|
||||
@@ -16,15 +16,14 @@ import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link SoftwareModuleMetadata} repository.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface SoftwareModuleMetadataRepository
|
||||
extends PagingAndSortingRepository<SoftwareModuleMetadata, SwMetadataCompositeKey>,
|
||||
JpaSpecificationExecutor<SoftwareModuleMetadata> {
|
||||
|
||||
@@ -21,15 +21,14 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link SoftwareModule} repository.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface SoftwareModuleRepository
|
||||
extends BaseEntityRepository<SoftwareModule, Long>, JpaSpecificationExecutor<SoftwareModule> {
|
||||
|
||||
@@ -69,7 +68,7 @@ public interface SoftwareModuleRepository
|
||||
*
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Query("UPDATE SoftwareModule b SET b.deleted = 1, b.lastModifiedAt = :lastModifiedAt, b.lastModifiedBy = :lastModifiedBy WHERE b.id IN :ids")
|
||||
void deleteSoftwareModule(@Param("lastModifiedAt") Long modifiedAt, @Param("lastModifiedBy") String modifiedBy,
|
||||
@Param("ids") final Long... ids);
|
||||
|
||||
@@ -12,16 +12,14 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Repository for {@link SoftwareModuleType}.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface SoftwareModuleTypeRepository
|
||||
extends BaseEntityRepository<SoftwareModuleType, Long>, JpaSpecificationExecutor<SoftwareModuleType> {
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@@ -43,7 +44,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
* Central system management operations of the SP server.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Validated
|
||||
@Service
|
||||
public class SystemManagement {
|
||||
@@ -164,6 +165,7 @@ public class SystemManagement {
|
||||
* @return the {@link CurrentTenantKeyGenerator}
|
||||
*/
|
||||
@Bean
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
public CurrentTenantKeyGenerator currentTenantKeyGenerator() {
|
||||
return new CurrentTenantKeyGenerator();
|
||||
}
|
||||
@@ -179,7 +181,7 @@ public class SystemManagement {
|
||||
* @return
|
||||
*/
|
||||
@Cacheable(value = "tenantMetadata", key = "#tenant.toUpperCase()")
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
@NotNull
|
||||
public TenantMetaData getTenantMetadata(@NotNull final String tenant) {
|
||||
@@ -206,7 +208,6 @@ public class SystemManagement {
|
||||
@NotNull
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
// tenant independent
|
||||
public List<String> findTenants() {
|
||||
return tenantMetaDataRepository.findAll().stream().map(md -> md.getTenant()).collect(Collectors.toList());
|
||||
}
|
||||
@@ -218,10 +219,9 @@ public class SystemManagement {
|
||||
* to delete
|
||||
*/
|
||||
@CacheEvict(value = { "tenantMetadata" }, key = "#tenant.toUpperCase()")
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN)
|
||||
// tenant independent
|
||||
public void deleteTenant(@NotNull final String tenant) {
|
||||
cacheManager.evictCaches(tenant);
|
||||
cacheManager.getCache("currentTenant").evict(currentTenantKeyGenerator().generate(null, null));
|
||||
@@ -250,7 +250,7 @@ public class SystemManagement {
|
||||
* @return {@link TenantMetaData} of {@link TenantAware#getCurrentTenant()}
|
||||
*/
|
||||
@Cacheable(value = "tenantMetadata", keyGenerator = "tenantKeyGenerator")
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
@NotNull
|
||||
public TenantMetaData getTenantMetadata() {
|
||||
@@ -279,7 +279,7 @@ public class SystemManagement {
|
||||
// suspend the transaction here to do a read-request against the medata
|
||||
// table, when the current
|
||||
// tenant is not cached anyway already.
|
||||
@Transactional(propagation = Propagation.NOT_SUPPORTED)
|
||||
@Transactional(propagation = Propagation.NOT_SUPPORTED, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public String currentTenant() {
|
||||
final String initialTenantCreation = createInitialTenant.get();
|
||||
if (initialTenantCreation == null) {
|
||||
@@ -298,7 +298,7 @@ public class SystemManagement {
|
||||
* @return updated {@link TenantMetaData} entity
|
||||
*/
|
||||
@CachePut(value = "tenantMetadata", key = "#metaData.tenant.toUpperCase()")
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
@NotNull
|
||||
public TenantMetaData updateTenantMetadata(@NotNull final TenantMetaData metaData) {
|
||||
@@ -342,6 +342,8 @@ public class SystemManagement {
|
||||
*/
|
||||
private class CurrentTenantKeyGenerator implements KeyGenerator {
|
||||
@Override
|
||||
// Exception squid:S923 - override
|
||||
@SuppressWarnings({ "squid:S923" })
|
||||
public Object generate(final Object target, final Method method, final Object... params) {
|
||||
final String initialTenantCreation = createInitialTenant.get();
|
||||
if (initialTenantCreation == null) {
|
||||
|
||||
@@ -38,21 +38,17 @@ import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
*
|
||||
* Mangement service class for {@link Tag}s.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* Management service class for {@link Tag}s.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Validated
|
||||
@Service
|
||||
public class TagManagement {
|
||||
@@ -102,7 +98,7 @@ public class TagManagement {
|
||||
* if given object already exists
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@NotNull
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
|
||||
public TargetTag createTargetTag(@NotNull final TargetTag targetTag) {
|
||||
@@ -133,7 +129,7 @@ public class TagManagement {
|
||||
* if given object has already an ID.
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@NotNull
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
|
||||
public List<TargetTag> createTargetTags(@NotNull final Iterable<TargetTag> targetTags) {
|
||||
@@ -155,7 +151,7 @@ public class TagManagement {
|
||||
* tag name of the {@link TargetTag} to be deleted
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_TARGET)
|
||||
public void deleteTargetTag(@NotEmpty final String targetTagName) {
|
||||
final TargetTag tag = targetTagRepository.findByNameEquals(targetTagName);
|
||||
@@ -220,7 +216,7 @@ public class TagManagement {
|
||||
* @return the new {@link TargetTag}
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@NotNull
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||
public TargetTag updateTargetTag(@NotNull final TargetTag targetTag) {
|
||||
@@ -254,7 +250,7 @@ public class TagManagement {
|
||||
*
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
|
||||
public DistributionSetTag createDistributionSetTag(@NotNull final DistributionSetTag distributionSetTag) {
|
||||
if (null != distributionSetTag.getId()) {
|
||||
@@ -282,7 +278,7 @@ public class TagManagement {
|
||||
* if a given entity already exists
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
|
||||
public List<DistributionSetTag> createDistributionSetTags(
|
||||
@NotNull final Iterable<DistributionSetTag> distributionSetTags) {
|
||||
@@ -306,7 +302,7 @@ public class TagManagement {
|
||||
* to be deleted
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
|
||||
public void deleteDistributionSetTag(@NotEmpty final String tagName) {
|
||||
final DistributionSetTag tag = distributionSetTagRepository.findByNameEquals(tagName);
|
||||
@@ -335,7 +331,7 @@ public class TagManagement {
|
||||
* of {@link DistributionSetTag#getName()} is <code>null</code>
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@NotNull
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
public DistributionSetTag updateDistributionSetTag(@NotNull final DistributionSetTag distributionSetTag) {
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.springframework.data.jpa.domain.Specifications;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@@ -35,10 +36,8 @@ import com.google.common.base.Strings;
|
||||
/**
|
||||
* Business service facade for managing {@link TargetFilterQuery}s.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Validated
|
||||
@Service
|
||||
public class TargetFilterQueryManagement {
|
||||
@@ -53,7 +52,7 @@ public class TargetFilterQueryManagement {
|
||||
* @return the created {@link TargetFilterQuery}
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@NotNull
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
|
||||
public TargetFilterQuery createTargetFilterQuery(@NotNull final TargetFilterQuery customTargetFilter) {
|
||||
@@ -71,7 +70,7 @@ public class TargetFilterQueryManagement {
|
||||
* IDs of target filter query to be deleted
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_TARGET)
|
||||
public void deleteTargetFilterQuery(@NotNull final Long targetFilterQueryId) {
|
||||
targetFilterQueryRepository.delete(targetFilterQueryId);
|
||||
@@ -161,7 +160,7 @@ public class TargetFilterQueryManagement {
|
||||
* @return the updated {@link TargetFilterQuery}
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@NotNull
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||
public TargetFilterQuery updateTargetFilterQuery(@NotNull final TargetFilterQuery targetFilterQuery) {
|
||||
|
||||
@@ -12,13 +12,14 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
*
|
||||
* Spring data repositories for {@link TargetFilterQuery}s.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface TargetFilterQueryRepository
|
||||
extends BaseEntityRepository<TargetFilterQuery, Long>, JpaSpecificationExecutor<TargetFilterQuery> {
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.transaction.Transactional;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.TargetInfo;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
@@ -20,15 +19,16 @@ import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Usually a JPA spring data repository to handle {@link TargetInfo} entity.
|
||||
* However, do to an eclipselink bug with spring boot now a regular interface
|
||||
* that is implemented by {@link EclipseLinkTargetInfoRepository}.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface TargetInfoRepository {
|
||||
|
||||
/**
|
||||
@@ -41,7 +41,7 @@ public interface TargetInfoRepository {
|
||||
* to set it for
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Query("update TargetInfo ti set ti.updateStatus = :status where ti.targetId in :targets and ti.updateStatus != :status")
|
||||
void setTargetUpdateStatus(@Param("status") TargetUpdateStatus status, @Param("targets") List<Long> targets);
|
||||
|
||||
@@ -63,7 +63,7 @@ public interface TargetInfoRepository {
|
||||
* to delete
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true)
|
||||
void deleteByTargetIdIn(final Collection<Long> targetIDs);
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@@ -74,7 +75,7 @@ import com.google.common.eventbus.EventBus;
|
||||
* Business service facade for managing {@link Target}s.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Validated
|
||||
@Service
|
||||
public class TargetManagement {
|
||||
@@ -260,7 +261,7 @@ public class TargetManagement {
|
||||
* @return the updated {@link Target}
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@NotNull
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_CONTROLLER)
|
||||
@@ -278,7 +279,7 @@ public class TargetManagement {
|
||||
* @return the updated {@link Target}s
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@NotNull
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_CONTROLLER)
|
||||
@@ -294,7 +295,7 @@ public class TargetManagement {
|
||||
* the technical IDs of the targets to be deleted
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_TARGET)
|
||||
public void deleteTargets(@NotEmpty final Long... targetIDs) {
|
||||
// we need to select the target IDs first to check the if the targetIDs
|
||||
@@ -527,11 +528,11 @@ public class TargetManagement {
|
||||
* @param targets
|
||||
* to toggle for
|
||||
* @param tag
|
||||
* to toogle
|
||||
* @return TagAssigmentResult with all metadata of the assigment outcome.
|
||||
* to toggle
|
||||
* @return TagAssigmentResult with all metadata of the assignment outcome.
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@NotNull
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||
public TargetTagAssignmentResult toggleTagAssignment(@NotEmpty final List<Target> targets,
|
||||
@@ -553,7 +554,7 @@ public class TargetManagement {
|
||||
* @return TagAssigmentResult with all metadata of the assigment outcome.
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@NotNull
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||
public TargetTagAssignmentResult toggleTagAssignment(@NotEmpty final Collection<String> targetIds,
|
||||
@@ -596,7 +597,7 @@ public class TargetManagement {
|
||||
* @return list of assigned targets
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@NotNull
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||
public List<Target> assignTag(@NotEmpty final Collection<String> targetIds, @NotNull final TargetTag tag) {
|
||||
@@ -635,7 +636,7 @@ public class TargetManagement {
|
||||
* @return list of unassigned targets
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@NotNull
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||
public List<Target> unAssignAllTargetsByTag(@NotNull final TargetTag tag) {
|
||||
@@ -652,7 +653,7 @@ public class TargetManagement {
|
||||
* @return the unassigned target or <null> if no target is unassigned
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||
public Target unAssignTag(@NotNull final String controllerID, @NotNull final TargetTag targetTag) {
|
||||
final List<Target> allTargets = targetRepository
|
||||
@@ -934,7 +935,7 @@ public class TargetManagement {
|
||||
* @return
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@NotNull
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_CONTROLLER)
|
||||
@@ -972,7 +973,7 @@ public class TargetManagement {
|
||||
*
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@NotNull
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_CONTROLLER)
|
||||
@@ -996,7 +997,7 @@ public class TargetManagement {
|
||||
* already exist.
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@NotNull
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
|
||||
public List<Target> createTargets(@NotNull final List<Target> targets) {
|
||||
@@ -1028,7 +1029,7 @@ public class TargetManagement {
|
||||
* @return newly created target
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@NotNull
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
|
||||
public List<Target> createTargets(@NotNull final Collection<Target> targets,
|
||||
|
||||
@@ -27,15 +27,14 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link Target} repository.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface TargetRepository extends BaseEntityRepository<Target, Long>, JpaSpecificationExecutor<Target> {
|
||||
|
||||
/**
|
||||
@@ -64,7 +63,7 @@ public interface TargetRepository extends BaseEntityRepository<Target, Long>, Jp
|
||||
* to be deleted
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
|
||||
@Query("DELETE FROM Target t WHERE t.id IN ?1")
|
||||
void deleteByIdIn(final Collection<Long> targetIDs);
|
||||
@@ -153,7 +152,7 @@ public interface TargetRepository extends BaseEntityRepository<Target, Long>, Jp
|
||||
*/
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true)
|
||||
<S extends Target> List<S> save(Iterable<S> entities);
|
||||
|
||||
@@ -167,7 +166,7 @@ public interface TargetRepository extends BaseEntityRepository<Target, Long>, Jp
|
||||
*/
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true)
|
||||
<S extends Target> S save(S entity);
|
||||
|
||||
@@ -276,7 +275,7 @@ public interface TargetRepository extends BaseEntityRepository<Target, Long>, Jp
|
||||
* to update
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Query("UPDATE Target t SET t.assignedDistributionSet = :set, t.lastModifiedAt = :lastModifiedAt, t.lastModifiedBy = :lastModifiedBy WHERE t.id IN :targets")
|
||||
void setAssignedDistributionSet(@Param("set") DistributionSet set, @Param("lastModifiedAt") Long modifiedAt,
|
||||
@Param("lastModifiedBy") String modifiedBy, @Param("targets") Collection<Long> targets);
|
||||
|
||||
@@ -13,15 +13,14 @@ import java.util.List;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* {@link TargetTag} repository.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface TargetTagRepository
|
||||
extends BaseEntityRepository<TargetTag, Long>, JpaSpecificationExecutor<TargetTag> {
|
||||
|
||||
@@ -33,7 +32,7 @@ public interface TargetTagRepository
|
||||
* @return 1 if tag was deleted
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
Long deleteByName(final String tagName);
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,18 +24,19 @@ import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* Central tenant configuration management operations of the SP server.
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Validated
|
||||
@Service
|
||||
public class TenantConfigurationManagement implements EnvironmentAware {
|
||||
|
||||
private static final TenantConfigurationManagement INSTANCE = new TenantConfigurationManagement();
|
||||
|
||||
@Autowired
|
||||
private TenantConfigurationRepository tenantConfigurationRepository;
|
||||
|
||||
@@ -46,16 +47,6 @@ public class TenantConfigurationManagement implements EnvironmentAware {
|
||||
|
||||
private Environment environment;
|
||||
|
||||
/**
|
||||
* Get Singleton instance, needed for classes which are not managed in
|
||||
* Spring context
|
||||
*
|
||||
* @return singleton instance of TenantConfigurationManagement
|
||||
*/
|
||||
public static TenantConfigurationManagement getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a configuration value from the e.g. tenant overwritten
|
||||
* configuration values or in case the tenant does not a have a specific
|
||||
@@ -160,7 +151,8 @@ public class TenantConfigurationManagement implements EnvironmentAware {
|
||||
* if the property cannot be converted to the given
|
||||
* {@code propertyType}
|
||||
*/
|
||||
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
|
||||
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
public TenantConfigurationValue<?> getConfigurationValue(final TenantConfigurationKey configurationKey) {
|
||||
return getConfigurationValue(configurationKey, configurationKey.getDataType());
|
||||
}
|
||||
@@ -185,7 +177,8 @@ public class TenantConfigurationManagement implements EnvironmentAware {
|
||||
* if the property cannot be converted to the given
|
||||
* {@code propertyType}
|
||||
*/
|
||||
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
|
||||
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION + SpringEvalExpressions.HAS_AUTH_OR
|
||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
public <T> T getGlobalConfigurationValue(final TenantConfigurationKey configurationKey,
|
||||
final Class<T> propertyType) {
|
||||
|
||||
@@ -221,7 +214,7 @@ public class TenantConfigurationManagement implements EnvironmentAware {
|
||||
* if the property cannot be converted to the given
|
||||
*/
|
||||
@CacheEvict(value = "tenantConfiguration", key = "#configurationKey.getKeyName()")
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
|
||||
public <T> TenantConfigurationValue<T> addOrUpdateConfiguration(final TenantConfigurationKey configurationKey,
|
||||
@@ -264,7 +257,7 @@ public class TenantConfigurationManagement implements EnvironmentAware {
|
||||
* the configuration key to be deleted
|
||||
*/
|
||||
@CacheEvict(value = "tenantConfiguration", key = "#configurationKey.getKeyName()")
|
||||
@Transactional
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
@Modifying
|
||||
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
|
||||
public void deleteConfiguration(final TenantConfigurationKey configurationKey) {
|
||||
|
||||
@@ -11,13 +11,14 @@ package org.eclipse.hawkbit.repository;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.TenantConfiguration;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* The spring-data repository for the entity {@link TenantConfiguration}.
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface TenantConfigurationRepository extends BaseEntityRepository<TenantConfiguration, Long> {
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,16 +12,14 @@ import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.TenantMetaData;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* repository for operations on {@link TenantMetaData} entity.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
|
||||
public interface TenantMetaDataRepository extends PagingAndSortingRepository<TenantMetaData, Long> {
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,13 +24,18 @@ import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.eclipse.persistence.annotations.ExistenceChecking;
|
||||
import org.eclipse.persistence.annotations.ExistenceType;
|
||||
|
||||
/**
|
||||
* @author Michael Hirsch
|
||||
*
|
||||
* Entity with JPA annotation to store the information which {@link Target} is
|
||||
* in a specific {@link RolloutGroup}.
|
||||
*
|
||||
*/
|
||||
@IdClass(RolloutTargetGroupId.class)
|
||||
@Entity
|
||||
@Table(name = "sp_rollouttargetgroup")
|
||||
@ExistenceChecking(ExistenceType.ASSUME_NON_EXISTENCE)
|
||||
public class RolloutTargetGroup implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -38,7 +38,8 @@ import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.model.helper.SystemSecurityContextHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
|
||||
import org.eclipse.persistence.annotations.CascadeOnDelete;
|
||||
@@ -245,18 +246,21 @@ public class TargetInfo implements Persistable<Long>, Serializable {
|
||||
if (lastTargetQuery == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Duration pollTime = DurationHelper.formattedStringToDuration(TenantConfigurationManagement.getInstance()
|
||||
.getConfigurationValue(TenantConfigurationKey.POLLING_TIME_INTERVAL, String.class).getValue());
|
||||
final Duration overdueTime = DurationHelper.formattedStringToDuration(TenantConfigurationManagement
|
||||
.getInstance().getConfigurationValue(TenantConfigurationKey.POLLING_OVERDUE_TIME_INTERVAL, String.class)
|
||||
.getValue());
|
||||
final LocalDateTime currentDate = LocalDateTime.now();
|
||||
final LocalDateTime lastPollDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(lastTargetQuery),
|
||||
ZoneId.systemDefault());
|
||||
final LocalDateTime nextPollDate = lastPollDate.plus(pollTime);
|
||||
final LocalDateTime overdueDate = nextPollDate.plus(overdueTime);
|
||||
return new PollStatus(lastPollDate, nextPollDate, overdueDate, currentDate);
|
||||
return SystemSecurityContextHolder.getInstance().getSystemSecurityContext().runAsSystem(() -> {
|
||||
final Duration pollTime = DurationHelper.formattedStringToDuration(TenantConfigurationManagementHolder
|
||||
.getInstance().getTenantConfigurationManagement()
|
||||
.getConfigurationValue(TenantConfigurationKey.POLLING_TIME_INTERVAL, String.class).getValue());
|
||||
final Duration overdueTime = DurationHelper.formattedStringToDuration(
|
||||
TenantConfigurationManagementHolder.getInstance().getTenantConfigurationManagement()
|
||||
.getConfigurationValue(TenantConfigurationKey.POLLING_OVERDUE_TIME_INTERVAL, String.class)
|
||||
.getValue());
|
||||
final LocalDateTime currentDate = LocalDateTime.now();
|
||||
final LocalDateTime lastPollDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(lastTargetQuery),
|
||||
ZoneId.systemDefault());
|
||||
final LocalDateTime nextPollDate = lastPollDate.plus(pollTime);
|
||||
final LocalDateTime overdueDate = nextPollDate.plus(overdueTime);
|
||||
return new PollStatus(lastPollDate, nextPollDate, overdueDate, currentDate);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.model.helper;
|
||||
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* A singleton bean which holds {@link SystemSecurityContext} service and makes
|
||||
* it accessible to beans which are not managed by spring, e.g. JPA entities.
|
||||
*/
|
||||
public final class SystemSecurityContextHolder {
|
||||
|
||||
private static final SystemSecurityContextHolder INSTANCE = new SystemSecurityContextHolder();
|
||||
|
||||
@Autowired
|
||||
private SystemSecurityContext systemSecurityContext;
|
||||
|
||||
private SystemSecurityContextHolder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the singleton {@link SystemSecurityContextHolder} instance
|
||||
*/
|
||||
public static SystemSecurityContextHolder getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link SystemSecurityContext} service
|
||||
*/
|
||||
public SystemSecurityContext getSystemSecurityContext() {
|
||||
return systemSecurityContext;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 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.model.helper;
|
||||
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* A singleton bean which holds {@link TenantConfigurationManagement} service
|
||||
* and makes it accessible to beans which are not managed by spring, e.g. JPA
|
||||
* entities.
|
||||
*/
|
||||
public final class TenantConfigurationManagementHolder {
|
||||
|
||||
private static final TenantConfigurationManagementHolder INSTANCE = new TenantConfigurationManagementHolder();
|
||||
|
||||
@Autowired
|
||||
private TenantConfigurationManagement tenantConfiguration;
|
||||
|
||||
private TenantConfigurationManagementHolder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the singleton {@link TenantConfigurationManagementHolder}
|
||||
* instance
|
||||
*/
|
||||
public static TenantConfigurationManagementHolder getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link TenantConfigurationManagement} service
|
||||
*/
|
||||
public TenantConfigurationManagement getTenantConfigurationManagement() {
|
||||
return tenantConfiguration;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -66,10 +66,6 @@ import cz.jirutka.rsql.parser.ast.RSQLVisitor;
|
||||
* <li>name==targetId1,description==plugAndPlay,updateStatus==UNKNOWN</li>
|
||||
* <li>name==targetId1 or description==plugAndPlay or updateStatus==UNKNOWN</li>
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public final class RSQLUtility {
|
||||
|
||||
@@ -279,6 +275,9 @@ public final class RSQLUtility {
|
||||
}
|
||||
|
||||
@Override
|
||||
// Exception squid:S2095 - see
|
||||
// https://jira.sonarsource.com/browse/SONARJAVA-1478
|
||||
@SuppressWarnings({ "squid:S2095" })
|
||||
public List<Predicate> visit(final ComparisonNode node, final String param) {
|
||||
A fieldName = null;
|
||||
try {
|
||||
@@ -304,6 +303,9 @@ public final class RSQLUtility {
|
||||
return mapToPredicate(node, fieldPath, node.getArguments(), transformedValue, fieldName);
|
||||
}
|
||||
|
||||
// Exception squid:S2095 - see
|
||||
// https://jira.sonarsource.com/browse/SONARJAVA-1478
|
||||
@SuppressWarnings({ "squid:S2095" })
|
||||
private List<String> getExpectedFieldList() {
|
||||
final List<String> expectedFieldList = Arrays.stream(enumType.getEnumConstants())
|
||||
.filter(enumField -> enumField.getSubEntityAttributes().isEmpty()).map(enumField -> {
|
||||
@@ -390,7 +392,9 @@ public final class RSQLUtility {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
// Exception squid:S2095 - see
|
||||
// https://jira.sonarsource.com/browse/SONARJAVA-1478
|
||||
@SuppressWarnings({ "rawtypes", "unchecked", "squid:S2095" })
|
||||
private Object transformEnumValue(final ComparisonNode node, final String value,
|
||||
final Class<? extends Object> javaType) {
|
||||
final Class<? extends Enum> tmpEnumType = (Class<? extends Enum>) javaType;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 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.specifications;
|
||||
|
||||
import javax.persistence.criteria.Join;
|
||||
import javax.persistence.criteria.ListJoin;
|
||||
import javax.persistence.criteria.SetJoin;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action_;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet_;
|
||||
import org.eclipse.hawkbit.repository.model.LocalArtifact;
|
||||
import org.eclipse.hawkbit.repository.model.LocalArtifact_;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule_;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
/**
|
||||
* Utility class for {@link Action}s {@link Specification}s. The class provides
|
||||
* Spring Data JPQL Specifications.
|
||||
*
|
||||
*/
|
||||
public class ActionSpecifications {
|
||||
|
||||
private ActionSpecifications() {
|
||||
// utility class
|
||||
}
|
||||
|
||||
/**
|
||||
* Specification which joins all necessary tables to retrieve the dependency
|
||||
* between a target and a local file assignment through the assigen action
|
||||
* of the target. All actions are included, not only active actions.
|
||||
*
|
||||
* @param target
|
||||
* the target to verfiy if the given artifact is currently
|
||||
* assigned or had been assigned
|
||||
* @param localArtifact
|
||||
* the local artifact to check wherever the target had ever been
|
||||
* assigned
|
||||
* @return a specification to use with spring JPA
|
||||
*/
|
||||
public static Specification<Action> hasTargetAssignedArtifact(final Target target,
|
||||
final LocalArtifact localArtifact) {
|
||||
return (actionRoot, query, criteriaBuilder) -> {
|
||||
final Join<Action, DistributionSet> dsJoin = actionRoot.join(Action_.distributionSet);
|
||||
final SetJoin<DistributionSet, SoftwareModule> modulesJoin = dsJoin.join(DistributionSet_.modules);
|
||||
final ListJoin<SoftwareModule, LocalArtifact> artifactsJoin = modulesJoin.join(SoftwareModule_.artifacts);
|
||||
return criteriaBuilder.and(
|
||||
criteriaBuilder.equal(artifactsJoin.get(LocalArtifact_.filename), localArtifact.getFilename()),
|
||||
criteriaBuilder.equal(actionRoot.get(Action_.target), target));
|
||||
};
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user