Merge branch 'master' into Minimize_maximize_status_popup

This commit is contained in:
Asharani
2016-05-16 16:33:04 +05:30
119 changed files with 1304 additions and 772 deletions

View File

@@ -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: * The master branch contains future development towards 0.2. We are currently focusing on:
* Rollout Management for large scale rollouts. * Rollout Management for large scale rollouts.
* Clustering capabilities for the update server. * 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. * And of course tons of usability improvements and bug fixes.
# Modules # Modules
`hawkbit-core` : core elements. `hawkbit-core` : internal interfaces and utility classes..
`hawkbit-security-core` : core security elements. `hawkbit-security-core` : authentication and authorization.
`hawkbit-security-integration` : security integration elements to integrate security into hawkbit. `hawkbit-security-integration` : authentication and authorization integrated with the APIs.
`hawkbit-artifact-repository-mongo` : artifact repository implementation to mongoDB. `hawkbit-artifact-repository-mongo` : artifact repository implementation to MongoDB.
`hawkbit-autoconfigure` : spring-boot auto-configuration. `hawkbit-autoconfigure` : spring-boot auto-configuration.
`hawkbit-dmf-api` : API for the Device Management Integration. `hawkbit-dmf-api` : API for the Device Management Integration.
`hawkbit-dmf-amqp` : AMQP endpoint implementation for the DMF API. `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-http-security` : implementation for security filters for HTTP.
`hawkbit-rest-api` : API classes for the REST Management API. `hawkbit-rest-api` : API classes for the REST Management API.
`hawkbit-rest-resource` : HTTP REST endpoints for the Management and the Direct Device API. `hawkbit-rest-resource` : HTTP REST endpoints for the Management and the Direct Device API.

View File

@@ -22,14 +22,17 @@ import org.springframework.context.annotation.Import;
@SpringBootApplication @SpringBootApplication
@Import({ RepositoryApplicationConfiguration.class }) @Import({ RepositoryApplicationConfiguration.class })
@EnableHawkbitManagedSecurityConfiguration @EnableHawkbitManagedSecurityConfiguration
// Exception squid:S1118 - Spring boot standard behavior
@SuppressWarnings({ "squid:S1118" })
public class Start { public class Start {
/** /**
* Main method to start the spring-boot application. * Main method to start the spring-boot application.
* *
* @param args * @param args
* the VM arguments. * the VM arguments.
*/ */
// Exception squid:S2095 - Spring boot standard behavior
@SuppressWarnings({ "squid:S2095" })
public static void main(final String[] args) { public static void main(final String[] args) {
SpringApplication.run(Start.class, args); SpringApplication.run(Start.class, args);
} }

View File

@@ -1,4 +1,3 @@
<?xml version="1.0"?>
<!-- <!--
Copyright (c) 2015 Bosch Software Innovations GmbH and others. Copyright (c) 2015 Bosch Software Innovations GmbH and others.
@@ -9,7 +8,8 @@
http://www.eclipse.org/legal/epl-v10.html 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"> xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
@@ -133,6 +133,10 @@
<artifactId>spring-boot-configuration-processor</artifactId> <artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
</dependencies> </dependencies>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>

View File

@@ -8,6 +8,8 @@
*/ */
package org.eclipse.hawkbit.simulator; package org.eclipse.hawkbit.simulator;
import org.eclipse.hawkbit.simulator.UpdateStatus.ResponseStatus;
/** /**
* The bean of a simulated device which can be stored in the * The bean of a simulated device which can be stored in the
* {@link DeviceSimulatorRepository} or shown in the UI. * {@link DeviceSimulatorRepository} or shown in the UI.
@@ -22,16 +24,15 @@ public abstract class AbstractSimulatedDevice {
private Status status; private Status status;
private double progress; private double progress;
private String swversion = "unknown"; 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 Protocol protocol = Protocol.DMF_AMQP;
private String targetSecurityToken;
private int nextPollCounterSec; private int nextPollCounterSec;
/** /**
* Enum definition of the protocol to be used for the simulated device. * Enum definition of the protocol to be used for the simulated device.
* *
* @author Michael Hirsch
*
*/ */
public enum Protocol { public enum Protocol {
/** /**
@@ -69,24 +70,6 @@ public abstract class AbstractSimulatedDevice {
ERROR; 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. * empty constructor.
*/ */
@@ -158,12 +141,12 @@ public abstract class AbstractSimulatedDevice {
this.swversion = swversion; this.swversion = swversion;
} }
public ResponseStatus getResponseStatus() { public UpdateStatus getUpdateStatus() {
return responseStatus; return updateStatus;
} }
public void setResponseStatus(final ResponseStatus responseStatus) { public void setUpdateStatus(final UpdateStatus updateStatus) {
this.responseStatus = responseStatus; this.updateStatus = updateStatus;
} }
public Protocol getProtocol() { public Protocol getProtocol() {
@@ -177,4 +160,13 @@ public abstract class AbstractSimulatedDevice {
public void setNextPollCounterSec(final int nextPollDelayInSec) { public void setNextPollCounterSec(final int nextPollDelayInSec) {
this.nextPollCounterSec = nextPollDelayInSec; this.nextPollCounterSec = nextPollDelayInSec;
} }
public String getTargetSecurityToken() {
return targetSecurityToken;
}
public void setTargetSecurityToken(final String targetSecurityToken) {
this.targetSecurityToken = targetSecurityToken;
}
} }

View File

@@ -8,8 +8,6 @@
*/ */
package org.eclipse.hawkbit.simulator; package org.eclipse.hawkbit.simulator;
import java.util.concurrent.ScheduledExecutorService;
import org.eclipse.hawkbit.simulator.http.ControllerResource; import org.eclipse.hawkbit.simulator.http.ControllerResource;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -18,7 +16,7 @@ import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.PathNotFoundException; 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 { public class DDISimulatedDevice extends AbstractSimulatedDevice {
@@ -26,12 +24,12 @@ public class DDISimulatedDevice extends AbstractSimulatedDevice {
private static final Logger LOGGER = LoggerFactory.getLogger(DDISimulatedDevice.class); private static final Logger LOGGER = LoggerFactory.getLogger(DDISimulatedDevice.class);
private final int pollDelaySec; private final int pollDelaySec;
private final ScheduledExecutorService pollthreadpool;
private final ControllerResource controllerResource; private final ControllerResource controllerResource;
private final DeviceSimulatorUpdater deviceUpdater;
private volatile boolean removed; private volatile boolean removed;
private volatile Long currentActionId; private volatile Long currentActionId;
private final DeviceSimulatorUpdater deviceUpdater;
/** /**
* @param id * @param id
@@ -42,18 +40,14 @@ public class DDISimulatedDevice extends AbstractSimulatedDevice {
* the delay of the poll interval in sec * the delay of the poll interval in sec
* @param controllerResource * @param controllerResource
* the http controller resource * the http controller resource
* @param pollthreadpool
* the threadpool for polling endpoint
* @param deviceUpdater * @param deviceUpdater
* the service to update devices * the service to update devices
*/ */
public DDISimulatedDevice(final String id, final String tenant, final int pollDelaySec, public DDISimulatedDevice(final String id, final String tenant, final int pollDelaySec,
final ControllerResource controllerResource, final ScheduledExecutorService pollthreadpool, final ControllerResource controllerResource, final DeviceSimulatorUpdater deviceUpdater) {
final DeviceSimulatorUpdater deviceUpdater) {
super(id, tenant, Protocol.DDI_HTTP); super(id, tenant, Protocol.DDI_HTTP);
this.pollDelaySec = pollDelaySec; this.pollDelaySec = pollDelaySec;
this.controllerResource = controllerResource; this.controllerResource = controllerResource;
this.pollthreadpool = pollthreadpool;
this.deviceUpdater = deviceUpdater; this.deviceUpdater = deviceUpdater;
setNextPollCounterSec(pollDelaySec); setNextPollCounterSec(pollDelaySec);
} }
@@ -76,27 +70,12 @@ public class DDISimulatedDevice extends AbstractSimulatedDevice {
final String basePollJson = controllerResource.get(getTenant(), getId()); final String basePollJson = controllerResource.get(getTenant(), getId());
try { try {
final String href = JsonPath.parse(basePollJson).read("_links.deploymentBase.href"); 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) { if (currentActionId == null) {
final String deploymentJson = controllerResource.getDeployment(getTenant(), getId(), actionId); final String deploymentJson = controllerResource.getDeployment(getTenant(), getId(), actionId);
final String swVersion = JsonPath.parse(deploymentJson).read("deployment.chunks[0].version"); final String swVersion = JsonPath.parse(deploymentJson).read("deployment.chunks[0].version");
currentActionId = actionId; currentActionId = actionId;
deviceUpdater.startUpdate(getTenant(), getId(), actionId, swVersion, (device, actionId1) -> { startDdiUpdate(actionId, swVersion);
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;
});
} }
} catch (final PathNotFoundException e) { } catch (final PathNotFoundException e) {
// href might not be in the json response, so ignore // 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;
});
}
} }

View File

@@ -9,10 +9,7 @@
package org.eclipse.hawkbit.simulator; package org.eclipse.hawkbit.simulator;
/** /**
* An simulated device using the DMF API of the hawkbit update server. * A simulated device using the DMF API of the hawkBit update server.
*
* @author Michael Hirsch
*
*/ */
public class DMFSimulatedDevice extends AbstractSimulatedDevice { public class DMFSimulatedDevice extends AbstractSimulatedDevice {

View File

@@ -21,8 +21,6 @@ import com.vaadin.spring.annotation.EnableVaadin;
/** /**
* The main-method to start the Spring-Boot application. * The main-method to start the Spring-Boot application.
* *
*
*
*/ */
@SpringBootApplication @SpringBootApplication
@EnableVaadin @EnableVaadin
@@ -46,6 +44,8 @@ public class DeviceSimulator {
* @param args * @param args
* the args * the args
*/ */
// Exception squid:S2095 - Spring boot standard behavior
@SuppressWarnings({ "squid:S2095" })
public static void main(final String[] args) { public static void main(final String[] args) {
SpringApplication.run(DeviceSimulator.class, args); SpringApplication.run(DeviceSimulator.class, args);
} }

View File

@@ -8,27 +8,56 @@
*/ */
package org.eclipse.hawkbit.simulator; 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.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; 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.AbstractSimulatedDevice.Protocol;
import org.eclipse.hawkbit.simulator.UpdateStatus.ResponseStatus;
import org.eclipse.hawkbit.simulator.amqp.SpSenderService; import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
import org.eclipse.hawkbit.simulator.event.InitUpdate; import org.eclipse.hawkbit.simulator.event.InitUpdate;
import org.eclipse.hawkbit.simulator.event.ProgressUpdate; 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.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.google.common.eventbus.EventBus; 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 @Service
public class DeviceSimulatorUpdater { public class DeviceSimulatorUpdater {
private static final Logger LOGGER = LoggerFactory.getLogger(DeviceSimulatorUpdater.class);
private static final ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(4); private static final ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(4);
@@ -54,14 +83,18 @@ public class DeviceSimulatorUpdater {
* @param actionId * @param actionId
* the actionId from the hawkbit update server to start the * the actionId from the hawkbit update server to start the
* update. * update.
* @param swVersion * @param modules
* the software module version from the hawkbit update server * 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 * @param callback
* the callback which gets called when the simulated update * the callback which gets called when the simulated update
* process has been finished * process has been finished
*/ */
public void startUpdate(final String tenant, final String id, final long actionId, final String swVersion, 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); AbstractSimulatedDevice device = repository.get(tenant, id);
// plug and play - non existing device will be auto created // plug and play - non existing device will be auto created
@@ -70,14 +103,23 @@ public class DeviceSimulatorUpdater {
} }
device.setProgress(0.0); 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)); eventbus.post(new InitUpdate(device));
threadPool.schedule(new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus, callback), threadPool.schedule(
2_000, TimeUnit.MILLISECONDS); new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus, callback, modules), 2_000,
TimeUnit.MILLISECONDS);
} }
private static final class DeviceSimulatorUpdateThread implements Runnable { private static final class DeviceSimulatorUpdateThread implements Runnable {
private static final int MINIMUM_TOKENLENGTH_FOR_HINT = 6;
private static final Random rndSleep = new SecureRandom(); private static final Random rndSleep = new SecureRandom();
private final AbstractSimulatedDevice device; private final AbstractSimulatedDevice device;
@@ -85,38 +127,205 @@ public class DeviceSimulatorUpdater {
private final long actionId; private final long actionId;
private final EventBus eventbus; private final EventBus eventbus;
private final UpdaterCallback callback; private final UpdaterCallback callback;
private final List<SoftwareModule> modules;
private DeviceSimulatorUpdateThread(final AbstractSimulatedDevice device, final SpSenderService spSenderService, 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.device = device;
this.spSenderService = spSenderService; this.spSenderService = spSenderService;
this.actionId = actionId; this.actionId = actionId;
this.eventbus = eventbus; this.eventbus = eventbus;
this.callback = callback; this.callback = callback;
this.modules = modules;
} }
@Override @Override
public void run() { 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; final double newProgress = device.getProgress() + 0.2;
device.setProgress(newProgress); device.setProgress(newProgress);
if (newProgress < 1.0) { if (newProgress < 1.0) {
threadPool.schedule( threadPool.schedule(
new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus, callback), new DeviceSimulatorUpdateThread(device, spSenderService, actionId, eventbus, callback, modules),
rndSleep.nextInt(5_000), TimeUnit.MILLISECONDS); rndSleep.nextInt(5_000), TimeUnit.MILLISECONDS);
} else { } else {
callback.updateFinished(device, actionId); callback.updateFinished(device, actionId);
} }
eventbus.post(new ProgressUpdate(device)); 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 * Callback interface which is called when the simulated update process has
* been finished and the caller of starting the simulated update process can * been finished and the caller of starting the simulated update process can
* send the result to the hawkbit update server back. * send the result back to the hawkBit update server.
*
* @author Michael Hirsch
*
*/ */
@FunctionalInterface @FunctionalInterface
public interface UpdaterCallback { public interface UpdaterCallback {

View File

@@ -26,9 +26,6 @@ import com.google.common.eventbus.EventBus;
/** /**
* Poll time trigger which executes the {@link DDISimulatedDevice#poll()} every * Poll time trigger which executes the {@link DDISimulatedDevice#poll()} every
* second. * second.
*
* @author Michael Hirsch
*
*/ */
@Component @Component
public class NextPollTimeController { public class NextPollTimeController {
@@ -59,16 +56,15 @@ public class NextPollTimeController {
devices.forEach(device -> { devices.forEach(device -> {
int nextCounter = device.getNextPollCounterSec() - 1; int nextCounter = device.getNextPollCounterSec() - 1;
if (nextCounter < 0) { if (nextCounter < 0 && device instanceof DDISimulatedDevice) {
if (device instanceof DDISimulatedDevice) { try {
try { pollService.submit(() -> ((DDISimulatedDevice) device).poll());
pollService.submit(() -> ((DDISimulatedDevice) device).poll()); } catch (final IllegalStateException e) {
} catch (final IllegalStateException e) { LOGGER.trace("Device could not be polled", e);
LOGGER.trace("Device could not be polled", e);
}
nextCounter = ((DDISimulatedDevice) device).getPollDelaySec();
} }
nextCounter = ((DDISimulatedDevice) device).getPollDelaySec();
} }
device.setNextPollCounterSec(nextCounter); device.setNextPollCounterSec(nextCounter);
}); });
eventBus.post(new NextPollCounterUpdate(devices)); eventBus.post(new NextPollCounterUpdate(devices));

View File

@@ -9,8 +9,6 @@
package org.eclipse.hawkbit.simulator; package org.eclipse.hawkbit.simulator;
import java.net.URL; 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.AbstractSimulatedDevice.Protocol;
import org.eclipse.hawkbit.simulator.http.ControllerResource; import org.eclipse.hawkbit.simulator.http.ControllerResource;
@@ -24,15 +22,9 @@ import feign.Logger;
/** /**
* The simulated device factory to create either {@link DMFSimulatedDevice} or * The simulated device factory to create either {@link DMFSimulatedDevice} or
* {@link DDISimulatedDevice#}. * {@link DDISimulatedDevice#}.
*
* @author Michael Hirsch
*
*/ */
@Service @Service
public class SimulatedDeviceFactory { public class SimulatedDeviceFactory {
private static final ScheduledExecutorService pollThreadPool = Executors.newScheduledThreadPool(4);
@Autowired @Autowired
private DeviceSimulatorUpdater deviceUpdater; private DeviceSimulatorUpdater deviceUpdater;
@@ -47,7 +39,8 @@ public class SimulatedDeviceFactory {
* the protocol of the device * the protocol of the device
* @return the created simulated 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); return createSimulatedDevice(id, tenant, protocol, 30, null, null);
} }
@@ -80,7 +73,7 @@ public class SimulatedDeviceFactory {
final ControllerResource controllerResource = Feign.builder().logger(new Logger.ErrorLogger()) final ControllerResource controllerResource = Feign.builder().logger(new Logger.ErrorLogger())
.requestInterceptor(new GatewayTokenInterceptor(gatewayToken)).logLevel(Logger.Level.BASIC) .requestInterceptor(new GatewayTokenInterceptor(gatewayToken)).logLevel(Logger.Level.BASIC)
.target(ControllerResource.class, baseEndpoint.toString()); .target(ControllerResource.class, baseEndpoint.toString());
return new DDISimulatedDevice(id, tenant, pollDelaySec, controllerResource, pollThreadPool, deviceUpdater); return new DDISimulatedDevice(id, tenant, pollDelaySec, controllerResource, deviceUpdater);
default: default:
throw new IllegalArgumentException("Protocol " + protocol + " unknown"); throw new IllegalArgumentException("Protocol " + protocol + " unknown");
} }

View File

@@ -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;
}
}

View File

@@ -25,6 +25,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.Header; import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.google.common.collect.Lists;
/** /**
* Handle all incoming Messages from hawkBit update server. * Handle all incoming Messages from hawkBit update server.
* *
@@ -88,6 +90,8 @@ public class SpReceiverService extends ReceiverService {
if (eventHeader == null) { if (eventHeader == null) {
logAndThrowMessageError(message, "Event Topic is not set"); logAndThrowMessageError(message, "Event Topic is not set");
} }
// Exception squid:S2259 - Checked before
@SuppressWarnings({ "squid:S2259" })
final EventTopic eventTopic = EventTopic.valueOf(eventHeader.toString()); final EventTopic eventTopic = EventTopic.valueOf(eventHeader.toString());
switch (eventTopic) { switch (eventTopic) {
case DOWNLOAD_AND_INSTALL: case DOWNLOAD_AND_INSTALL:
@@ -109,7 +113,7 @@ public class SpReceiverService extends ReceiverService {
final Long actionId = convertMessage(message, Long.class); final Long actionId = convertMessage(message, Long.class);
final SimulatedUpdate update = new SimulatedUpdate(tenant, thingId, actionId); 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) { private void handleUpdateProcess(final Message message, final String thingId) {
@@ -120,19 +124,20 @@ public class SpReceiverService extends ReceiverService {
final DownloadAndUpdateRequest downloadAndUpdateRequest = convertMessage(message, final DownloadAndUpdateRequest downloadAndUpdateRequest = convertMessage(message,
DownloadAndUpdateRequest.class); DownloadAndUpdateRequest.class);
final Long actionId = downloadAndUpdateRequest.getActionId(); final Long actionId = downloadAndUpdateRequest.getActionId();
final String targetSecurityToken = downloadAndUpdateRequest.getTargetSecurityToken();
deviceUpdater.startUpdate(tenant, thingId, actionId, deviceUpdater.startUpdate(tenant, thingId, actionId, null, downloadAndUpdateRequest.getSoftwareModules(),
downloadAndUpdateRequest.getSoftwareModules().get(0).getModuleVersion(), (device, actionId1) -> { targetSecurityToken, (device, actionId1) -> {
switch (device.getResponseStatus()) { switch (device.getUpdateStatus().getResponseStatus()) {
case SUCCESSFUL: case SUCCESSFUL:
spSenderService.finishUpdateProcess( spSenderService.finishUpdateProcess(
new SimulatedUpdate(device.getTenant(), device.getId(), actionId1), new SimulatedUpdate(device.getTenant(), device.getId(), actionId1),
"Simulation complete!"); device.getUpdateStatus().getStatusMessages());
break; break;
case ERROR: case ERROR:
spSenderService.finishUpdateProcessWithError( spSenderService.finishUpdateProcessWithError(
new SimulatedUpdate(device.getTenant(), device.getId(), actionId1), new SimulatedUpdate(device.getTenant(), device.getId(), actionId1),
"Simulation complete with error!"); device.getUpdateStatus().getStatusMessages());
break; break;
default: default:
break; break;

View File

@@ -8,6 +8,7 @@
*/ */
package org.eclipse.hawkbit.simulator.amqp; package org.eclipse.hawkbit.simulator.amqp;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings; 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.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; 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 @Service
public class SpSenderService extends SenderService { public class SpSenderService extends SenderService {
@@ -59,8 +56,9 @@ public class SpSenderService extends SenderService {
* @param description * @param description
* a description according the update process * a description according the update process
*/ */
public void finishUpdateProcess(final SimulatedUpdate update, final String description) { public void finishUpdateProcess(final SimulatedUpdate update, final List<String> updateResultMessages) {
final Message updateResultMessage = createUpdateResultMessage(update, ActionStatus.FINISHED, description); final Message updateResultMessage = createUpdateResultMessage(update, ActionStatus.FINISHED,
updateResultMessages);
sendMessage(spExchange, updateResultMessage); sendMessage(spExchange, updateResultMessage);
} }
@@ -72,9 +70,9 @@ public class SpSenderService extends SenderService {
* @param messageDescription * @param messageDescription
* a description according the update process * a description according the update process
*/ */
public void finishUpdateProcessWithError(final SimulatedUpdate update, final String messageDescription) { public void finishUpdateProcessWithError(final SimulatedUpdate update, final List<String> updateResultMessages) {
sendErrorgMessage(update, messageDescription); sendErrorgMessage(update, updateResultMessages);
LOGGER.debug("Update process finished with error \"{}\" reported by thing {}", messageDescription, LOGGER.debug("Update process finished with error \"{}\" reported by thing {}", updateResultMessages,
update.getThingId()); update.getThingId());
} }
@@ -88,8 +86,8 @@ public class SpSenderService extends SenderService {
* @param actionId * @param actionId
* the ID of the action for the error message * the ID of the action for the error message
*/ */
public void sendErrorMessage(final String tenant, final String messageDescription, final Long actionId) { public void sendErrorMessage(final String tenant, final List<String> updateResultMessages, final Long actionId) {
final Message message = createActionStatusMessage(tenant, ActionStatus.ERROR, messageDescription, actionId); final Message message = createActionStatusMessage(tenant, ActionStatus.ERROR, updateResultMessages, actionId);
sendMessage(spExchange, message); sendMessage(spExchange, message);
} }
@@ -101,8 +99,8 @@ public class SpSenderService extends SenderService {
* @param warningMessage * @param warningMessage
* a warning description * a warning description
*/ */
public void sendWarningMessage(final SimulatedUpdate update, final String warningMessage) { public void sendWarningMessage(final SimulatedUpdate update, final List<String> updateResultMessages) {
final Message message = createActionStatusMessage(update, warningMessage, ActionStatus.WARNING); final Message message = createActionStatusMessage(update, updateResultMessages, ActionStatus.WARNING);
sendMessage(spExchange, message); sendMessage(spExchange, message);
} }
@@ -119,8 +117,8 @@ public class SpSenderService extends SenderService {
* the cached value * the cached value
*/ */
public void sendActionStatusMessage(final String tenant, final ActionStatus actionStatus, public void sendActionStatusMessage(final String tenant, final ActionStatus actionStatus,
final String actionMessage, final Long actionId) { final List<String> updateResultMessages, final Long actionId) {
final Message message = createActionStatusMessage(tenant, actionStatus, actionMessage, actionId); final Message message = createActionStatusMessage(tenant, actionStatus, updateResultMessages, actionId);
sendMessage(message); sendMessage(message);
} }
@@ -162,11 +160,11 @@ public class SpSenderService extends SenderService {
* *
* @param context * @param context
* the current context * the current context
* @param messageDescription * @param updateResultMessages
* a description according the update process * a list of descriptions according the update process
*/ */
private void sendErrorgMessage(final SimulatedUpdate update, final String messageDescription) { private void sendErrorgMessage(final SimulatedUpdate update, final List<String> updateResultMessages) {
final Message message = createActionStatusMessage(update, messageDescription, ActionStatus.ERROR); final Message message = createActionStatusMessage(update, updateResultMessages, ActionStatus.ERROR);
sendMessage(spExchange, message); sendMessage(spExchange, message);
} }
@@ -183,7 +181,7 @@ public class SpSenderService extends SenderService {
* the cacheValue value * the cacheValue value
*/ */
private Message createActionStatusMessage(final String tenant, final ActionStatus actionStatus, 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 MessageProperties messageProperties = new MessageProperties();
final Map<String, Object> headers = messageProperties.getHeaders(); final Map<String, Object> headers = messageProperties.getHeaders();
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(); final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus();
@@ -192,15 +190,14 @@ public class SpSenderService extends SenderService {
headers.put(MessageHeaderKey.TENANT, tenant); headers.put(MessageHeaderKey.TENANT, tenant);
headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name()); headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON); headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON);
if (!StringUtils.isEmpty(actionMessage)) { actionUpdateStatus.getMessage().addAll(updateResultMessages);
actionUpdateStatus.getMessage().add(actionMessage);
}
actionUpdateStatus.setActionId(actionId); actionUpdateStatus.setActionId(actionId);
return convertMessage(actionUpdateStatus, messageProperties); return convertMessage(actionUpdateStatus, messageProperties);
} }
private Message createUpdateResultMessage(final SimulatedUpdate cacheValue, final ActionStatus actionStatus, private Message createUpdateResultMessage(final SimulatedUpdate cacheValue, final ActionStatus actionStatus,
final String updateResultMessage) { final List<String> updateResultMessages) {
final MessageProperties messageProperties = new MessageProperties(); final MessageProperties messageProperties = new MessageProperties();
final Map<String, Object> headers = messageProperties.getHeaders(); final Map<String, Object> headers = messageProperties.getHeaders();
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(); final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus();
@@ -209,14 +206,14 @@ public class SpSenderService extends SenderService {
headers.put(MessageHeaderKey.TENANT, cacheValue.getTenant()); headers.put(MessageHeaderKey.TENANT, cacheValue.getTenant());
headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name()); headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON); headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON);
actionUpdateStatus.getMessage().add(updateResultMessage); actionUpdateStatus.getMessage().addAll(updateResultMessages);
actionUpdateStatus.setActionId(cacheValue.getActionId()); actionUpdateStatus.setActionId(cacheValue.getActionId());
return convertMessage(actionUpdateStatus, messageProperties); 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) { final ActionStatus status) {
return createActionStatusMessage(update.getTenant(), status, messageDescription, update.getActionId()); return createActionStatusMessage(update.getTenant(), status, updateResultMessages, update.getActionId());
} }
} }

View File

@@ -13,8 +13,6 @@ import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
/** /**
* Event definition object which is published if the simulated device updated * Event definition object which is published if the simulated device updated
* its update progress. * its update progress.
*
* @author Michael Hirsch
* *
*/ */
public class ProgressUpdate { public class ProgressUpdate {

View File

@@ -8,21 +8,19 @@
*/ */
package org.eclipse.hawkbit.simulator.ui; package org.eclipse.hawkbit.simulator.ui;
import java.net.URL;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice; import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol; 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.AbstractSimulatedDevice.Status;
import org.eclipse.hawkbit.simulator.DeviceSimulatorRepository; import org.eclipse.hawkbit.simulator.DeviceSimulatorRepository;
import org.eclipse.hawkbit.simulator.SimulatedDeviceFactory; 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.amqp.SpSenderService;
import org.eclipse.hawkbit.simulator.event.InitUpdate; import org.eclipse.hawkbit.simulator.event.InitUpdate;
import org.eclipse.hawkbit.simulator.event.NextPollCounterUpdate; import org.eclipse.hawkbit.simulator.event.NextPollCounterUpdate;
import org.eclipse.hawkbit.simulator.event.ProgressUpdate; import org.eclipse.hawkbit.simulator.event.ProgressUpdate;
import org.eclipse.hawkbit.simulator.ui.GenerateDialog.GenerateDialogCallback;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import com.google.common.collect.Lists; 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 * 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 * current simulated devices in a grid with their current status and update
* progress. * progress.
*
* @author Michael Hirsch
* *
*/ */
@SpringView(name = "") @SpringView(name = "")
public class SimulatorView extends VerticalLayout implements View { 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; private static final long serialVersionUID = 1L;
@Autowired @Autowired
@@ -79,6 +91,7 @@ public class SimulatorView extends VerticalLayout implements View {
private BeanContainer<String, AbstractSimulatedDevice> beanContainer; private BeanContainer<String, AbstractSimulatedDevice> beanContainer;
@SuppressWarnings("unchecked")
@Override @Override
public void enter(final ViewChangeEvent event) { public void enter(final ViewChangeEvent event) {
eventbus.register(this); eventbus.register(this);
@@ -91,7 +104,7 @@ public class SimulatorView extends VerticalLayout implements View {
createToolbar(); createToolbar();
beanContainer = new BeanContainer<>(AbstractSimulatedDevice.class); beanContainer = new BeanContainer<>(AbstractSimulatedDevice.class);
beanContainer.setBeanIdProperty("id"); beanContainer.setBeanIdProperty(ID_COL);
grid.setSizeFull(); grid.setSizeFull();
grid.setCellStyleGenerator(new CellStyleGenerator() { grid.setCellStyleGenerator(new CellStyleGenerator() {
@@ -100,28 +113,28 @@ public class SimulatorView extends VerticalLayout implements View {
@Override @Override
public String getStyle(final CellReference cellReference) { 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.setSelectionMode(SelectionMode.NONE);
grid.setContainerDataSource(beanContainer); grid.setContainerDataSource(beanContainer);
grid.appendHeaderRow().getCell("responseStatus").setComponent(responseComboBox); grid.appendHeaderRow().getCell(RESPONSE_STATUS_COL).setComponent(responseComboBox);
grid.setColumnOrder("id", "status", "swversion", "progress", "tenant", "protocol", "responseStatus", grid.setColumnOrder(ID_COL, STATUS_COL, SWVERSION_COL, PROGRESS_COL, TENANT_COL, PROTOCOL_COL,
"nextPollCounterSec"); RESPONSE_STATUS_COL, NEXT_POLL_COUNTER_SEC_COL);
// header widths // header widths
grid.getColumn("status").setMaximumWidth(80); grid.getColumn(STATUS_COL).setMaximumWidth(80);
grid.getColumn("protocol").setMaximumWidth(180); grid.getColumn(PROTOCOL_COL).setMaximumWidth(180);
grid.getColumn("responseStatus").setMaximumWidth(240); grid.getColumn(RESPONSE_STATUS_COL).setMaximumWidth(240);
grid.getColumn("nextPollCounterSec").setMaximumWidth(210); grid.getColumn(NEXT_POLL_COUNTER_SEC_COL).setMaximumWidth(210);
grid.getColumn("nextPollCounterSec").setHeaderCaption("Next Poll in (sec)"); grid.getColumn(NEXT_POLL_COUNTER_SEC_COL).setHeaderCaption("Next Poll in (sec)");
grid.getColumn("swversion").setHeaderCaption("SW Version"); grid.getColumn(SWVERSION_COL).setHeaderCaption("SW Version");
grid.getColumn("responseStatus").setHeaderCaption("Response Update Status"); grid.getColumn(RESPONSE_STATUS_COL).setHeaderCaption("Response Update Status");
grid.getColumn("progress").setRenderer(new ProgressBarRenderer()); grid.getColumn(PROGRESS_COL).setRenderer(new ProgressBarRenderer());
grid.getColumn("protocol").setConverter(createProtocolConverter()); grid.getColumn(PROTOCOL_COL).setConverter(createProtocolConverter());
grid.getColumn("status").setRenderer(new HtmlRenderer(), createStatusConverter()); grid.getColumn(STATUS_COL).setRenderer(new HtmlRenderer(), createStatusConverter());
grid.removeColumn("tenant"); grid.removeColumn(TENANT_COL);
// grid combobox // grid combobox
responseComboBox.setItemIcon(ResponseStatus.SUCCESSFUL, FontAwesome.CHECK_CIRCLE); responseComboBox.setItemIcon(ResponseStatus.SUCCESSFUL, FontAwesome.CHECK_CIRCLE);
@@ -129,8 +142,8 @@ public class SimulatorView extends VerticalLayout implements View {
responseComboBox.setNullSelectionAllowed(false); responseComboBox.setNullSelectionAllowed(false);
responseComboBox.setValue(ResponseStatus.SUCCESSFUL); responseComboBox.setValue(ResponseStatus.SUCCESSFUL);
responseComboBox.addValueChangeListener(valueChangeEvent -> { responseComboBox.addValueChangeListener(valueChangeEvent -> {
beanContainer.getItemIds().forEach(itemId -> beanContainer.getItem(itemId).getItemProperty("responseStatus") beanContainer.getItemIds().forEach(itemId -> beanContainer.getItem(itemId)
.setValue(valueChangeEvent.getProperty().getValue())); .getItemProperty(RESPONSE_STATUS_COL).setValue(valueChangeEvent.getProperty().getValue()));
}); });
// add all components // add all components
@@ -141,7 +154,7 @@ public class SimulatorView extends VerticalLayout implements View {
setExpandRatio(grid, 1.0F); setExpandRatio(grid, 1.0F);
// load beans // load beans
repository.getAll().forEach(device -> beanContainer.addBean(device)); repository.getAll().forEach(beanContainer::addBean);
} }
@Override @Override
@@ -150,21 +163,16 @@ public class SimulatorView extends VerticalLayout implements View {
eventbus.unregister(this); eventbus.unregister(this);
} }
@SuppressWarnings("unchecked")
@Subscribe @Subscribe
public void pollCounterUpdate(final NextPollCounterUpdate update) { public void pollCounterUpdate(final NextPollCounterUpdate update) {
final List<AbstractSimulatedDevice> devices = update.getDevices(); final List<AbstractSimulatedDevice> devices = update.getDevices();
this.getUI().access(new Runnable() { this.getUI().access(() -> devices.forEach(device -> {
final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId());
@Override if (item != null) {
public void run() { item.getItemProperty(NEXT_POLL_COUNTER_SEC_COL).setValue(device.getNextPollCounterSec());
devices.forEach(device -> {
final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId());
if (item != null) {
item.getItemProperty("nextPollCounterSec").setValue(device.getNextPollCounterSec());
}
});
} }
}); }));
} }
/** /**
@@ -173,21 +181,19 @@ public class SimulatorView extends VerticalLayout implements View {
* @param update * @param update
* the update event posted on the event bus * the update event posted on the event bus
*/ */
@SuppressWarnings("unchecked")
@Subscribe @Subscribe
public void initUpdate(final InitUpdate update) { public void initUpdate(final InitUpdate update) {
final AbstractSimulatedDevice device = update.getDevice(); final AbstractSimulatedDevice device = update.getDevice();
this.getUI().access(new Runnable() { this.getUI().access(() -> {
final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId());
@Override if (item == null) {
public void run() { return;
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());
}
} }
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 * @param update
* the update event posted on the event bus * the update event posted on the event bus
*/ */
@SuppressWarnings("unchecked")
@Subscribe @Subscribe
public void progessUpdate(final ProgressUpdate update) { public void progessUpdate(final ProgressUpdate update) {
final AbstractSimulatedDevice device = update.getDevice(); final AbstractSimulatedDevice device = update.getDevice();
this.getUI().access(new Runnable() { this.getUI().access(() -> {
@Override final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId());
public void run() { if (item != null) {
final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId()); item.getItemProperty(PROGRESS_COL).setValue(device.getProgress());
if (item != null) { setStatusColumn(device, item);
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);
}
}
} }
}); });
} }
@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() { private void createToolbar() {
final Button createDevicesButton = new Button("generate..."); final Button createDevicesButton = new Button("generate...");
createDevicesButton.setIcon(FontAwesome.GEARS); createDevicesButton.setIcon(FontAwesome.GEARS);
@@ -246,18 +254,15 @@ public class SimulatorView extends VerticalLayout implements View {
} }
private void openGenerateDialog() { private void openGenerateDialog() {
UI.getCurrent().addWindow(new GenerateDialog(new GenerateDialogCallback() { UI.getCurrent().addWindow(
@Override new GenerateDialog((namePrefix, tenant, amount, pollDelay, basePollUrl, gatewayToken, protocol) -> {
public void okButton(final String namePrefix, final String tenant, final int amount, final int pollDelay, for (int index = 0; index < amount; index++) {
final URL basePollUrl, final String gatewayToken, final Protocol protocol) { final String deviceId = namePrefix + index;
for (int index = 0; index < amount; index++) { beanContainer.addBean(repository.add(deviceFactory.createSimulatedDevice(deviceId,
final String deviceId = namePrefix + index; tenant.toLowerCase(), protocol, pollDelay, basePollUrl, gatewayToken)));
beanContainer.addBean(repository.add(deviceFactory.createSimulatedDevice(deviceId, spSenderService.createOrUpdateThing(tenant, deviceId);
tenant.toLowerCase(), protocol, pollDelay, basePollUrl, gatewayToken))); }
spSenderService.createOrUpdateThing(tenant, deviceId); }));
}
}
}));
} }
private Converter<String, Protocol> createProtocolConverter() { private Converter<String, Protocol> createProtocolConverter() {

View File

@@ -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. 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 the example application in our hawkBit sandbox on Bluemix
- try out Management UI https://hawkbit.eu-gb.mybluemix.net/UI - 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) - 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 - try out DDI API https://hawkbit.eu-gb.mybluemix.net/DEFAULT/controller/v1/MYTESTDEVICE (authentication disabled)
## On your own workstation ## On your own workstation
### Run ### Run
``` ```
java -jar examples/hawkbit-example-app/target/hawkbit-example-app-*-SNAPSHOT.jar 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: Or:
``` ```
run org eclipse.hawkbit.app.Start run org eclipse.hawkbit.app.Start
``` ```

View File

@@ -20,11 +20,11 @@ import com.vaadin.spring.annotation.SpringUI;
* login path. The easiest way to get an hawkBit login UI running is to extend * login path. The easiest way to get an hawkBit login UI running is to extend
* the {@link HawkbitLoginUI} and to annotated it with {@link SpringUI} as in * the {@link HawkbitLoginUI} and to annotated it with {@link SpringUI} as in
* this example to the defined {@link HawkbitTheme#LOGIN_UI_PATH}. * this example to the defined {@link HawkbitTheme#LOGIN_UI_PATH}.
*
*
*
*/ */
@SpringUI(path = HawkbitTheme.LOGIN_UI_PATH) @SpringUI(path = HawkbitTheme.LOGIN_UI_PATH)
// Exception squid:MaximumInheritanceDepth - Most of the inheritance comes from
// Vaadin.
@SuppressWarnings({ "squid:MaximumInheritanceDepth" })
public class MyLoginUI extends HawkbitLoginUI { public class MyLoginUI extends HawkbitLoginUI {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@@ -29,6 +29,9 @@ import com.vaadin.spring.annotation.SpringUI;
*/ */
@SpringUI @SpringUI
@Push(value = PushMode.AUTOMATIC, transport = Transport.WEBSOCKET) @Push(value = PushMode.AUTOMATIC, transport = Transport.WEBSOCKET)
// Exception squid:MaximumInheritanceDepth - Most of the inheritance comes from
// Vaadin.
@SuppressWarnings({ "squid:MaximumInheritanceDepth" })
public class MyUI extends HawkbitUI { public class MyUI extends HawkbitUI {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@@ -26,6 +26,8 @@ import org.springframework.context.annotation.Import;
@EnableHawkbitManagedSecurityConfiguration @EnableHawkbitManagedSecurityConfiguration
@EnableRestResources @EnableRestResources
@EnableDirectDeviceApi @EnableDirectDeviceApi
// Exception squid:S1118 - Spring boot standard behavior
@SuppressWarnings({ "squid:S1118" })
public class Start { public class Start {
/** /**
@@ -34,6 +36,8 @@ public class Start {
* @param args * @param args
* the VM arguments. * the VM arguments.
*/ */
// Exception squid:S2095 - Spring boot standard behavior
@SuppressWarnings({ "squid:S2095" })
public static void main(final String[] args) { public static void main(final String[] args) {
SpringApplication.run(Start.class, args); SpringApplication.run(Start.class, args);
} }

View File

@@ -8,3 +8,9 @@
# #
vaadin.servlet.productionMode=true 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

View File

@@ -0,0 +1,32 @@
#
# 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
#
# This profile adds basic configurations for a MySQL DB usage.
# Keep in mind that you need the MariaDB driver in your classpath on compile.
# see https://github.com/eclipse/hawkbit/wiki/Run-hawkBit
spring.jpa.database=MYSQL
spring.datasource.url=jdbc:mysql://localhost:3306/hawkbit
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driverClassName=org.mariadb.jdbc.Driver
spring.datasource.max-active=100
spring.datasource.max-idle=10
spring.datasource.min-idle=10
spring.datasource.initial-size=10
spring.datasource.validation-query=select 1 from dual
spring.datasource.validation-interval=30000
spring.datasource.test-on-borrow=true
spring.datasource.test-on-return=false
spring.datasource.test-while-idle=true
spring.datasource.time-between-eviction-runs-millis=30000
spring.datasource.min-evictable-idle-time-millis=60000
spring.datasource.max-wait=10000
spring.datasource.jmx-enabled=true

View File

@@ -7,15 +7,22 @@
# http://www.eclipse.org/legal/epl-v10.html # http://www.eclipse.org/legal/epl-v10.html
# #
# DDI authentication configuration
hawkbit.server.ddi.security.authentication.anonymous.enabled=true hawkbit.server.ddi.security.authentication.anonymous.enabled=true
hawkbit.server.ddi.security.authentication.targettoken.enabled=false hawkbit.server.ddi.security.authentication.targettoken.enabled=true
hawkbit.server.ddi.security.authentication.gatewaytoken.enabled=false 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 vaadin.servlet.productionMode=false
## Configuration for RabbitMQ integration ## Configuration for DMF/RabbitMQ integration
spring.profiles.active=amqp
spring.rabbitmq.username=guest spring.rabbitmq.username=guest
spring.rabbitmq.password=guest spring.rabbitmq.password=guest
spring.rabbitmq.virtualHost=/ spring.rabbitmq.virtualHost=/

View File

@@ -18,7 +18,7 @@
</parent> </parent>
<packaging>jar</packaging> <packaging>jar</packaging>
<artifactId>hawkbit-mgmt-api-client</artifactId> <artifactId>hawkbit-mgmt-api-client</artifactId>
<name>hawkBit Management API example client</name> <name>hawkBit :: Management API example client</name>
<build> <build>
<plugins> <plugins>

View File

@@ -18,8 +18,9 @@ import com.google.common.collect.Lists;
/** /**
* Builder pattern for building {@link DistributionSetRequestBodyPost}. * Builder pattern for building {@link DistributionSetRequestBodyPost}.
*/ */
// Exception squid:S1701 - builder pattern
@SuppressWarnings({ "squid:S1701" })
public class DistributionSetBuilder { public class DistributionSetBuilder {
private String name; private String name;
private String version; private String version;
private String type; private String type;

View File

@@ -21,8 +21,9 @@ import com.google.common.collect.Lists;
* Builder pattern for building {@link DistributionSetTypeRequestBodyPost}. * Builder pattern for building {@link DistributionSetTypeRequestBodyPost}.
* *
*/ */
// Exception squid:S1701 - builder pattern
@SuppressWarnings({ "squid:S1701" })
public class DistributionSetTypeBuilder { public class DistributionSetTypeBuilder {
private String key; private String key;
private String name; private String name;
private final List<SoftwareModuleTypeAssigmentRest> mandatorymodules = Lists.newArrayList(); private final List<SoftwareModuleTypeAssigmentRest> mandatorymodules = Lists.newArrayList();

View File

@@ -17,6 +17,8 @@ import org.eclipse.hawkbit.rest.resource.model.rollout.RolloutRestRequestBody;
* Builder pattern for building {@link RolloutRestRequestBody}. * Builder pattern for building {@link RolloutRestRequestBody}.
* *
*/ */
// Exception squid:S1701 - builder pattern
@SuppressWarnings({ "squid:S1701" })
public class RolloutBuilder { public class RolloutBuilder {
private String name; private String name;

View File

@@ -21,6 +21,8 @@ import com.google.common.collect.Lists;
* Builder pattern for building {@link SoftwareModuleRequestBodyPost}. * Builder pattern for building {@link SoftwareModuleRequestBodyPost}.
* *
*/ */
// Exception squid:S1701 - builder pattern
@SuppressWarnings({ "squid:S1701" })
public class SoftwareModuleBuilder { public class SoftwareModuleBuilder {
private String name; private String name;

View File

@@ -21,6 +21,8 @@ import com.google.common.collect.Lists;
* Builder pattern for building {@link SoftwareModuleRequestBodyPost}. * Builder pattern for building {@link SoftwareModuleRequestBodyPost}.
* *
*/ */
// Exception squid:S1701 - builder pattern
@SuppressWarnings({ "squid:S1701" })
public class SoftwareModuleTypeBuilder { public class SoftwareModuleTypeBuilder {
private String key; private String key;

View File

@@ -19,6 +19,8 @@ import com.google.common.collect.Lists;
* Builder pattern for building {@link TagRequestBodyPut}. * Builder pattern for building {@link TagRequestBodyPut}.
* *
*/ */
// Exception squid:S1701 - builder pattern
@SuppressWarnings({ "squid:S1701" })
public class TagBuilder { public class TagBuilder {
private String name; private String name;

View File

@@ -21,6 +21,8 @@ import com.google.common.collect.Lists;
* Builder pattern for building {@link TargetRequestBody}. * Builder pattern for building {@link TargetRequestBody}.
* *
*/ */
// Exception squid:S1701 - builder pattern
@SuppressWarnings({ "squid:S1701" })
public class TargetBuilder { public class TargetBuilder {
private String controllerId; private String controllerId;

View File

@@ -8,11 +8,14 @@
*/ */
package org.eclipse.hawkbit.artifact.repository; package org.eclipse.hawkbit.artifact.repository;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.security.DigestOutputStream; import java.security.DigestOutputStream;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
@@ -122,7 +125,11 @@ public class ArtifactStore implements ArtifactRepository {
LOGGER.debug("storing file {} of content {}", filename, contentType); LOGGER.debug("storing file {} of content {}", filename, contentType);
tempFile = File.createTempFile("uploadFile", null); tempFile = File.createTempFile("uploadFile", null);
try (final FileOutputStream os = new FileOutputStream(tempFile)) { 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) { } catch (final IOException | MongoException e1) {
throw new ArtifactStoreException(e1.getMessage(), 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 File tempFile, final DbArtifactHash hash) {
final GridFsArtifact storedArtifact; final GridFsArtifact storedArtifact;
try { try {
@@ -185,7 +192,8 @@ public class ArtifactStore implements ArtifactRepository {
throw new ArtifactStoreException(e.getMessage(), e); 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() throw new HashNotMatchException("The given md5 hash " + hash.getMd5()
+ " not matching the calculated md5 hash " + storedArtifact.getHashes().getMd5(), + " not matching the calculated md5 hash " + storedArtifact.getHashes().getMd5(),
HashNotMatchException.MD5); HashNotMatchException.MD5);
@@ -195,14 +203,14 @@ public class ArtifactStore implements ArtifactRepository {
} }
private static String computeSHA1Hash(final InputStream stream, final FileOutputStream os, private static String computeSHA1Hash(final InputStream stream, final OutputStream os, final String providedSHA1Sum)
final String providedSHA1Sum) throws NoSuchAlgorithmException, IOException { throws NoSuchAlgorithmException, IOException {
String sha1Hash; String sha1Hash;
// compute digest // compute digest
final MessageDigest md = MessageDigest.getInstance("SHA-1"); final MessageDigest md = MessageDigest.getInstance("SHA-1");
final DigestOutputStream dos = new DigestOutputStream(os, md); try (final DigestOutputStream dos = new DigestOutputStream(os, md)) {
ByteStreams.copy(stream, dos); ByteStreams.copy(stream, dos);
dos.close(); }
sha1Hash = BaseEncoding.base16().lowerCase().encode(md.digest()); sha1Hash = BaseEncoding.base16().lowerCase().encode(md.digest());
if (providedSHA1Sum != null && !providedSHA1Sum.equalsIgnoreCase(sha1Hash)) { if (providedSHA1Sum != null && !providedSHA1Sum.equalsIgnoreCase(sha1Hash)) {
throw new HashNotMatchException( throw new HashNotMatchException(

View File

@@ -71,6 +71,8 @@ public class MongoConfiguration extends AbstractMongoConfiguration {
@Override @Override
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
// Closed by pre-destroy
@SuppressWarnings({ "squid:S2095" })
public Mongo mongo() throws UnknownHostException { public Mongo mongo() throws UnknownHostException {
final MongoClientURI uri = new MongoClientURI(properties.getUri(), createBuilderOutOfOptions(options)); final MongoClientURI uri = new MongoClientURI(properties.getUri(), createBuilderOutOfOptions(options));

3
hawkbit-core/README.md Normal file
View File

@@ -0,0 +1,3 @@
# hawkBit Core
Various internal interfaces and utility classes.

View File

@@ -22,11 +22,10 @@ import org.springframework.core.io.support.ResourcePatternResolver;
* This resource bundles using specified basenames, to resource loading. This * This resource bundles using specified basenames, to resource loading. This
* MessageSource implementation supports more than 1 properties file with the * MessageSource implementation supports more than 1 properties file with the
* same name. All properties files will be merged. * same name. All properties files will be merged.
*
*
*
*/ */
public class DistributedResourceBundleMessageSource extends ReloadableResourceBundleMessageSource { 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 Logger LOGGER = LoggerFactory.getLogger(DistributedResourceBundleMessageSource.class);
private static final String PROPERTIES_SUFFIX = ".properties"; private static final String PROPERTIES_SUFFIX = ".properties";
private ResourceLoader resourceLoader; private ResourceLoader resourceLoader;

View File

@@ -13,7 +13,6 @@ package org.eclipse.hawkbit.api;
* URLs to specific artifacts. * URLs to specific artifacts.
* *
*/ */
@FunctionalInterface
public interface ArtifactUrlHandler { public interface ArtifactUrlHandler {
/** /**
@@ -34,4 +33,11 @@ public interface ArtifactUrlHandler {
*/ */
String getUrl(String controllerId, final Long softwareModuleId, final String filename, final String sha1Hash, String getUrl(String controllerId, final Long softwareModuleId, final String filename, final String sha1Hash,
final UrlProtocol protocol); final UrlProtocol protocol);
/**
* @param protocol
* to check support for
* @return <code>true</code> of the handler supports given protocol.
*/
boolean protocolSupported(UrlProtocol protocol);
} }

View File

@@ -78,6 +78,12 @@ public class ArtifactUrlHandlerProperties {
* @return the pattern to build the URL. * @return the pattern to build the URL.
*/ */
String getPattern(); 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}"; 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 @Override
public String getHostname() { public String getHostname() {
return hostname; return hostname;
@@ -143,6 +163,20 @@ public class ArtifactUrlHandlerProperties {
*/ */
private String pattern = "{protocol}://{hostname}:{port}/{tenant}/controller/v1/{targetId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}"; 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 @Override
public String getHostname() { public String getHostname() {
return hostname; return hostname;
@@ -193,6 +227,20 @@ public class ArtifactUrlHandlerProperties {
*/ */
private String pattern = "{protocol}://{ip}:{port}/fw/{tenant}/{targetId}/sha1/{artifactSHA1}"; 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 @Override
public String getHostname() { public String getHostname() {
return hostname; return hostname;

View File

@@ -84,4 +84,15 @@ public class PropertyBasedArtifactUrlHandler implements ArtifactUrlHandler {
return replaceMap; 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();
}
} }

View File

@@ -39,6 +39,8 @@ public class TenantConfigurationPollingDurationValidator implements TenantConfig
} }
@Override @Override
// Exception squid:S1166 - Hide origin exception
@SuppressWarnings({ "squid:S1166" })
public void validate(final Object tenantConfigurationObject) { public void validate(final Object tenantConfigurationObject) {
TenantConfigurationValidator.super.validate(tenantConfigurationObject); TenantConfigurationValidator.super.validate(tenantConfigurationObject);
final String tenantConfigurationString = (String) tenantConfigurationObject; final String tenantConfigurationString = (String) tenantConfigurationObject;

View File

@@ -17,7 +17,6 @@ import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails; import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.repository.ControllerManagement; import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.security.CoapAnonymousPreAuthenticatedFilter;
import org.eclipse.hawkbit.security.ControllerPreAuthenticateSecurityTokenFilter; import org.eclipse.hawkbit.security.ControllerPreAuthenticateSecurityTokenFilter;
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedAnonymousDownload; import org.eclipse.hawkbit.security.ControllerPreAuthenticatedAnonymousDownload;
import org.eclipse.hawkbit.security.ControllerPreAuthenticatedAnonymousFilter; import org.eclipse.hawkbit.security.ControllerPreAuthenticatedAnonymousFilter;
@@ -97,7 +96,6 @@ public class AmqpControllerAuthentfication {
filterChain.add(anonymousDownloadFilter); filterChain.add(anonymousDownloadFilter);
filterChain.add(new ControllerPreAuthenticatedAnonymousFilter(ddiSecruityProperties)); filterChain.add(new ControllerPreAuthenticatedAnonymousFilter(ddiSecruityProperties));
filterChain.add(new CoapAnonymousPreAuthenticatedFilter());
} }
/** /**

View File

@@ -82,6 +82,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
.getSoftwareModules(); .getSoftwareModules();
final DownloadAndUpdateRequest downloadAndUpdateRequest = new DownloadAndUpdateRequest(); final DownloadAndUpdateRequest downloadAndUpdateRequest = new DownloadAndUpdateRequest();
downloadAndUpdateRequest.setActionId(targetAssignDistributionSetEvent.getActionId()); downloadAndUpdateRequest.setActionId(targetAssignDistributionSetEvent.getActionId());
downloadAndUpdateRequest.setTargetSecurityToken(targetAssignDistributionSetEvent.getTargetToken());
for (final org.eclipse.hawkbit.repository.model.SoftwareModule softwareModule : modules) { for (final org.eclipse.hawkbit.repository.model.SoftwareModule softwareModule : modules) {
final SoftwareModule amqpSoftwareModule = convertToAmqpSoftwareModule(controllerId, softwareModule); final SoftwareModule amqpSoftwareModule = convertToAmqpSoftwareModule(controllerId, softwareModule);
@@ -154,18 +155,26 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
private Artifact convertArtifact(final String targetId, final LocalArtifact localArtifact) { private Artifact convertArtifact(final String targetId, final LocalArtifact localArtifact) {
final Artifact artifact = new Artifact(); final Artifact artifact = new Artifact();
artifact.getUrls().put(Artifact.UrlProtocol.COAP, if (artifactUrlHandler.protocolSupported(UrlProtocol.COAP)) {
artifactUrlHandler.getUrl(targetId, localArtifact.getSoftwareModule().getId(), artifact.getUrls().put(Artifact.UrlProtocol.COAP,
localArtifact.getFilename(), localArtifact.getSha1Hash(), UrlProtocol.COAP)); artifactUrlHandler.getUrl(targetId, localArtifact.getSoftwareModule().getId(),
artifact.getUrls().put(Artifact.UrlProtocol.HTTP, localArtifact.getFilename(), localArtifact.getSha1Hash(), UrlProtocol.COAP));
artifactUrlHandler.getUrl(targetId, localArtifact.getSoftwareModule().getId(), }
localArtifact.getFilename(), localArtifact.getSha1Hash(), UrlProtocol.HTTP));
artifact.getUrls().put(Artifact.UrlProtocol.HTTPS, if (artifactUrlHandler.protocolSupported(UrlProtocol.HTTP)) {
artifactUrlHandler.getUrl(targetId, localArtifact.getSoftwareModule().getId(), artifact.getUrls().put(Artifact.UrlProtocol.HTTP,
localArtifact.getFilename(), localArtifact.getSha1Hash(), UrlProtocol.HTTPS)); 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.setFilename(localArtifact.getFilename());
artifact.setHashes(new ArtifactHash(localArtifact.getSha1Hash(), null)); artifact.setHashes(new ArtifactHash(localArtifact.getSha1Hash(), localArtifact.getMd5Hash()));
artifact.setSize(localArtifact.getSize()); artifact.setSize(localArtifact.getSize());
return artifact; return artifact;
} }

View File

@@ -305,7 +305,8 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
final List<SoftwareModule> softwareModuleList = controllerManagement final List<SoftwareModule> softwareModuleList = controllerManagement
.findSoftwareModulesByDistributionSet(distributionSet); .findSoftwareModulesByDistributionSet(distributionSet);
eventBus.post(new TargetAssignDistributionSetEvent(target.getOptLockRevision(), target.getTenant(), 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()));
} }
@@ -336,9 +337,8 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
final Action action = checkActionExist(message, actionUpdateStatus); final Action action = checkActionExist(message, actionUpdateStatus);
final ActionStatus actionStatus = new ActionStatus(); final ActionStatus actionStatus = new ActionStatus();
final List<String> messageText = actionUpdateStatus.getMessage(); actionUpdateStatus.getMessage().forEach(actionStatus::addMessage);
final String messageString = String.join(", ", messageText);
actionStatus.addMessage(messageString);
actionStatus.setAction(action); actionStatus.setAction(action);
actionStatus.setOccurredAt(System.currentTimeMillis()); actionStatus.setOccurredAt(System.currentTimeMillis());

View File

@@ -8,8 +8,8 @@
*/ */
package org.eclipse.hawkbit.amqp; package org.eclipse.hawkbit.amqp;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
@@ -23,6 +23,7 @@ import static org.mockito.Mockito.when;
import java.net.URI; import java.net.URI;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional;
import org.eclipse.hawkbit.AbstractIntegrationTestWithMongoDB; import org.eclipse.hawkbit.AbstractIntegrationTestWithMongoDB;
import org.eclipse.hawkbit.TestDataUtil; import org.eclipse.hawkbit.TestDataUtil;
@@ -58,6 +59,12 @@ import ru.yandex.qatools.allure.annotations.Stories;
@Stories("AmqpMessage Dispatcher Service Test") @Stories("AmqpMessage Dispatcher Service Test")
public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWithMongoDB { 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 AmqpMessageDispatcherService amqpMessageDispatcherService;
private RabbitTemplate rabbitTemplate; private RabbitTemplate rabbitTemplate;
@@ -89,8 +96,7 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWit
@Description("Verfies that download and install event with no software modul works") @Description("Verfies that download and install event with no software modul works")
public void testSendDownloadRequesWithEmptySoftwareModules() { public void testSendDownloadRequesWithEmptySoftwareModules() {
final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent( final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent(
1L, "default", CONTROLLER_ID, 1l, new ArrayList<SoftwareModule>(), 1L, TENANT, CONTROLLER_ID, 1L, new ArrayList<SoftwareModule>(), AMQP_URI, TEST_TOKEN);
IpUtil.createAmqpUri("vHost", "mytest"));
amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent); amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent);
final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress()); final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress());
final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage); final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage);
@@ -104,7 +110,7 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWit
final DistributionSet dsA = TestDataUtil.generateDistributionSet("", softwareManagement, final DistributionSet dsA = TestDataUtil.generateDistributionSet("", softwareManagement,
distributionSetManagement); distributionSetManagement);
final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent( 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); amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent);
final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress()); final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress());
final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage); final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage);
@@ -143,7 +149,7 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWit
Mockito.when(rabbitTemplate.convertSendAndReceive(any())).thenReturn(receivedList); Mockito.when(rabbitTemplate.convertSendAndReceive(any())).thenReturn(receivedList);
final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent( 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); amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent);
final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress()); final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress());
final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage); final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage);
@@ -154,7 +160,19 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWit
if (!softwareModule.getModuleId().equals(module.getId())) { if (!softwareModule.getModuleId().equals(module.getId())) {
continue; 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") @Description("Verfies that send cancel event works")
public void testSendCancelRequest() { public void testSendCancelRequest() {
final CancelTargetAssignmentEvent cancelTargetAssignmentDistributionSetEvent = new CancelTargetAssignmentEvent( final CancelTargetAssignmentEvent cancelTargetAssignmentDistributionSetEvent = new CancelTargetAssignmentEvent(
1L, "default", CONTROLLER_ID, 1l, IpUtil.createAmqpUri("vHost", "mytest")); 1L, TENANT, CONTROLLER_ID, 1L, AMQP_URI);
amqpMessageDispatcherService amqpMessageDispatcherService
.targetCancelAssignmentToDistributionSet(cancelTargetAssignmentDistributionSetEvent); .targetCancelAssignmentToDistributionSet(cancelTargetAssignmentDistributionSetEvent);
final Message sendMessage = createArgumentCapture(cancelTargetAssignmentDistributionSetEvent.getTargetAdress()); final Message sendMessage = createArgumentCapture(cancelTargetAssignmentDistributionSetEvent.getTargetAdress());
@@ -187,13 +205,12 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTestWit
downloadAndUpdateRequest.getActionId(), Long.valueOf(1)); downloadAndUpdateRequest.getActionId(), Long.valueOf(1));
assertEquals("The topic of the event shuold contain DOWNLOAD_AND_INSTALL", EventTopic.DOWNLOAD_AND_INSTALL, assertEquals("The topic of the event shuold contain DOWNLOAD_AND_INSTALL", EventTopic.DOWNLOAD_AND_INSTALL,
sendMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.TOPIC)); sendMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.TOPIC));
assertEquals("Security token of target", downloadAndUpdateRequest.getTargetSecurityToken(), TEST_TOKEN);
return downloadAndUpdateRequest; return downloadAndUpdateRequest;
} }
/**
* @param sendMessage
*/
private void assertEventMessage(final Message sendMessage) { private void assertEventMessage(final Message sendMessage) {
assertNotNull("The message should not be null", sendMessage); assertNotNull("The message should not be null", sendMessage);

View File

@@ -333,20 +333,22 @@ public class AmqpMessageHandlerServiceTest {
assertThat(downloadResponse.getResponseCode()).as("Message body response code is wrong") assertThat(downloadResponse.getResponseCode()).as("Message body response code is wrong")
.isEqualTo(HttpStatus.OK.value()); .isEqualTo(HttpStatus.OK.value());
assertThat(downloadResponse.getArtifact().getSize()).as("Wrong artifact size in message body").isEqualTo(1L); 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") assertThat(downloadResponse.getDownloadUrl()).as("download url is wrong")
.startsWith("http://localhost/api/v1/downloadserver/downloadId/"); .startsWith("http://localhost/api/v1/downloadserver/downloadId/");
} }
@Test @Test
@Description("Tests TODO") @Description("Tests TODO")
public void lookupNextUpdateActionAfterFinished() throws IllegalArgumentException, IllegalAccessException { public void lookupNextUpdateActionAfterFinished() throws IllegalAccessException {
// Mock // Mock
final Action action = createActionWithTarget(22L, Status.FINISHED); final Action action = createActionWithTarget(22L, Status.FINISHED);
when(controllerManagementMock.findActionWithDetails(Matchers.any())).thenReturn(action); when(controllerManagementMock.findActionWithDetails(Matchers.any())).thenReturn(action);
when(controllerManagementMock.addUpdateActionStatus(Matchers.any(), Matchers.any())).thenReturn(action); when(controllerManagementMock.addUpdateActionStatus(Matchers.any(), Matchers.any())).thenReturn(action);
// for the test the same action can be used // 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); actionList.add(action);
when(controllerManagementMock.findActionByTargetAndActive(Matchers.any())).thenReturn(actionList); when(controllerManagementMock.findActionByTargetAndActive(Matchers.any())).thenReturn(actionList);
@@ -372,6 +374,8 @@ public class AmqpMessageHandlerServiceTest {
assertThat(targetAssignDistributionSetEvent.getControllerId()).as("event has wrong controller id") assertThat(targetAssignDistributionSetEvent.getControllerId()).as("event has wrong controller id")
.isEqualTo("target1"); .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.getActionId()).as("event has wrong action id").isEqualTo(22L);
assertThat(targetAssignDistributionSetEvent.getSoftwareModules()).as("event has wrong sofware modules") assertThat(targetAssignDistributionSetEvent.getSoftwareModules()).as("event has wrong sofware modules")
.isEqualTo(softwareModuleList); .isEqualTo(softwareModuleList);
@@ -379,7 +383,7 @@ public class AmqpMessageHandlerServiceTest {
} }
private ActionUpdateStatus createActionUpdateStatus(final ActionStatus status) { private ActionUpdateStatus createActionUpdateStatus(final ActionStatus status) {
return createActionUpdateStatus(status, 2l); return createActionUpdateStatus(status, 2L);
} }
private ActionUpdateStatus createActionUpdateStatus(final ActionStatus status, final Long id) { private ActionUpdateStatus createActionUpdateStatus(final ActionStatus status, final Long id) {
@@ -404,15 +408,14 @@ public class AmqpMessageHandlerServiceTest {
} }
private List<SoftwareModule> createSoftwareModuleList() { private List<SoftwareModule> createSoftwareModuleList() {
final List<SoftwareModule> softwareModuleList = new ArrayList<SoftwareModule>(); final List<SoftwareModule> softwareModuleList = new ArrayList<>();
final SoftwareModule softwareModule = new SoftwareModule(); final SoftwareModule softwareModule = new SoftwareModule();
softwareModule.setId(777L); softwareModule.setId(777L);
softwareModuleList.add(softwareModule); softwareModuleList.add(softwareModule);
return softwareModuleList; return softwareModuleList;
} }
private Action createActionWithTarget(final Long targetId, final Status status) private Action createActionWithTarget(final Long targetId, final Status status) throws IllegalAccessException {
throws IllegalArgumentException, IllegalAccessException {
// is needed for the creation of targets // is needed for the creation of targets
initalizeSecurityTokenGenerator(); initalizeSecurityTokenGenerator();
@@ -427,7 +430,7 @@ public class AmqpMessageHandlerServiceTest {
return action; return action;
} }
private void initalizeSecurityTokenGenerator() throws IllegalArgumentException, IllegalAccessException { private void initalizeSecurityTokenGenerator() throws IllegalAccessException {
final SecurityTokenGeneratorHolder instance = SecurityTokenGeneratorHolder.getInstance(); final SecurityTokenGeneratorHolder instance = SecurityTokenGeneratorHolder.getInstance();
final Field[] fields = instance.getClass().getDeclaredFields(); final Field[] fields = instance.getClass().getDeclaredFields();
for (final Field field : fields) { for (final Field field : fields) {

View File

@@ -52,6 +52,8 @@ public class BaseAmqpServiceTest {
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(); final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus();
actionUpdateStatus.setActionId(1L); actionUpdateStatus.setActionId(1L);
actionUpdateStatus.setSoftwareModuleId(2L); actionUpdateStatus.setSoftwareModuleId(2L);
actionUpdateStatus.getMessage().add("Message 1");
actionUpdateStatus.getMessage().add("Message 2");
final Message message = rabbitTemplate.getMessageConverter().toMessage(actionUpdateStatus, final Message message = rabbitTemplate.getMessageConverter().toMessage(actionUpdateStatus,
new MessageProperties()); new MessageProperties());

View File

@@ -13,10 +13,6 @@ import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
* JSON representation of artifact hash. * JSON representation of artifact hash.
*
*
*
*
*/ */
public class ArtifactHash { public class ArtifactHash {

View File

@@ -19,15 +19,16 @@ import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
* JSON representation of download and update request. * JSON representation of download and update request.
* *
*
*
*
*/ */
@JsonInclude(Include.NON_NULL) @JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class DownloadAndUpdateRequest { public class DownloadAndUpdateRequest {
@JsonProperty @JsonProperty
private Long actionId; private Long actionId;
@JsonProperty
private String targetSecurityToken;
@JsonProperty @JsonProperty
private final List<SoftwareModule> softwareModules = new LinkedList<>(); private final List<SoftwareModule> softwareModules = new LinkedList<>();
@@ -39,6 +40,14 @@ public class DownloadAndUpdateRequest {
this.actionId = correlator; this.actionId = correlator;
} }
public String getTargetSecurityToken() {
return targetSecurityToken;
}
public void setTargetSecurityToken(final String targetSecurityToken) {
this.targetSecurityToken = targetSecurityToken;
}
public List<SoftwareModule> getSoftwareModules() { public List<SoftwareModule> getSoftwareModules() {
return softwareModules; return softwareModules;
} }

View File

@@ -26,8 +26,6 @@ import com.fasterxml.jackson.annotation.JsonProperty;
public class TenantSecurityToken { public class TenantSecurityToken {
public static final String AUTHORIZATION_HEADER = "Authorization"; 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 @JsonProperty
private final String tenant; private final String tenant;

View File

@@ -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.CacheManagerHolder;
import org.eclipse.hawkbit.repository.model.helper.SecurityTokenGeneratorHolder; import org.eclipse.hawkbit.repository.model.helper.SecurityTokenGeneratorHolder;
import org.eclipse.hawkbit.repository.model.helper.SystemManagementHolder; 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.TenantAwareHolder;
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
import org.eclipse.hawkbit.security.SecurityTokenGenerator; import org.eclipse.hawkbit.security.SecurityTokenGenerator;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
@@ -48,14 +51,24 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess
@EnableAutoConfiguration @EnableAutoConfiguration
public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { 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 * @return the {@link TenantConfigurationManagement} singleton bean which
* make it accessible in beans which cannot access the service * make it accessible in beans which cannot access the service
* directly, e.g. JPA entities. * directly, e.g. JPA entities.
*/ */
@Bean @Bean
public TenantConfigurationManagement tenantConfigurationManagement() { public TenantConfigurationManagementHolder tenantConfigurationManagementHolder() {
return TenantConfigurationManagement.getInstance(); return TenantConfigurationManagementHolder.getInstance();
} }
/** /**

View File

@@ -90,6 +90,8 @@ public class ExceptionMappingAspectHandler implements Ordered {
+ " || execution( * org.eclipse.hawkbit.controller.*.*(..)) " + " || execution( * org.eclipse.hawkbit.controller.*.*(..)) "
+ " || execution( * org.eclipse.hawkbit.rest.resource.*.*(..)) " + " || execution( * org.eclipse.hawkbit.rest.resource.*.*(..)) "
+ " || execution( * org.eclipse.hawkbit.service.*.*(..)) )", throwing = "ex") + " || execution( * org.eclipse.hawkbit.service.*.*(..)) )", throwing = "ex")
// Exception squid:S00112 - Is aspectJ proxy
@SuppressWarnings({ "squid:S00112" })
public void catchAndWrapJpaExceptionsService(final Exception ex) throws Throwable { public void catchAndWrapJpaExceptionsService(final Exception ex) throws Throwable {
LOG.trace("exception occured", ex); LOG.trace("exception occured", ex);
Exception translatedAccessException = translateEclipseLinkExceptionIfPossible(ex); Exception translatedAccessException = translateEclipseLinkExceptionIfPossible(ex);

View File

@@ -20,9 +20,9 @@ import org.eclipse.hawkbit.eventbus.event.TargetDeletedEvent;
import org.eclipse.hawkbit.eventbus.event.TargetInfoUpdateEvent; import org.eclipse.hawkbit.eventbus.event.TargetInfoUpdateEvent;
import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor; import org.eclipse.hawkbit.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.TargetRepository; 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.Target;
import org.eclipse.hawkbit.repository.model.TargetInfo; import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -67,6 +67,8 @@ public class EntityChangeEventListener {
* {@link ProceedingJoinPoint#proceed()} * {@link ProceedingJoinPoint#proceed()}
*/ */
@Around("execution(* org.eclipse.hawkbit.repository.TargetInfoRepository.save(..))") @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 { public Object targetCreated(final ProceedingJoinPoint joinpoint) throws Throwable {
final boolean isNew = isTargetInfoNew(joinpoint.getArgs()[0]); final boolean isNew = isTargetInfoNew(joinpoint.getArgs()[0]);
final Object result = joinpoint.proceed(); final Object result = joinpoint.proceed();
@@ -92,6 +94,8 @@ public class EntityChangeEventListener {
* {@link ProceedingJoinPoint#proceed()} * {@link ProceedingJoinPoint#proceed()}
*/ */
@Around("execution(* org.eclipse.hawkbit.repository.TargetRepository.deleteByIdIn(..))") @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 { public Object targetDeletedById(final ProceedingJoinPoint joinpoint) throws Throwable {
final String currentTenant = tenantAware.getCurrentTenant(); final String currentTenant = tenantAware.getCurrentTenant();
final Object result = joinpoint.proceed(); final Object result = joinpoint.proceed();
@@ -111,8 +115,9 @@ public class EntityChangeEventListener {
* in case exception happens in the * in case exception happens in the
* {@link ProceedingJoinPoint#proceed()} * {@link ProceedingJoinPoint#proceed()}
*/ */
@SuppressWarnings("unchecked")
@Around("execution(* org.eclipse.hawkbit.repository.TargetRepository.delete(..))") @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 { public Object targetDeleted(final ProceedingJoinPoint joinpoint) throws Throwable {
final String currentTenant = tenantAware.getCurrentTenant(); final String currentTenant = tenantAware.getCurrentTenant();
final Object result = joinpoint.proceed(); final Object result = joinpoint.proceed();

View File

@@ -16,8 +16,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
/** /**
* Event that gets sent when a distribution set gets assigned to a target. * Event that gets sent when a distribution set gets assigned to a target.
* *
*
*
*/ */
public class TargetAssignDistributionSetEvent extends AbstractEvent { public class TargetAssignDistributionSetEvent extends AbstractEvent {
@@ -25,6 +23,7 @@ public class TargetAssignDistributionSetEvent extends AbstractEvent {
private final String controllerId; private final String controllerId;
private final Long actionId; private final Long actionId;
private final URI targetAdress; private final URI targetAdress;
private final String targetToken;
/** /**
* Creates a new {@link TargetAssignDistributionSetEvent}. * Creates a new {@link TargetAssignDistributionSetEvent}.
@@ -41,14 +40,18 @@ public class TargetAssignDistributionSetEvent extends AbstractEvent {
* the software modules which have been assigned to the target * the software modules which have been assigned to the target
* @param targetAdress * @param targetAdress
* the targetAdress of the target * the targetAdress of the target
* @param targetToken
* the authentication token of the target
*/ */
public TargetAssignDistributionSetEvent(final long revision, final String tenant, final String controllerId, 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); super(revision, tenant);
this.controllerId = controllerId; this.controllerId = controllerId;
this.actionId = actionId; this.actionId = actionId;
this.softwareModules = softwareModules; this.softwareModules = softwareModules;
this.targetAdress = targetAdress; this.targetAdress = targetAdress;
this.targetToken = targetToken;
} }
/** /**
@@ -77,4 +80,7 @@ public class TargetAssignDistributionSetEvent extends AbstractEvent {
return targetAdress; return targetAdress;
} }
public String getTargetToken() {
return targetToken;
}
} }

View File

@@ -29,6 +29,8 @@ public class AfterTransactionCommitDefaultServiceExecutor extends TransactionSyn
private static final ThreadLocal<List<Runnable>> THREAD_LOCAL_RUNNABLES = new ThreadLocal<>(); private static final ThreadLocal<List<Runnable>> THREAD_LOCAL_RUNNABLES = new ThreadLocal<>();
@Override @Override
// Exception squid:S1217 - Is aspectJ proxy
@SuppressWarnings({ "squid:S1217" })
public void afterCommit() { public void afterCommit() {
final List<Runnable> afterCommitRunnables = THREAD_LOCAL_RUNNABLES.get(); final List<Runnable> afterCommitRunnables = THREAD_LOCAL_RUNNABLES.get();
LOGGER.debug("Transaction successfully committed, executing {} runnables", afterCommitRunnables.size()); LOGGER.debug("Transaction successfully committed, executing {} runnables", afterCommitRunnables.size());
@@ -60,6 +62,7 @@ public class AfterTransactionCommitDefaultServiceExecutor extends TransactionSyn
} }
@Override @Override
@SuppressWarnings({ "squid:S1217" })
public void afterCompletion(final int status) { public void afterCompletion(final int status) {
final String transactionStatus = status == STATUS_COMMITTED ? "COMMITTED" : "ROLLEDBACK"; final String transactionStatus = status == STATUS_COMMITTED ? "COMMITTED" : "ROLLEDBACK";
LOGGER.debug("Transaction completed after commit with status {}", transactionStatus); LOGGER.debug("Transaction completed after commit with status {}", transactionStatus);

View File

@@ -29,13 +29,14 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
/** /**
* {@link Action} repository. * {@link Action} repository.
* *
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public interface ActionRepository extends BaseEntityRepository<Action, Long>, JpaSpecificationExecutor<Action> { public interface ActionRepository extends BaseEntityRepository<Action, Long>, JpaSpecificationExecutor<Action> {
/** /**
* Retrieves an Action with all lazy attributes. * Retrieves an Action with all lazy attributes.
@@ -172,7 +173,7 @@ public interface ActionRepository extends BaseEntityRepository<Action, Long>, Jp
* active * active
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Query("UPDATE Action a SET a.active = false WHERE a IN :keySet AND a.target IN :targetsIds") @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); 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 * the current status of the actions which are affected
*/ */
@Modifying @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") @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, void switchStatus(@Param("statusToSet") Action.Status statusToSet, @Param("targetsIds") List<Long> targetIds,
@Param("active") boolean active, @Param("currentStatus") Action.Status currentStatus); @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 * the current status of the actions which are affected
*/ */
@Modifying @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") @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, void switchStatus(@Param("statusToSet") Action.Status statusToSet, @Param("rollout") Rollout rollout,
@Param("active") boolean active, @Param("currentStatus") Action.Status currentStatus); @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") @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); List<TotalTargetCountActionStatus> getStatusCountByRolloutGroupId(List<Long> rolloutGroupId);
// Asha-ends here
} }

View File

@@ -16,13 +16,14 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType; import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
/** /**
* {@link ActionStatus} repository. * {@link ActionStatus} repository.
* *
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public interface ActionStatusRepository public interface ActionStatusRepository
extends BaseEntityRepository<ActionStatus, Long>, JpaSpecificationExecutor<ActionStatus> { extends BaseEntityRepository<ActionStatus, Long>, JpaSpecificationExecutor<ActionStatus> {

View File

@@ -43,17 +43,15 @@ import org.springframework.data.jpa.repository.Modifying;
import org.springframework.hateoas.Identifiable; import org.springframework.hateoas.Identifiable;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; 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 @Validated
@Service @Service
public class ArtifactManagement { public class ArtifactManagement {
@@ -108,7 +106,7 @@ public class ArtifactManagement {
* if check against provided SHA1 checksum failed * if check against provided SHA1 checksum failed
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public LocalArtifact createLocalArtifact(@NotNull final InputStream stream, @NotNull final Long moduleId, public LocalArtifact createLocalArtifact(@NotNull final InputStream stream, @NotNull final Long moduleId,
@NotEmpty final String filename, final String providedMd5Sum, final String providedSha1Sum, @NotEmpty final String filename, final String providedMd5Sum, final String providedSha1Sum,
@@ -138,7 +136,7 @@ public class ArtifactManagement {
return storeArtifactMetadata(softwareModule, filename, result, existing); 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) { final SoftwareModule softwareModule) {
if (softwareModule.getLocalArtifactByFilename(filename).isPresent()) { if (softwareModule.getLocalArtifactByFilename(filename).isPresent()) {
if (overrideExisting) { if (overrideExisting) {
@@ -222,9 +220,7 @@ public class ArtifactManagement {
artifact.setSize(result.getSize()); artifact.setSize(result.getSize());
LOG.debug("storing new artifact into repository {}", artifact); LOG.debug("storing new artifact into repository {}", artifact);
final LocalArtifact artifactPersisted = localArtifactRepository.save(artifact); return localArtifactRepository.save(artifact);
return artifactPersisted;
} }
/** /**
@@ -242,7 +238,7 @@ public class ArtifactManagement {
* @return created {@link ExternalArtifactProvider} * @return created {@link ExternalArtifactProvider}
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public ExternalArtifactProvider createExternalArtifactProvider(@NotEmpty final String name, public ExternalArtifactProvider createExternalArtifactProvider(@NotEmpty final String name,
final String description, @NotNull final String basePath, final String defaultUrlSuffix) { 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 * if {@link SoftwareModule} with given ID does not exist
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public ExternalArtifact createExternalArtifact(@NotNull final ExternalArtifactProvider externalRepository, public ExternalArtifact createExternalArtifact(@NotNull final ExternalArtifactProvider externalRepository,
final String urlSuffix, @NotNull final Long moduleId) { final String urlSuffix, @NotNull final Long moduleId) {
final SoftwareModule module = getModuleAndThrowExceptionIfThatFails(moduleId); final SoftwareModule module = getModuleAndThrowExceptionIfThatFails(moduleId);
final ExternalArtifact result = externalArtifactRepository return externalArtifactRepository.save(new ExternalArtifact(externalRepository, urlSuffix, module));
.save(new ExternalArtifact(externalRepository, urlSuffix, module));
return result;
} }
/** /**
@@ -290,7 +283,7 @@ public class ArtifactManagement {
* *
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
public void deleteLocalArtifact(@NotNull final Long id) { public void deleteLocalArtifact(@NotNull final Long id) {
final LocalArtifact existing = localArtifactRepository.findOne(id); final LocalArtifact existing = localArtifactRepository.findOne(id);
@@ -313,7 +306,7 @@ public class ArtifactManagement {
* the related local artifact * the related local artifact
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
public void deleteGridFsArtifact(@NotNull final LocalArtifact existing) { public void deleteGridFsArtifact(@NotNull final LocalArtifact existing) {
if (existing == null) { if (existing == null) {
@@ -324,7 +317,7 @@ public class ArtifactManagement {
for (final LocalArtifact lArtifact : localArtifactRepository for (final LocalArtifact lArtifact : localArtifactRepository
.findByGridFsFileName(existing.getGridFsFileName())) { .findByGridFsFileName(existing.getGridFsFileName())) {
if (!lArtifact.getSoftwareModule().isDeleted() if (!lArtifact.getSoftwareModule().isDeleted()
&& lArtifact.getSoftwareModule().getId() != existing.getSoftwareModule().getId()) { && Long.compare(lArtifact.getSoftwareModule().getId(), existing.getSoftwareModule().getId()) != 0) {
artifactIsOnlyUsedByOneSoftwareModule = false; artifactIsOnlyUsedByOneSoftwareModule = false;
break; break;
} }
@@ -350,7 +343,7 @@ public class ArtifactManagement {
* *
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
public void deleteExternalArtifact(@NotNull final Long id) { public void deleteExternalArtifact(@NotNull final Long id) {
final ExternalArtifact existing = externalArtifactRepository.findOne(id); final ExternalArtifact existing = externalArtifactRepository.findOne(id);
@@ -425,17 +418,17 @@ public class ArtifactManagement {
* @param filename * @param filename
* of the artifact * of the artifact
* @param overrideExisting * @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 * if it already exists
* @param contentType * @param contentType
* the contentType of the file * the contentType of the file
* *
* @return uploaded {@link LocalArtifact} * @return uploaded {@link LocalArtifact}
* *
* @throw ArtifactUploadFailedException if upload failes * @throw ArtifactUploadFailedException if upload fails
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public LocalArtifact createLocalArtifact(final InputStream inputStream, final Long moduleId, final String filename, public LocalArtifact createLocalArtifact(final InputStream inputStream, final Long moduleId, final String filename,
final boolean overrideExisting, final String contentType) { final boolean overrideExisting, final String contentType) {
@@ -461,7 +454,7 @@ public class ArtifactManagement {
* @throw ArtifactUploadFailedException if upload failes * @throw ArtifactUploadFailedException if upload failes
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public LocalArtifact createLocalArtifact(final InputStream inputStream, final Long moduleId, final String filename, public LocalArtifact createLocalArtifact(final InputStream inputStream, final Long moduleId, final String filename,
final boolean overrideExisting) { final boolean overrideExisting) {

View File

@@ -14,21 +14,19 @@ import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
/** /**
* Command repository operations for all {@link TenantAwareBaseEntity}s. * Command repository operations for all {@link TenantAwareBaseEntity}s.
* *
*
*
*
* @param <T> * @param <T>
* type if the entity type * type if the entity type
* @param <I> * @param <I>
* of the entity type * of the entity type
*/ */
@NoRepositoryBean @NoRepositoryBean
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public interface BaseEntityRepository<T extends TenantAwareBaseEntity, I extends Serializable> public interface BaseEntityRepository<T extends TenantAwareBaseEntity, I extends Serializable>
extends PagingAndSortingRepository<T, I> { extends PagingAndSortingRepository<T, I> {
@@ -39,7 +37,7 @@ public interface BaseEntityRepository<T extends TenantAwareBaseEntity, I extends
* to delete data from * to delete data from
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
void deleteByTenantIgnoreCase(String tenant); void deleteByTenantIgnoreCase(String tenant);
} }

View File

@@ -46,6 +46,7 @@ import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -56,13 +57,15 @@ import org.springframework.validation.annotation.Validated;
* *
* *
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated @Validated
@Service @Service
public class ControllerManagement { public class ControllerManagement {
private static final Logger LOG = LoggerFactory.getLogger(ControllerManagement.class); private static final Logger LOG = LoggerFactory.getLogger(ControllerManagement.class);
private static final Logger LOG_DOS = LoggerFactory.getLogger("server-security.dos"); private static final Logger LOG_DOS = LoggerFactory.getLogger("server-security.dos");
public static final String SERVER_MESSAGE_PREFIX = "Update Server: ";
@Autowired @Autowired
private EntityManager entityManager; private EntityManager entityManager;
@@ -131,7 +134,7 @@ public class ControllerManagement {
* if target with given ID could not be found * if target with given ID could not be found
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public Target updateLastTargetQuery(@NotEmpty final String targetid, final URI address) { public Target updateLastTargetQuery(@NotEmpty final String targetid, final URI address) {
final Target target = targetRepository.findByControllerId(targetid); final Target target = targetRepository.findByControllerId(targetid);
@@ -205,7 +208,7 @@ public class ControllerManagement {
* *
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public TargetInfo updateLastTargetQuery(@NotNull final TargetInfo target, final URI address) { public TargetInfo updateLastTargetQuery(@NotNull final TargetInfo target, final URI address) {
return updateTargetStatus(target, null, System.currentTimeMillis(), address); return updateTargetStatus(target, null, System.currentTimeMillis(), address);
@@ -262,7 +265,7 @@ public class ControllerManagement {
* @return target reference * @return target reference
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public Target findOrRegisterTargetIfItDoesNotexist(@NotEmpty final String targetid, final URI address) { public Target findOrRegisterTargetIfItDoesNotexist(@NotEmpty final String targetid, final URI address) {
final Specification<Target> spec = (targetRoot, query, cb) -> cb.equal(targetRoot.get(Target_.controllerId), final Specification<Target> spec = (targetRoot, query, cb) -> cb.equal(targetRoot.get(Target_.controllerId),
@@ -298,7 +301,7 @@ public class ControllerManagement {
* @return the updated TargetInfo * @return the updated TargetInfo
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public TargetInfo updateTargetStatus(@NotNull final TargetInfo targetInfo, final TargetUpdateStatus status, public TargetInfo updateTargetStatus(@NotNull final TargetInfo targetInfo, final TargetUpdateStatus status,
final Long lastTargetQuery, final URI address) { final Long lastTargetQuery, final URI address) {
@@ -327,7 +330,7 @@ public class ControllerManagement {
* *
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public Action addCancelActionStatus(@NotNull final ActionStatus actionStatus, final Action action) { public Action addCancelActionStatus(@NotNull final ActionStatus actionStatus, final Action action) {
@@ -341,13 +344,14 @@ public class ControllerManagement {
break; break;
case CANCELED: case CANCELED:
case FINISHED: 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. // 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); deploymentManagement.successCancellation(action);
break; break;
case RETRIEVED: case RETRIEVED:
actionStatus.addMessage("Cancelation request retrieved"); actionStatus.addMessage(ControllerManagement.SERVER_MESSAGE_PREFIX + "Cancellation request retrieved.");
break; break;
default: default:
} }
@@ -373,7 +377,7 @@ public class ControllerManagement {
* inserted * inserted
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public Action addUpdateActionStatus(@NotNull final ActionStatus actionStatus, final Action action) { public Action addUpdateActionStatus(@NotNull final ActionStatus actionStatus, final Action action) {
@@ -481,7 +485,7 @@ public class ControllerManagement {
*/ */
@Modifying @Modifying
@NotNull @NotNull
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public Target updateControllerAttributes(@NotEmpty final String targetid, @NotNull final Map<String, String> data) { public Target updateControllerAttributes(@NotEmpty final String targetid, @NotNull final Map<String, String> data) {
final Target target = targetRepository.findByControllerId(targetid); final Target target = targetRepository.findByControllerId(targetid);
@@ -519,7 +523,7 @@ public class ControllerManagement {
* {@link Status#RETRIEVED} * {@link Status#RETRIEVED}
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
public Action registerRetrieved(final Action action, final String message) { public Action registerRetrieved(final Action action, final String message) {
return handleRegisterRetrieved(action, message); return handleRegisterRetrieved(action, message);
@@ -583,7 +587,7 @@ public class ControllerManagement {
*/ */
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void addActionStatusMessage(final ActionStatus statusMessage) { public void addActionStatusMessage(final ActionStatus statusMessage) {
actionStatusRepository.save(statusMessage); actionStatusRepository.save(statusMessage);
} }
@@ -600,7 +604,7 @@ public class ControllerManagement {
* @return the security context of the target, in case no target exists for * @return the security context of the target, in case no target exists for
* the given controllerId {@code null} is returned * the given controllerId {@code null} is returned
*/ */
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
public String getSecurityTokenByControllerId(final String controllerId) { public String getSecurityTokenByControllerId(final String controllerId) {
final Target target = targetRepository.findByControllerId(controllerId); final Target target = targetRepository.findByControllerId(controllerId);
return target != null ? target.getSecurityToken() : null; return target != null ? target.getSecurityToken() : null;

View File

@@ -67,6 +67,7 @@ import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; 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 @Validated
@Service @Service
public class DeploymentManagement { public class DeploymentManagement {
@@ -133,7 +134,7 @@ public class DeploymentManagement {
* {@link SoftwareModuleType} are not assigned as define by the * {@link SoftwareModuleType} are not assigned as define by the
* {@link DistributionSetType}. * * {@link DistributionSetType}. *
*/ */
@Transactional @Transactional(isolation = Isolation.READ_COMMITTED)
@Modifying @Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true) @CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
@@ -167,7 +168,7 @@ public class DeploymentManagement {
* {@link DistributionSetType}. * {@link DistributionSetType}.
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_COMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true) @CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
public DistributionSetAssignmentResult assignDistributionSet(@NotNull final Long dsID, public DistributionSetAssignmentResult assignDistributionSet(@NotNull final Long dsID,
@@ -195,9 +196,12 @@ public class DeploymentManagement {
* {@link DistributionSetType}. * {@link DistributionSetType}.
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true) @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, public DistributionSetAssignmentResult assignDistributionSet(@NotNull final Long dsID, final ActionType actionType,
final long forcedTimestamp, @NotEmpty final String... targetIDs) { final long forcedTimestamp, @NotEmpty final String... targetIDs) {
return assignDistributionSet(dsID, Arrays.stream(targetIDs) return assignDistributionSet(dsID, Arrays.stream(targetIDs)
@@ -219,7 +223,7 @@ public class DeploymentManagement {
* {@link DistributionSetType}. * {@link DistributionSetType}.
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_COMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true) @CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
public DistributionSetAssignmentResult assignDistributionSet(@NotNull final Long dsID, public DistributionSetAssignmentResult assignDistributionSet(@NotNull final Long dsID,
@@ -243,8 +247,8 @@ public class DeploymentManagement {
* a list of all targets and their action type * a list of all targets and their action type
* @param rollout * @param rollout
* the rollout for this assignment * the rollout for this assignment
* @param rolloutgroup * @param rolloutGroup
* the rolloutgroup for this assignment * the rollout group for this assignment
* @return the assignment result * @return the assignment result
* *
* @throw IncompleteDistributionSetException if mandatory * @throw IncompleteDistributionSetException if mandatory
@@ -252,7 +256,7 @@ public class DeploymentManagement {
* {@link DistributionSetType}. * {@link DistributionSetType}.
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_COMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true) @CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
public DistributionSetAssignmentResult assignDistributionSet(@NotNull final Long dsID, public DistributionSetAssignmentResult assignDistributionSet(@NotNull final Long dsID,
@@ -276,8 +280,8 @@ public class DeploymentManagement {
* a list of all targets and their action type * a list of all targets and their action type
* @param rollout * @param rollout
* the rollout for this assignment * the rollout for this assignment
* @param rolloutgroup * @param rolloutGroup
* the rolloutgroup for this assignment * the rollout group for this assignment
* @return the assignment result * @return the assignment result
* *
* @throw IncompleteDistributionSetException if mandatory * @throw IncompleteDistributionSetException if mandatory
@@ -389,8 +393,8 @@ public class DeploymentManagement {
softwareModules)); softwareModules));
} }
private Action createTargetAction(final Map<String, TargetWithActionType> targetsWithActionMap, final Target target, private static Action createTargetAction(final Map<String, TargetWithActionType> targetsWithActionMap,
final DistributionSet set, final Rollout rollout, final RolloutGroup rolloutGroup) { final Target target, final DistributionSet set, final Rollout rollout, final RolloutGroup rolloutGroup) {
final Action actionForTarget = new Action(); final Action actionForTarget = new Action();
final TargetWithActionType targetWithActionType = targetsWithActionMap.get(target.getControllerId()); final TargetWithActionType targetWithActionType = targetsWithActionMap.get(target.getControllerId());
actionForTarget.setActionType(targetWithActionType.getActionType()); actionForTarget.setActionType(targetWithActionType.getActionType());
@@ -421,13 +425,14 @@ public class DeploymentManagement {
afterCommit.afterCommit(() -> { afterCommit.afterCommit(() -> {
eventBus.post(new TargetInfoUpdateEvent(target.getTargetInfo())); eventBus.post(new TargetInfoUpdateEvent(target.getTargetInfo()));
eventBus.post(new TargetAssignDistributionSetEvent(target.getOptLockRevision(), target.getTenant(), 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 * Removes {@link UpdateAction}s that are no longer necessary and sends
* cancelations to the controller. * cancellations to the controller.
* *
* @param myTarget * @param myTarget
* to override {@link UpdateAction}s * to override {@link UpdateAction}s
@@ -512,7 +517,7 @@ public class DeploymentManagement {
* action * action
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_COMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public Action cancelAction(@NotNull final Action action, @NotNull final Target target) { public Action cancelAction(@NotNull final Action action, @NotNull final Target target) {
LOG.debug("cancelAction({}, {})", action, target); LOG.debug("cancelAction({}, {})", action, target);
@@ -569,7 +574,7 @@ public class DeploymentManagement {
* in case the given action is not active * in case the given action is not active
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_COMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public Action forceQuitAction(@NotNull final Action action) { public Action forceQuitAction(@NotNull final Action action) {
final Action mergedAction = entityManager.merge(action); final Action mergedAction = entityManager.merge(action);
@@ -614,7 +619,7 @@ public class DeploymentManagement {
* the rolloutgroup for this action * the rolloutgroup for this action
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_COMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public void createScheduledAction(final List<Target> targets, final DistributionSet distributionSet, public void createScheduledAction(final List<Target> targets, final DistributionSet distributionSet,
final ActionType actionType, final long forcedTime, final Rollout rollout, final ActionType actionType, final long forcedTime, final Rollout rollout,
@@ -648,7 +653,7 @@ public class DeploymentManagement {
* @return the action which has been started * @return the action which has been started
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_COMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE) + SpringEvalExpressions.IS_SYSTEM_CODE)
public Action startScheduledAction(@NotNull final Action action) { public Action startScheduledAction(@NotNull final Action action) {
@@ -911,7 +916,7 @@ public class DeploymentManagement {
* @return the updated or the found {@link TargetAction} * @return the updated or the found {@link TargetAction}
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public Action forceTargetAction(final Long actionId) { public Action forceTargetAction(final Long actionId) {
final Action action = actionRepository.findOne(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}. * {@link Action} and {@link Target}.
* *
* @param pageReq * @param pageReq
@@ -981,11 +986,11 @@ public class DeploymentManagement {
* @param rollout * @param rollout
* the rollout the actions belong to * the rollout the actions belong to
* @param rolloutGroupParent * @param rolloutGroupParent
* the parent rolloutgroup the actions should reference * the parent rollout group the actions should reference
* @param actionStatus * @param actionStatus
* the status the actions have * the status the actions have
* @return the actions referring a specific rollout and a specific parent * @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 @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE) + SpringEvalExpressions.IS_SYSTEM_CODE)

View File

@@ -59,6 +59,7 @@ import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -69,7 +70,7 @@ import com.google.common.eventbus.EventBus;
* Business facade for managing the {@link DistributionSet}s. * Business facade for managing the {@link DistributionSet}s.
* *
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated @Validated
@Service @Service
public class DistributionSetManagement { public class DistributionSetManagement {
@@ -142,7 +143,7 @@ public class DistributionSetManagement {
* the assignment outcome. * the assignment outcome.
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@NotNull @NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public DistributionSetTagAssignmentResult toggleTagAssignment(@NotEmpty final List<DistributionSet> sets, public DistributionSetTagAssignmentResult toggleTagAssignment(@NotEmpty final List<DistributionSet> sets,
@@ -164,7 +165,7 @@ public class DistributionSetManagement {
* the assignment outcome. * the assignment outcome.
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@NotNull @NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public DistributionSetTagAssignmentResult toggleTagAssignment(@NotEmpty final Collection<Long> dsIds, public DistributionSetTagAssignmentResult toggleTagAssignment(@NotEmpty final Collection<Long> dsIds,
@@ -229,7 +230,7 @@ public class DistributionSetManagement {
* @throw DataDependencyViolationException in case of illegal update * @throw DataDependencyViolationException in case of illegal update
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public DistributionSet updateDistributionSet(@NotNull final DistributionSet ds) { public DistributionSet updateDistributionSet(@NotNull final DistributionSet ds) {
checkNotNull(ds.getId()); checkNotNull(ds.getId());
@@ -254,7 +255,7 @@ public class DistributionSetManagement {
* to delete * to delete
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
public void deleteDistributionSet(@NotNull final DistributionSet set) { public void deleteDistributionSet(@NotNull final DistributionSet set) {
deleteDistributionSet(set.getId()); deleteDistributionSet(set.getId());
@@ -269,7 +270,7 @@ public class DistributionSetManagement {
* to be deleted * to be deleted
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
public void deleteDistributionSet(@NotEmpty final Long... distributionSetIDs) { public void deleteDistributionSet(@NotEmpty final Long... distributionSetIDs) {
final List<Long> toHardDelete = new ArrayList<>(); final List<Long> toHardDelete = new ArrayList<>();
@@ -310,7 +311,7 @@ public class DistributionSetManagement {
* {@link SoftwareModule}s. * {@link SoftwareModule}s.
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public DistributionSet createDistributionSet(@NotNull final DistributionSet dSet) { public DistributionSet createDistributionSet(@NotNull final DistributionSet dSet) {
prepareDsSave(dSet); prepareDsSave(dSet);
@@ -344,7 +345,7 @@ public class DistributionSetManagement {
* {@link SoftwareModule}s. * {@link SoftwareModule}s.
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public List<DistributionSet> createDistributionSets(@NotNull final Iterable<DistributionSet> distributionSets) { public List<DistributionSet> createDistributionSets(@NotNull final Iterable<DistributionSet> distributionSets) {
for (final DistributionSet ds : distributionSets) { for (final DistributionSet ds : distributionSets) {
@@ -366,7 +367,7 @@ public class DistributionSetManagement {
* @return the updated {@link DistributionSet}. * @return the updated {@link DistributionSet}.
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public DistributionSet assignSoftwareModules(@NotNull final DistributionSet ds, public DistributionSet assignSoftwareModules(@NotNull final DistributionSet ds,
final Set<SoftwareModule> softwareModules) { final Set<SoftwareModule> softwareModules) {
@@ -388,7 +389,7 @@ public class DistributionSetManagement {
* @return the updated {@link DistributionSet}. * @return the updated {@link DistributionSet}.
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public DistributionSet unassignSoftwareModule(@NotNull final DistributionSet ds, public DistributionSet unassignSoftwareModule(@NotNull final DistributionSet ds,
final SoftwareModule softwareModule) { final SoftwareModule softwareModule) {
@@ -413,7 +414,7 @@ public class DistributionSetManagement {
* s while the DS type is already in use. * s while the DS type is already in use.
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public DistributionSetType updateDistributionSetType(@NotNull final DistributionSetType dsType) { public DistributionSetType updateDistributionSetType(@NotNull final DistributionSetType dsType) {
checkNotNull(dsType.getId()); checkNotNull(dsType.getId());
@@ -715,7 +716,7 @@ public class DistributionSetManagement {
* @return created {@link Entity} * @return created {@link Entity}
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public DistributionSetType createDistributionSetType(@NotNull final DistributionSetType type) { public DistributionSetType createDistributionSetType(@NotNull final DistributionSetType type) {
if (type.getId() != null) { if (type.getId() != null) {
@@ -732,7 +733,7 @@ public class DistributionSetManagement {
* to delete * to delete
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
public void deleteDistributionSetType(@NotNull final DistributionSetType type) { 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 * in case the meta data entry already exists for the specific
* key * key
*/ */
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public DistributionSetMetadata createDistributionSetMetadata(@NotNull final DistributionSetMetadata metadata) { 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 * in case one of the meta data entry already exists for the
* specific key * specific key
*/ */
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public List<DistributionSetMetadata> createDistributionSetMetadata( public List<DistributionSetMetadata> createDistributionSetMetadata(
@@ -802,7 +803,7 @@ public class DistributionSetManagement {
* in case the meta data entry does not exists and cannot be * in case the meta data entry does not exists and cannot be
* updated * updated
*/ */
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public DistributionSetMetadata updateDistributionSetMetadata(@NotNull final DistributionSetMetadata metadata) { public DistributionSetMetadata updateDistributionSetMetadata(@NotNull final DistributionSetMetadata metadata) {
@@ -820,7 +821,7 @@ public class DistributionSetManagement {
* @param id * @param id
* the ID of the distribution set meta data to delete * the ID of the distribution set meta data to delete
*/ */
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public void deleteDistributionSetMetadata(@NotNull final DsMetadataCompositeKey id) { public void deleteDistributionSetMetadata(@NotNull final DsMetadataCompositeKey id) {
@@ -913,7 +914,7 @@ public class DistributionSetManagement {
* @return created {@link Entity} * @return created {@link Entity}
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public List<DistributionSetType> createDistributionSetTypes(@NotNull final Collection<DistributionSetType> types) { public List<DistributionSetType> createDistributionSetTypes(@NotNull final Collection<DistributionSetType> types) {
return types.stream().map(this::createDistributionSetType).collect(Collectors.toList()); return types.stream().map(this::createDistributionSetType).collect(Collectors.toList());
@@ -926,7 +927,7 @@ public class DistributionSetManagement {
* @param softwareModules * @param softwareModules
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public void checkDistributionSetAlreadyUse(final DistributionSet distributionSet) { public void checkDistributionSetAlreadyUse(final DistributionSet distributionSet) {
checkDistributionSetSoftwareModulesIsAllowedToModify(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()) { if (distributionSetFilter.getSelectDSWithNoTag() != null && distributionSetFilter.getSelectDSWithNoTag()) {
return true; return true;
} }
return false; return false;
} }
private Boolean isTagsSelected(final DistributionSetFilter distributionSetFilter) { private static Boolean isTagsSelected(final DistributionSetFilter distributionSetFilter) {
if (distributionSetFilter.getTagNames() != null && !distributionSetFilter.getTagNames().isEmpty()) { if (distributionSetFilter.getTagNames() != null && !distributionSetFilter.getTagNames().isEmpty()) {
return true; 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"); throw new EntityAlreadyExistsException("Metadata entry with key '" + metadataKey + "' already exists");
} }
@@ -1048,7 +1049,7 @@ public class DistributionSetManagement {
* @return list of assigned ds * @return list of assigned ds
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@NotNull @NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public List<DistributionSet> assignTag(@NotEmpty final Collection<Long> dsIds, public List<DistributionSet> assignTag(@NotEmpty final Collection<Long> dsIds,
@@ -1077,7 +1078,7 @@ public class DistributionSetManagement {
* @return list of unassigned ds * @return list of unassigned ds
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@NotNull @NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public List<DistributionSet> unAssignAllDistributionSetsByTag(@NotNull final DistributionSetTag tag) { 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 * @return the unassigned ds or <null> if no ds is unassigned
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public DistributionSet unAssignTag(@NotNull final Long dsId, @NotNull final DistributionSetTag distributionSetTag) { public DistributionSet unAssignTag(@NotNull final Long dsId, @NotNull final DistributionSetTag distributionSetTag) {
final List<DistributionSet> allDs = findDistributionSetListWithDetails(Arrays.asList(dsId)); final List<DistributionSet> allDs = findDistributionSetListWithDetails(Arrays.asList(dsId));

View File

@@ -12,6 +12,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DsMetadataCompositeKey; import org.eclipse.hawkbit.repository.model.DsMetadataCompositeKey;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; 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 public interface DistributionSetMetadataRepository
extends PagingAndSortingRepository<DistributionSetMetadata, DsMetadataCompositeKey>, extends PagingAndSortingRepository<DistributionSetMetadata, DsMetadataCompositeKey>,
JpaSpecificationExecutor<DistributionSetMetadata> { JpaSpecificationExecutor<DistributionSetMetadata> {

View File

@@ -21,6 +21,7 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; 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 public interface DistributionSetRepository
extends BaseEntityRepository<DistributionSet, Long>, JpaSpecificationExecutor<DistributionSet> { extends BaseEntityRepository<DistributionSet, Long>, JpaSpecificationExecutor<DistributionSet> {
@@ -50,7 +51,7 @@ public interface DistributionSetRepository
* to be deleted * to be deleted
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Query("update DistributionSet d set d.deleted = 1 where d.id in :ids") @Query("update DistributionSet d set d.deleted = 1 where d.id in :ids")
void deleteDistributionSet(@Param("ids") Long... ids); void deleteDistributionSet(@Param("ids") Long... ids);
@@ -62,7 +63,7 @@ public interface DistributionSetRepository
* @return number of affected/deleted records * @return number of affected/deleted records
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477 // Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
@Query("DELETE FROM DistributionSet d WHERE d.id IN ?1") @Query("DELETE FROM DistributionSet d WHERE d.id IN ?1")
int deleteByIdIn(Collection<Long> ids); int deleteByIdIn(Collection<Long> ids);
@@ -82,7 +83,7 @@ public interface DistributionSetRepository
* yet to an {@link UpdateAction}, i.e. unused. * yet to an {@link UpdateAction}, i.e. unused.
* *
* @param ids * @param ids
* to searcgh for * to search for
* @return * @return
*/ */
@Query("select ac.distributionSet.id from Action ac where ac.distributionSet.id in :ids") @Query("select ac.distributionSet.id from Action ac where ac.distributionSet.id in :ids")

View File

@@ -15,15 +15,14 @@ import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.TargetTag; import org.eclipse.hawkbit.repository.model.TargetTag;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
/** /**
* {@link TargetTag} repository. * {@link TargetTag} repository.
* *
*
*
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public interface DistributionSetTagRepository public interface DistributionSetTagRepository
extends BaseEntityRepository<DistributionSetTag, Long>, JpaSpecificationExecutor<DistributionSetTag> { extends BaseEntityRepository<DistributionSetTag, Long>, JpaSpecificationExecutor<DistributionSetTag> {
/** /**
@@ -34,7 +33,7 @@ public interface DistributionSetTagRepository
* @return 1 if tag was deleted * @return 1 if tag was deleted
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
Long deleteByName(final String tagName); Long deleteByName(final String tagName);
/** /**

View File

@@ -14,16 +14,14 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
/** /**
* {@link PagingAndSortingRepository} for {@link DistributionSetType}. * {@link PagingAndSortingRepository} for {@link DistributionSetType}.
* *
*
*
*
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public interface DistributionSetTypeRepository public interface DistributionSetTypeRepository
extends BaseEntityRepository<DistributionSetType, Long>, JpaSpecificationExecutor<DistributionSetType> { extends BaseEntityRepository<DistributionSetType, Long>, JpaSpecificationExecutor<DistributionSetType> {

View File

@@ -13,7 +13,6 @@ import java.util.List;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.persistence.Query; import javax.persistence.Query;
import javax.transaction.Transactional;
import org.eclipse.hawkbit.repository.model.TargetInfo; import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; 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.cache.annotation.CacheEvict;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.stereotype.Service; 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 * Custom repository implementation as standard spring repository fails as of
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=415027 . * https://bugs.eclipse.org/bugs/show_bug.cgi?id=415027 .
* *
*
*
*/ */
@Service @Service
@Transactional @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public class EclipseLinkTargetInfoRepository implements TargetInfoRepository { public class EclipseLinkTargetInfoRepository implements TargetInfoRepository {
@Autowired @Autowired
@@ -38,6 +37,7 @@ public class EclipseLinkTargetInfoRepository implements TargetInfoRepository {
@Override @Override
@Modifying @Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void setTargetUpdateStatus(final TargetUpdateStatus status, final List<Long> targets) { public void setTargetUpdateStatus(final TargetUpdateStatus status, final List<Long> targets) {
final Query query = entityManager.createQuery( final Query query = entityManager.createQuery(
"update TargetInfo ti set ti.updateStatus = :status where ti.targetId in :targets and ti.updateStatus != :status"); "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 @Override
@Modifying @Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true) @CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true)
public <S extends TargetInfo> S save(final S entity) { public <S extends TargetInfo> S save(final S entity) {
@@ -61,6 +62,7 @@ public class EclipseLinkTargetInfoRepository implements TargetInfoRepository {
@Override @Override
@Modifying @Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true) @CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true)
public void deleteByTargetIdIn(final Collection<Long> targetIDs) { public void deleteByTargetIdIn(final Collection<Long> targetIDs) {
final javax.persistence.Query query = entityManager final javax.persistence.Query query = entityManager

View File

@@ -9,16 +9,14 @@
package org.eclipse.hawkbit.repository; package org.eclipse.hawkbit.repository;
import org.eclipse.hawkbit.repository.model.ExternalArtifactProvider; import org.eclipse.hawkbit.repository.model.ExternalArtifactProvider;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
/** /**
* Repository for {@link ExternalArtifactProvider}. * Repository for {@link ExternalArtifactProvider}.
* *
*
*
*
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public interface ExternalArtifactProviderRepository extends BaseEntityRepository<ExternalArtifactProvider, Long> { public interface ExternalArtifactProviderRepository extends BaseEntityRepository<ExternalArtifactProvider, Long> {
} }

View File

@@ -11,15 +11,14 @@ package org.eclipse.hawkbit.repository;
import org.eclipse.hawkbit.repository.model.ExternalArtifact; import org.eclipse.hawkbit.repository.model.ExternalArtifact;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
/** /**
* {@link ExternalArtifact} repository. * {@link ExternalArtifact} repository.
* *
*
*
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public interface ExternalArtifactRepository extends BaseEntityRepository<ExternalArtifact, Long> { public interface ExternalArtifactRepository extends BaseEntityRepository<ExternalArtifact, Long> {
/** /**

View File

@@ -15,13 +15,14 @@ import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
/** /**
* {@link LocalArtifact} repository. * {@link LocalArtifact} repository.
* *
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public interface LocalArtifactRepository extends BaseEntityRepository<LocalArtifact, Long> { public interface LocalArtifactRepository extends BaseEntityRepository<LocalArtifact, Long> {
/** /**

View File

@@ -48,14 +48,15 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; 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 @Validated
@Service @Service
public class ReportManagement { public class ReportManagement {
@@ -404,7 +405,7 @@ public class ReportManagement {
return innerOuterReport; return innerOuterReport;
} }
private final class InnerOuter { private static final class InnerOuter {
final DSName name; final DSName name;
long count; long count;
final List<InnerOuter> outer; final List<InnerOuter> outer;
@@ -433,9 +434,6 @@ public class ReportManagement {
/** /**
* Object contains the name and the id of an entity. * Object contains the name and the id of an entity.
* *
*
*
*
*/ */
private static final class DSName { private static final class DSName {
@@ -510,9 +508,6 @@ public class ReportManagement {
* Return DateTypes. * Return DateTypes.
*/ */
public static final class DateTypes implements Serializable { public static final class DateTypes implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final PerMonth PER_MONTH = new PerMonth(); private static final PerMonth PER_MONTH = new PerMonth();

View File

@@ -43,6 +43,7 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -52,7 +53,7 @@ import org.springframework.validation.annotation.Validated;
*/ */
@Validated @Validated
@Service @Service
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public class RolloutGroupManagement { public class RolloutGroupManagement {
@Autowired @Autowired
@@ -189,7 +190,7 @@ public class RolloutGroupManagement {
final ListJoin<Target, RolloutTargetGroup> rolloutTargetJoin = root.join(Target_.rolloutTargetGroup); final ListJoin<Target, RolloutTargetGroup> rolloutTargetJoin = root.join(Target_.rolloutTargetGroup);
return criteriaBuilder.and(specification.toPredicate(root, query, criteriaBuilder), return criteriaBuilder.and(specification.toPredicate(root, query, criteriaBuilder),
criteriaBuilder.equal(rolloutTargetJoin.get(RolloutTargetGroup_.rolloutGroup), rolloutGroup)); criteriaBuilder.equal(rolloutTargetJoin.get(RolloutTargetGroup_.rolloutGroup), rolloutGroup));
} , page); }, page);
} }
/** /**
@@ -213,7 +214,7 @@ public class RolloutGroupManagement {
return targetRepository.findByActionsRolloutGroup(rolloutGroup, page); 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()); return rolloutGroup != null && RolloutStatus.READY.equals(rolloutGroup.getRollout().getStatus());
} }
@@ -259,5 +260,4 @@ public class RolloutGroupManagement {
.collect(Collectors.toList()); .collect(Collectors.toList());
return new PageImpl<>(targetWithActionStatus, pageRequest, totalCount); return new PageImpl<>(targetWithActionStatus, pageRequest, totalCount);
} }
}
}

View File

@@ -18,12 +18,13 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
/** /**
* The repository interface for the {@link RolloutGroup} model. * The repository interface for the {@link RolloutGroup} model.
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public interface RolloutGroupRepository public interface RolloutGroupRepository
extends BaseEntityRepository<RolloutGroup, Long>, JpaSpecificationExecutor<RolloutGroup> { extends BaseEntityRepository<RolloutGroup, Long>, JpaSpecificationExecutor<RolloutGroup> {

View File

@@ -74,7 +74,7 @@ import org.springframework.validation.annotation.Validated;
@Validated @Validated
@Service @Service
@EnableScheduling @EnableScheduling
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public class RolloutManagement { public class RolloutManagement {
private static final Logger LOGGER = LoggerFactory.getLogger(RolloutManagement.class); private static final Logger LOGGER = LoggerFactory.getLogger(RolloutManagement.class);
@@ -136,7 +136,7 @@ public class RolloutManagement {
/** /**
* Retrieves all rollouts. * Retrieves all rollouts.
* *
* @param page * @param page
* the page request to sort and limit the result * the page request to sort and limit the result
* @return a page of found rollouts * @return a page of found rollouts
@@ -148,7 +148,7 @@ public class RolloutManagement {
/** /**
* Retrieves all rollouts found by the given specification. * Retrieves all rollouts found by the given specification.
* *
* @param specification * @param specification
* the specification to filter rollouts * the specification to filter rollouts
* @param page * @param page
@@ -165,7 +165,7 @@ public class RolloutManagement {
/** /**
* Retrieves a specific rollout by its ID. * Retrieves a specific rollout by its ID.
* *
* @param rolloutId * @param rolloutId
* the ID of the rollout to retrieve * the ID of the rollout to retrieve
* @return the founded rollout or {@code null} if rollout with given ID does * @return the founded rollout or {@code null} if rollout with given ID does
@@ -182,13 +182,13 @@ public class RolloutManagement {
* which are effected by this rollout to create. The targets will then be * 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 * split up into groups. The size of the groups can be defined in the
* {@code groupSize} parameter. * {@code groupSize} parameter.
* *
* The rollout is not started. Only the preparation of the rollout is done, * The rollout is not started. Only the preparation of the rollout is done,
* persisting and creating all the necessary groups. The Rollout and the * persisting and creating all the necessary groups. The Rollout and the
* groups are persisted in {@link RolloutStatus#READY} and * groups are persisted in {@link RolloutStatus#READY} and
* {@link RolloutGroupStatus#READY} so they can be started * {@link RolloutGroupStatus#READY} so they can be started
* {@link #startRollout(Rollout)}. * {@link #startRollout(Rollout)}.
* *
* @param rollout * @param rollout
* the rollout entity to create * the rollout entity to create
* @param amountGroup * @param amountGroup
@@ -197,11 +197,11 @@ public class RolloutManagement {
* the rolloutgroup conditions and actions which should be * the rolloutgroup conditions and actions which should be
* applied for each {@link RolloutGroup} * applied for each {@link RolloutGroup}
* @return the persisted rollout. * @return the persisted rollout.
* *
* @throws IllegalArgumentException * @throws IllegalArgumentException
* in case the given groupSize is zero or lower. * in case the given groupSize is zero or lower.
*/ */
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
public Rollout createRollout(final Rollout rollout, final int amountGroup, public Rollout createRollout(final Rollout rollout, final int amountGroup,
@@ -217,22 +217,22 @@ public class RolloutManagement {
* will be done synchronously and will be returned. The targets will then be * 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 * split up into groups. The size of the groups can be defined in the
* {@code groupSize} parameter. * {@code groupSize} parameter.
* *
* The creation of the rollout groups is executed asynchronously due it * 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 * might take some time to split up the targets into groups. The creation of
* the {@link RolloutGroup} is published as event * the {@link RolloutGroup} is published as event
* {@link RolloutGroupCreatedEvent}. * {@link RolloutGroupCreatedEvent}.
* *
* The rollout is in status {@link RolloutStatus#CREATING} until all rollout * The rollout is in status {@link RolloutStatus#CREATING} until all rollout
* groups has been created and the targets are split up, then the rollout * groups has been created and the targets are split up, then the rollout
* will change the status to {@link RolloutStatus#READY}. * will change the status to {@link RolloutStatus#READY}.
* *
* The rollout is not started. Only the preparation of the rollout is done, * The rollout is not started. Only the preparation of the rollout is done,
* persisting and creating all the necessary groups. The Rollout and the * persisting and creating all the necessary groups. The Rollout and the
* groups are persisted in {@link RolloutStatus#READY} and * groups are persisted in {@link RolloutStatus#READY} and
* {@link RolloutGroupStatus#READY} so they can be started * {@link RolloutGroupStatus#READY} so they can be started
* {@link #startRollout(Rollout)}. * {@link #startRollout(Rollout)}.
* *
* @param rollout * @param rollout
* the rollout to be created * the rollout to be created
* @param amountGroup * @param amountGroup
@@ -244,7 +244,7 @@ public class RolloutManagement {
* @return the created rollout entity in state * @return the created rollout entity in state
* {@link RolloutStatus#CREATING} * {@link RolloutStatus#CREATING}
*/ */
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
public Rollout createRolloutAsync(final Rollout rollout, final int amountGroup, public Rollout createRolloutAsync(final Rollout rollout, final int amountGroup,
@@ -276,7 +276,7 @@ public class RolloutManagement {
return rolloutRepository.save(rollout); return rolloutRepository.save(rollout);
} }
private void verifyRolloutGroupParameter(final int amountGroup) { private static void verifyRolloutGroupParameter(final int amountGroup) {
if (amountGroup <= 0) { if (amountGroup <= 0) {
throw new IllegalArgumentException("the amountGroup must be greater than zero"); throw new IllegalArgumentException("the amountGroup must be greater than zero");
} else if (amountGroup > 500) { } else if (amountGroup > 500) {
@@ -284,8 +284,8 @@ public class RolloutManagement {
} }
} }
private Rollout createRolloutGroupsInNewTransaction(final int amountOfGroups, final RolloutGroupConditions conditions, private Rollout createRolloutGroupsInNewTransaction(final int amountOfGroups,
final Rollout savedRollout) { final RolloutGroupConditions conditions, final Rollout savedRollout) {
final DefaultTransactionDefinition def = new DefaultTransactionDefinition(); final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("creatingRollout"); def.setName("creatingRollout");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
@@ -298,7 +298,7 @@ public class RolloutManagement {
* sizes are calculated by dividing the total count of targets through the * 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 * amount of given groups. In same cases this will lead to less rollout
* groups than given by client. * groups than given by client.
* *
* @param amountOfGroups * @param amountOfGroups
* the amount of groups * the amount of groups
* @param conditions * @param conditions
@@ -361,17 +361,19 @@ public class RolloutManagement {
* for each affected target in the rollout. The actions of the first group * for each affected target in the rollout. The actions of the first group
* will be started immediately {@link RolloutGroupStatus#RUNNING} as the * will be started immediately {@link RolloutGroupStatus#RUNNING} as the
* other groups will be {@link RolloutGroupStatus#SCHEDULED} state. * other groups will be {@link RolloutGroupStatus#SCHEDULED} state.
* *
* The rollout itself will be then also in {@link RolloutStatus#RUNNING}. * The rollout itself will be then also in {@link RolloutStatus#RUNNING}.
* *
* @param rollout * @param rollout
* the rollout to be started * the rollout to be started
* *
* @return started rollout
*
* @throws RolloutIllegalStateException * @throws RolloutIllegalStateException
* if given rollout is not in {@link RolloutStatus#READY}. Only * if given rollout is not in {@link RolloutStatus#READY}. Only
* ready rollouts can be started. * ready rollouts can be started.
*/ */
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE) + SpringEvalExpressions.IS_SYSTEM_CODE)
@@ -388,12 +390,14 @@ public class RolloutManagement {
* actions of the first group will be started immediately * actions of the first group will be started immediately
* {@link RolloutGroupStatus#RUNNING} as the other groups will be * {@link RolloutGroupStatus#RUNNING} as the other groups will be
* {@link RolloutGroupStatus#SCHEDULED} state. * {@link RolloutGroupStatus#SCHEDULED} state.
* *
* The rollout itself will be then also in {@link RolloutStatus#RUNNING}. * The rollout itself will be then also in {@link RolloutStatus#RUNNING}.
* *
* @param rollout * @param rollout
* the rollout to be started * the rollout to be started
* *
* @return the started rollout
*
* @throws RolloutIllegalStateException * @throws RolloutIllegalStateException
* if given rollout is not in {@link RolloutStatus#READY}. Only * if given rollout is not in {@link RolloutStatus#READY}. Only
* ready rollouts can be started. * ready rollouts can be started.
@@ -461,19 +465,19 @@ public class RolloutManagement {
* {@link RolloutGroupStatus#SCHEDULED} will not be started and keep in * {@link RolloutGroupStatus#SCHEDULED} will not be started and keep in
* {@link RolloutGroupStatus#SCHEDULED} state until the rollout is * {@link RolloutGroupStatus#SCHEDULED} state until the rollout is
* {@link RolloutManagement#resumeRollout(Rollout)}. * {@link RolloutManagement#resumeRollout(Rollout)}.
* *
* Switching the rollout status to {@link RolloutStatus#PAUSED} is * Switching the rollout status to {@link RolloutStatus#PAUSED} is
* sufficient due the {@link #checkRunningRollouts(long)} will not check * sufficient due the {@link #checkRunningRollouts(long)} will not check
* this rollout anymore. * this rollout anymore.
* *
* @param rollout * @param rollout
* the rollout to be paused. * the rollout to be paused.
* *
* @throws RolloutIllegalStateException * @throws RolloutIllegalStateException
* if given rollout is not in {@link RolloutStatus#RUNNING}. * if given rollout is not in {@link RolloutStatus#RUNNING}.
* Only running rollouts can be paused. * Only running rollouts can be paused.
*/ */
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE) + SpringEvalExpressions.IS_SYSTEM_CODE)
@@ -496,14 +500,14 @@ public class RolloutManagement {
* Resumes a paused rollout. The rollout switches back to * Resumes a paused rollout. The rollout switches back to
* {@link RolloutStatus#RUNNING} state which is then picked up again by the * {@link RolloutStatus#RUNNING} state which is then picked up again by the
* {@link #checkRunningRollouts(long)}. * {@link #checkRunningRollouts(long)}.
* *
* @param rollout * @param rollout
* the rollout to be resumed * the rollout to be resumed
* @throws RolloutIllegalStateException * @throws RolloutIllegalStateException
* if given rollout is not in {@link RolloutStatus#PAUSED}. Only * if given rollout is not in {@link RolloutStatus#PAUSED}. Only
* paused rollouts can be resumed. * paused rollouts can be resumed.
*/ */
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_SYSTEM_CODE) + SpringEvalExpressions.IS_SYSTEM_CODE)
@@ -521,7 +525,7 @@ public class RolloutManagement {
* Checking running rollouts. Rollouts which are checked updating the * Checking running rollouts. Rollouts which are checked updating the
* {@link Rollout#setLastCheck(long)} to indicate that the current instance * {@link Rollout#setLastCheck(long)} to indicate that the current instance
* is handling the specific rollout. This code should run as system-code. * is handling the specific rollout. This code should run as system-code.
* *
* <pre> * <pre>
* {@code * {@code
* SystemSecurityContext.runAsSystem(new Callable<Void>() { * SystemSecurityContext.runAsSystem(new Callable<Void>() {
@@ -531,15 +535,15 @@ public class RolloutManagement {
* }); * });
* } * }
* </pre> * </pre>
* *
* This method is attend to be called by a scheduler. * This method is attend to be called by a scheduler.
* {@link RolloutScheduler}. And must be running in an transaction so it's * {@link RolloutScheduler}. And must be running in an transaction so it's
* splitted from the scheduler. * splitted from the scheduler.
* *
* Rollouts which are currently running are investigated, by means the * Rollouts which are currently running are investigated, by means the
* error- and finish condition of running groups in this rollout are * error- and finish condition of running groups in this rollout are
* evaluated. * evaluated.
* *
* @param delayBetweenChecks * @param delayBetweenChecks
* the time in milliseconds of the delay between the further and * the time in milliseconds of the delay between the further and
* this check. This check is only applied if the last check is * this check. This check is only applied if the last check is
@@ -728,7 +732,7 @@ public class RolloutManagement {
/** /**
* Count rollouts by specified filter text. * Count rollouts by specified filter text.
* *
* @param searchText * @param searchText
* name or description * name or description
* @return total count rollouts for specified filter text. * @return total count rollouts for specified filter text.
@@ -750,7 +754,7 @@ public class RolloutManagement {
/** /**
* * Retrieves a specific rollout by its ID. * * Retrieves a specific rollout by its ID.
* *
* @param pageable * @param pageable
* the page request to sort and limit the result * the page request to sort and limit the result
* @param searchText * @param searchText
@@ -768,7 +772,7 @@ public class RolloutManagement {
/** /**
* Retrieves a specific rollout by its name. * Retrieves a specific rollout by its name.
* *
* @param rolloutName * @param rolloutName
* the name of the rollout to retrieve * the name of the rollout to retrieve
* @return the founded rollout or {@code null} if rollout with given name * @return the founded rollout or {@code null} if rollout with given name
@@ -781,14 +785,14 @@ public class RolloutManagement {
/** /**
* Update rollout details. * Update rollout details.
* *
* @param rollout * @param rollout
* rollout to be updated * rollout to be updated
* *
* @return Rollout updated rollout * @return Rollout updated rollout
*/ */
@NotNull @NotNull
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
public Rollout updateRollout(@NotNull final Rollout rollout) { public Rollout updateRollout(@NotNull final Rollout rollout) {
@@ -798,7 +802,7 @@ public class RolloutManagement {
/** /**
* Get count of targets in different status in rollout. * Get count of targets in different status in rollout.
* *
* @param page * @param page
* the page request to sort and limit the result * the page request to sort and limit the result
* @return a list of rollouts with details of targets count for different * @return a list of rollouts with details of targets count for different
@@ -850,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()))) { if (!(RolloutStatus.READY.equals(mergedRollout.getStatus()))) {
throw new RolloutIllegalStateException("Rollout can only be started in state ready but current state is " throw new RolloutIllegalStateException("Rollout can only be started in state ready but current state is "
+ rollout.getStatus().name().toLowerCase()); + rollout.getStatus().name().toLowerCase());
@@ -860,7 +864,7 @@ public class RolloutManagement {
/*** /***
* Get finished percentage details for a specified group which is in running * Get finished percentage details for a specified group which is in running
* state. * state.
* *
* @param rolloutId * @param rolloutId
* the ID of the {@link Rollout} * the ID of the {@link Rollout}
* @param rolloutGroup * @param rolloutGroup

View File

@@ -18,12 +18,13 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
/** /**
* The repository interface for the {@link Rollout} model. * 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> { 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 * @return the count of the updated rows. Zero if no row has been updated
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Query("UPDATE Rollout r SET r.lastCheck = :lastCheck WHERE r.lastCheck < (:lastCheck - :delay) AND r.status=:status") @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, int updateLastCheck(@Param("lastCheck") final long lastCheck, @Param("delay") final long delay,
@Param("status") final RolloutStatus status); @Param("status") final RolloutStatus status);

View File

@@ -12,11 +12,14 @@ import org.eclipse.hawkbit.repository.model.RolloutTargetGroup;
import org.eclipse.hawkbit.repository.model.RolloutTargetGroupId; import org.eclipse.hawkbit.repository.model.RolloutTargetGroupId;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.CrudRepository; 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 public interface RolloutTargetGroupRepository
extends CrudRepository<RolloutTargetGroup, RolloutTargetGroupId>, JpaSpecificationExecutor<RolloutTargetGroup> { extends CrudRepository<RolloutTargetGroup, RolloutTargetGroupId>, JpaSpecificationExecutor<RolloutTargetGroup> {
} }

View File

@@ -52,6 +52,7 @@ import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -62,7 +63,7 @@ import com.google.common.collect.Sets;
* Business facade for managing {@link SoftwareModule}s. * Business facade for managing {@link SoftwareModule}s.
* *
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated @Validated
@Service @Service
public class SoftwareManagement { public class SoftwareManagement {
@@ -108,7 +109,7 @@ public class SoftwareManagement {
* of {@link SoftwareModule#getId()} is <code>null</code> * of {@link SoftwareModule#getId()} is <code>null</code>
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public SoftwareModule updateSoftwareModule(@NotNull final SoftwareModule sm) { public SoftwareModule updateSoftwareModule(@NotNull final SoftwareModule sm) {
checkNotNull(sm.getId()); checkNotNull(sm.getId());
@@ -138,7 +139,7 @@ public class SoftwareManagement {
* @return updated {@link Entity} * @return updated {@link Entity}
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public SoftwareModuleType updateSoftwareModuleType(@NotNull final SoftwareModuleType sm) { public SoftwareModuleType updateSoftwareModuleType(@NotNull final SoftwareModuleType sm) {
checkNotNull(sm.getId()); checkNotNull(sm.getId());
@@ -283,7 +284,7 @@ public class SoftwareManagement {
* is the {@link SoftwareModule} to be deleted * is the {@link SoftwareModule} to be deleted
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
public void deleteSoftwareModule(@NotNull final SoftwareModule bsm) { 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. * Deletes {@link SoftwareModule}s which is any if the given ids.
* *
* @param ids * @param ids
* of the Software Moduels to be deleted * of the Software Modules to be deleted
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
public void deleteSoftwareModules(@NotNull final Iterable<Long> ids) { public void deleteSoftwareModules(@NotNull final Iterable<Long> ids) {
final List<SoftwareModule> swModulesToDelete = softwareModuleRepository.findByIdIn(ids); final List<SoftwareModule> swModulesToDelete = softwareModuleRepository.findByIdIn(ids);
@@ -579,7 +580,7 @@ public class SoftwareManagement {
return new SliceImpl<>(resultList); 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 SoftwareModuleType type) {
final List<Specification<SoftwareModule>> specList = new ArrayList<>(); final List<Specification<SoftwareModule>> specList = new ArrayList<>();
if (!Strings.isNullOrEmpty(searchText)) { if (!Strings.isNullOrEmpty(searchText)) {
@@ -700,7 +701,7 @@ public class SoftwareManagement {
* @return created {@link Entity} * @return created {@link Entity}
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public SoftwareModuleType createSoftwareModuleType(@NotNull final SoftwareModuleType type) { public SoftwareModuleType createSoftwareModuleType(@NotNull final SoftwareModuleType type) {
if (type.getId() != null) { if (type.getId() != null) {
@@ -718,7 +719,7 @@ public class SoftwareManagement {
* @return created {@link Entity} * @return created {@link Entity}
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public List<SoftwareModuleType> createSoftwareModuleType(@NotNull final Collection<SoftwareModuleType> types) { public List<SoftwareModuleType> createSoftwareModuleType(@NotNull final Collection<SoftwareModuleType> types) {
return types.stream().map(this::createSoftwareModuleType).collect(Collectors.toList()); return types.stream().map(this::createSoftwareModuleType).collect(Collectors.toList());
@@ -731,7 +732,7 @@ public class SoftwareManagement {
* to delete * to delete
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
public void deleteSoftwareModuleType(@NotNull final SoftwareModuleType type) { 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 * in case the meta data entry already exists for the specific
* key * key
*/ */
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public SoftwareModuleMetadata createSoftwareModuleMetadata(@NotNull final SoftwareModuleMetadata metadata) { 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 * in case one of the meta data entry already exists for the
* specific key * specific key
*/ */
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public List<SoftwareModuleMetadata> createSoftwareModuleMetadata( public List<SoftwareModuleMetadata> createSoftwareModuleMetadata(
@@ -832,7 +833,7 @@ public class SoftwareManagement {
* in case the meta data entry does not exists and cannot be * in case the meta data entry does not exists and cannot be
* updated * updated
*/ */
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public SoftwareModuleMetadata updateSoftwareModuleMetadata(@NotNull final SoftwareModuleMetadata metadata) { public SoftwareModuleMetadata updateSoftwareModuleMetadata(@NotNull final SoftwareModuleMetadata metadata) {
@@ -851,7 +852,7 @@ public class SoftwareManagement {
* @param id * @param id
* the ID of the software module meta data to delete * the ID of the software module meta data to delete
*/ */
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public void deleteSoftwareModuleMetadata(@NotNull final SwMetadataCompositeKey id) { 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"); throw new EntityAlreadyExistsException("Metadata entry with key '" + metadataKey + "' already exists");
} }

View File

@@ -16,15 +16,14 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
/** /**
* {@link SoftwareModuleMetadata} repository. * {@link SoftwareModuleMetadata} repository.
* *
*
*
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public interface SoftwareModuleMetadataRepository public interface SoftwareModuleMetadataRepository
extends PagingAndSortingRepository<SoftwareModuleMetadata, SwMetadataCompositeKey>, extends PagingAndSortingRepository<SoftwareModuleMetadata, SwMetadataCompositeKey>,
JpaSpecificationExecutor<SoftwareModuleMetadata> { JpaSpecificationExecutor<SoftwareModuleMetadata> {

View File

@@ -21,15 +21,14 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
/** /**
* {@link SoftwareModule} repository. * {@link SoftwareModule} repository.
* *
*
*
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public interface SoftwareModuleRepository public interface SoftwareModuleRepository
extends BaseEntityRepository<SoftwareModule, Long>, JpaSpecificationExecutor<SoftwareModule> { extends BaseEntityRepository<SoftwareModule, Long>, JpaSpecificationExecutor<SoftwareModule> {
@@ -69,7 +68,7 @@ public interface SoftwareModuleRepository
* *
*/ */
@Modifying @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") @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, void deleteSoftwareModule(@Param("lastModifiedAt") Long modifiedAt, @Param("lastModifiedBy") String modifiedBy,
@Param("ids") final Long... ids); @Param("ids") final Long... ids);

View File

@@ -12,16 +12,14 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
/** /**
* Repository for {@link SoftwareModuleType}. * Repository for {@link SoftwareModuleType}.
* *
*
*
*
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public interface SoftwareModuleTypeRepository public interface SoftwareModuleTypeRepository
extends BaseEntityRepository<SoftwareModuleType, Long>, JpaSpecificationExecutor<SoftwareModuleType> { extends BaseEntityRepository<SoftwareModuleType, Long>, JpaSpecificationExecutor<SoftwareModuleType> {

View File

@@ -35,6 +35,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -43,7 +44,7 @@ import org.springframework.validation.annotation.Validated;
* Central system management operations of the SP server. * Central system management operations of the SP server.
* *
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated @Validated
@Service @Service
public class SystemManagement { public class SystemManagement {
@@ -180,7 +181,7 @@ public class SystemManagement {
* @return * @return
*/ */
@Cacheable(value = "tenantMetadata", key = "#tenant.toUpperCase()") @Cacheable(value = "tenantMetadata", key = "#tenant.toUpperCase()")
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
@NotNull @NotNull
public TenantMetaData getTenantMetadata(@NotNull final String tenant) { public TenantMetaData getTenantMetadata(@NotNull final String tenant) {
@@ -218,7 +219,7 @@ public class SystemManagement {
* to delete * to delete
*/ */
@CacheEvict(value = { "tenantMetadata" }, key = "#tenant.toUpperCase()") @CacheEvict(value = { "tenantMetadata" }, key = "#tenant.toUpperCase()")
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN)
public void deleteTenant(@NotNull final String tenant) { public void deleteTenant(@NotNull final String tenant) {
@@ -249,7 +250,7 @@ public class SystemManagement {
* @return {@link TenantMetaData} of {@link TenantAware#getCurrentTenant()} * @return {@link TenantMetaData} of {@link TenantAware#getCurrentTenant()}
*/ */
@Cacheable(value = "tenantMetadata", keyGenerator = "tenantKeyGenerator") @Cacheable(value = "tenantMetadata", keyGenerator = "tenantKeyGenerator")
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
@NotNull @NotNull
public TenantMetaData getTenantMetadata() { public TenantMetaData getTenantMetadata() {
@@ -278,7 +279,7 @@ public class SystemManagement {
// suspend the transaction here to do a read-request against the medata // suspend the transaction here to do a read-request against the medata
// table, when the current // table, when the current
// tenant is not cached anyway already. // tenant is not cached anyway already.
@Transactional(propagation = Propagation.NOT_SUPPORTED) @Transactional(propagation = Propagation.NOT_SUPPORTED, isolation = Isolation.READ_UNCOMMITTED)
public String currentTenant() { public String currentTenant() {
final String initialTenantCreation = createInitialTenant.get(); final String initialTenantCreation = createInitialTenant.get();
if (initialTenantCreation == null) { if (initialTenantCreation == null) {
@@ -297,7 +298,7 @@ public class SystemManagement {
* @return updated {@link TenantMetaData} entity * @return updated {@link TenantMetaData} entity
*/ */
@CachePut(value = "tenantMetadata", key = "#metaData.tenant.toUpperCase()") @CachePut(value = "tenantMetadata", key = "#metaData.tenant.toUpperCase()")
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
@NotNull @NotNull
public TenantMetaData updateTenantMetadata(@NotNull final TenantMetaData metaData) { public TenantMetaData updateTenantMetadata(@NotNull final TenantMetaData metaData) {
@@ -341,6 +342,8 @@ public class SystemManagement {
*/ */
private class CurrentTenantKeyGenerator implements KeyGenerator { private class CurrentTenantKeyGenerator implements KeyGenerator {
@Override @Override
// Exception squid:S923 - override
@SuppressWarnings({ "squid:S923" })
public Object generate(final Object target, final Method method, final Object... params) { public Object generate(final Object target, final Method method, final Object... params) {
final String initialTenantCreation = createInitialTenant.get(); final String initialTenantCreation = createInitialTenant.get();
if (initialTenantCreation == null) { if (initialTenantCreation == null) {

View File

@@ -38,21 +38,17 @@ import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import com.google.common.eventbus.EventBus; import com.google.common.eventbus.EventBus;
/** /**
* * Management service class for {@link Tag}s.
* Mangement service class for {@link Tag}s.
*
*
*
*
* *
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated @Validated
@Service @Service
public class TagManagement { public class TagManagement {
@@ -102,7 +98,7 @@ public class TagManagement {
* if given object already exists * if given object already exists
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@NotNull @NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
public TargetTag createTargetTag(@NotNull final TargetTag targetTag) { public TargetTag createTargetTag(@NotNull final TargetTag targetTag) {
@@ -133,7 +129,7 @@ public class TagManagement {
* if given object has already an ID. * if given object has already an ID.
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@NotNull @NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
public List<TargetTag> createTargetTags(@NotNull final Iterable<TargetTag> targetTags) { 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 * tag name of the {@link TargetTag} to be deleted
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_TARGET)
public void deleteTargetTag(@NotEmpty final String targetTagName) { public void deleteTargetTag(@NotEmpty final String targetTagName) {
final TargetTag tag = targetTagRepository.findByNameEquals(targetTagName); final TargetTag tag = targetTagRepository.findByNameEquals(targetTagName);
@@ -220,7 +216,7 @@ public class TagManagement {
* @return the new {@link TargetTag} * @return the new {@link TargetTag}
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@NotNull @NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public TargetTag updateTargetTag(@NotNull final TargetTag targetTag) { public TargetTag updateTargetTag(@NotNull final TargetTag targetTag) {
@@ -254,7 +250,7 @@ public class TagManagement {
* *
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public DistributionSetTag createDistributionSetTag(@NotNull final DistributionSetTag distributionSetTag) { public DistributionSetTag createDistributionSetTag(@NotNull final DistributionSetTag distributionSetTag) {
if (null != distributionSetTag.getId()) { if (null != distributionSetTag.getId()) {
@@ -282,7 +278,7 @@ public class TagManagement {
* if a given entity already exists * if a given entity already exists
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
public List<DistributionSetTag> createDistributionSetTags( public List<DistributionSetTag> createDistributionSetTags(
@NotNull final Iterable<DistributionSetTag> distributionSetTags) { @NotNull final Iterable<DistributionSetTag> distributionSetTags) {
@@ -306,7 +302,7 @@ public class TagManagement {
* to be deleted * to be deleted
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
public void deleteDistributionSetTag(@NotEmpty final String tagName) { public void deleteDistributionSetTag(@NotEmpty final String tagName) {
final DistributionSetTag tag = distributionSetTagRepository.findByNameEquals(tagName); final DistributionSetTag tag = distributionSetTagRepository.findByNameEquals(tagName);
@@ -335,7 +331,7 @@ public class TagManagement {
* of {@link DistributionSetTag#getName()} is <code>null</code> * of {@link DistributionSetTag#getName()} is <code>null</code>
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@NotNull @NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
public DistributionSetTag updateDistributionSetTag(@NotNull final DistributionSetTag distributionSetTag) { public DistributionSetTag updateDistributionSetTag(@NotNull final DistributionSetTag distributionSetTag) {

View File

@@ -26,6 +26,7 @@ import org.springframework.data.jpa.domain.Specifications;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -35,10 +36,8 @@ import com.google.common.base.Strings;
/** /**
* Business service facade for managing {@link TargetFilterQuery}s. * Business service facade for managing {@link TargetFilterQuery}s.
* *
*
*
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated @Validated
@Service @Service
public class TargetFilterQueryManagement { public class TargetFilterQueryManagement {
@@ -53,7 +52,7 @@ public class TargetFilterQueryManagement {
* @return the created {@link TargetFilterQuery} * @return the created {@link TargetFilterQuery}
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@NotNull @NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
public TargetFilterQuery createTargetFilterQuery(@NotNull final TargetFilterQuery customTargetFilter) { public TargetFilterQuery createTargetFilterQuery(@NotNull final TargetFilterQuery customTargetFilter) {
@@ -71,7 +70,7 @@ public class TargetFilterQueryManagement {
* IDs of target filter query to be deleted * IDs of target filter query to be deleted
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_TARGET)
public void deleteTargetFilterQuery(@NotNull final Long targetFilterQueryId) { public void deleteTargetFilterQuery(@NotNull final Long targetFilterQueryId) {
targetFilterQueryRepository.delete(targetFilterQueryId); targetFilterQueryRepository.delete(targetFilterQueryId);
@@ -161,7 +160,7 @@ public class TargetFilterQueryManagement {
* @return the updated {@link TargetFilterQuery} * @return the updated {@link TargetFilterQuery}
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@NotNull @NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public TargetFilterQuery updateTargetFilterQuery(@NotNull final TargetFilterQuery targetFilterQuery) { public TargetFilterQuery updateTargetFilterQuery(@NotNull final TargetFilterQuery targetFilterQuery) {

View File

@@ -12,13 +12,14 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; 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 public interface TargetFilterQueryRepository
extends BaseEntityRepository<TargetFilterQuery, Long>, JpaSpecificationExecutor<TargetFilterQuery> { extends BaseEntityRepository<TargetFilterQuery, Long>, JpaSpecificationExecutor<TargetFilterQuery> {

View File

@@ -12,7 +12,6 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.transaction.Transactional;
import org.eclipse.hawkbit.repository.model.TargetInfo; import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; 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.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; 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. * Usually a JPA spring data repository to handle {@link TargetInfo} entity.
* However, do to an eclipselink bug with spring boot now a regular interface * However, do to an eclipselink bug with spring boot now a regular interface
* that is implemented by {@link EclipseLinkTargetInfoRepository}. * that is implemented by {@link EclipseLinkTargetInfoRepository}.
* *
*
*
*/ */
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public interface TargetInfoRepository { public interface TargetInfoRepository {
/** /**
@@ -41,7 +41,7 @@ public interface TargetInfoRepository {
* to set it for * to set it for
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Query("update TargetInfo ti set ti.updateStatus = :status where ti.targetId in :targets and ti.updateStatus != :status") @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); void setTargetUpdateStatus(@Param("status") TargetUpdateStatus status, @Param("targets") List<Long> targets);
@@ -63,7 +63,7 @@ public interface TargetInfoRepository {
* to delete * to delete
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true) @CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true)
void deleteByTargetIdIn(final Collection<Long> targetIDs); void deleteByTargetIdIn(final Collection<Long> targetIDs);
} }

View File

@@ -62,6 +62,7 @@ import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -74,7 +75,7 @@ import com.google.common.eventbus.EventBus;
* Business service facade for managing {@link Target}s. * Business service facade for managing {@link Target}s.
* *
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated @Validated
@Service @Service
public class TargetManagement { public class TargetManagement {
@@ -260,7 +261,7 @@ public class TargetManagement {
* @return the updated {@link Target} * @return the updated {@link Target}
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@NotNull @NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET + SpringEvalExpressions.HAS_AUTH_OR @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER) + SpringEvalExpressions.IS_CONTROLLER)
@@ -278,7 +279,7 @@ public class TargetManagement {
* @return the updated {@link Target}s * @return the updated {@link Target}s
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@NotNull @NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET + SpringEvalExpressions.HAS_AUTH_OR @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER) + SpringEvalExpressions.IS_CONTROLLER)
@@ -294,7 +295,7 @@ public class TargetManagement {
* the technical IDs of the targets to be deleted * the technical IDs of the targets to be deleted
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DELETE_TARGET)
public void deleteTargets(@NotEmpty final Long... targetIDs) { public void deleteTargets(@NotEmpty final Long... targetIDs) {
// we need to select the target IDs first to check the if the targetIDs // we need to select the target IDs first to check the if the targetIDs
@@ -527,11 +528,11 @@ public class TargetManagement {
* @param targets * @param targets
* to toggle for * to toggle for
* @param tag * @param tag
* to toogle * to toggle
* @return TagAssigmentResult with all metadata of the assigment outcome. * @return TagAssigmentResult with all metadata of the assignment outcome.
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@NotNull @NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public TargetTagAssignmentResult toggleTagAssignment(@NotEmpty final List<Target> targets, public TargetTagAssignmentResult toggleTagAssignment(@NotEmpty final List<Target> targets,
@@ -553,7 +554,7 @@ public class TargetManagement {
* @return TagAssigmentResult with all metadata of the assigment outcome. * @return TagAssigmentResult with all metadata of the assigment outcome.
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@NotNull @NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public TargetTagAssignmentResult toggleTagAssignment(@NotEmpty final Collection<String> targetIds, public TargetTagAssignmentResult toggleTagAssignment(@NotEmpty final Collection<String> targetIds,
@@ -596,7 +597,7 @@ public class TargetManagement {
* @return list of assigned targets * @return list of assigned targets
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@NotNull @NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public List<Target> assignTag(@NotEmpty final Collection<String> targetIds, @NotNull final TargetTag tag) { 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 * @return list of unassigned targets
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@NotNull @NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public List<Target> unAssignAllTargetsByTag(@NotNull final TargetTag tag) { 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 * @return the unassigned target or <null> if no target is unassigned
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
public Target unAssignTag(@NotNull final String controllerID, @NotNull final TargetTag targetTag) { public Target unAssignTag(@NotNull final String controllerID, @NotNull final TargetTag targetTag) {
final List<Target> allTargets = targetRepository final List<Target> allTargets = targetRepository
@@ -934,7 +935,7 @@ public class TargetManagement {
* @return * @return
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@NotNull @NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET + SpringEvalExpressions.HAS_AUTH_OR @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER) + SpringEvalExpressions.IS_CONTROLLER)
@@ -972,7 +973,7 @@ public class TargetManagement {
* *
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@NotNull @NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET + SpringEvalExpressions.HAS_AUTH_OR @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.IS_CONTROLLER) + SpringEvalExpressions.IS_CONTROLLER)
@@ -996,7 +997,7 @@ public class TargetManagement {
* already exist. * already exist.
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@NotNull @NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
public List<Target> createTargets(@NotNull final List<Target> targets) { public List<Target> createTargets(@NotNull final List<Target> targets) {
@@ -1028,7 +1029,7 @@ public class TargetManagement {
* @return newly created target * @return newly created target
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@NotNull @NotNull
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
public List<Target> createTargets(@NotNull final Collection<Target> targets, public List<Target> createTargets(@NotNull final Collection<Target> targets,

View File

@@ -27,15 +27,14 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
/** /**
* {@link Target} repository. * {@link Target} repository.
* *
*
*
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public interface TargetRepository extends BaseEntityRepository<Target, Long>, JpaSpecificationExecutor<Target> { public interface TargetRepository extends BaseEntityRepository<Target, Long>, JpaSpecificationExecutor<Target> {
/** /**
@@ -64,7 +63,7 @@ public interface TargetRepository extends BaseEntityRepository<Target, Long>, Jp
* to be deleted * to be deleted
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477 // Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
@Query("DELETE FROM Target t WHERE t.id IN ?1") @Query("DELETE FROM Target t WHERE t.id IN ?1")
void deleteByIdIn(final Collection<Long> targetIDs); void deleteByIdIn(final Collection<Long> targetIDs);
@@ -153,7 +152,7 @@ public interface TargetRepository extends BaseEntityRepository<Target, Long>, Jp
*/ */
@Override @Override
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true) @CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true)
<S extends Target> List<S> save(Iterable<S> entities); <S extends Target> List<S> save(Iterable<S> entities);
@@ -167,7 +166,7 @@ public interface TargetRepository extends BaseEntityRepository<Target, Long>, Jp
*/ */
@Override @Override
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true) @CacheEvict(value = { "targetStatus", "distributionUsageInstalled", "targetsLastPoll" }, allEntries = true)
<S extends Target> S save(S entity); <S extends Target> S save(S entity);
@@ -276,7 +275,7 @@ public interface TargetRepository extends BaseEntityRepository<Target, Long>, Jp
* to update * to update
*/ */
@Modifying @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") @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, void setAssignedDistributionSet(@Param("set") DistributionSet set, @Param("lastModifiedAt") Long modifiedAt,
@Param("lastModifiedBy") String modifiedBy, @Param("targets") Collection<Long> targets); @Param("lastModifiedBy") String modifiedBy, @Param("targets") Collection<Long> targets);

View File

@@ -13,15 +13,14 @@ import java.util.List;
import org.eclipse.hawkbit.repository.model.TargetTag; import org.eclipse.hawkbit.repository.model.TargetTag;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
/** /**
* {@link TargetTag} repository. * {@link TargetTag} repository.
* *
*
*
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public interface TargetTagRepository public interface TargetTagRepository
extends BaseEntityRepository<TargetTag, Long>, JpaSpecificationExecutor<TargetTag> { extends BaseEntityRepository<TargetTag, Long>, JpaSpecificationExecutor<TargetTag> {
@@ -33,7 +32,7 @@ public interface TargetTagRepository
* @return 1 if tag was deleted * @return 1 if tag was deleted
*/ */
@Modifying @Modifying
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
Long deleteByName(final String tagName); Long deleteByName(final String tagName);
/** /**

View File

@@ -24,18 +24,19 @@ import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.security.access.prepost.PreAuthorize; 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.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
/** /**
* Central tenant configuration management operations of the SP server. * Central tenant configuration management operations of the SP server.
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
@Validated @Validated
@Service
public class TenantConfigurationManagement implements EnvironmentAware { public class TenantConfigurationManagement implements EnvironmentAware {
private static final TenantConfigurationManagement INSTANCE = new TenantConfigurationManagement();
@Autowired @Autowired
private TenantConfigurationRepository tenantConfigurationRepository; private TenantConfigurationRepository tenantConfigurationRepository;
@@ -46,16 +47,6 @@ public class TenantConfigurationManagement implements EnvironmentAware {
private Environment environment; 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 * Retrieves a configuration value from the e.g. tenant overwritten
* configuration values or in case the tenant does not a have a specific * 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 * if the property cannot be converted to the given
* {@code propertyType} * {@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) { public TenantConfigurationValue<?> getConfigurationValue(final TenantConfigurationKey configurationKey) {
return getConfigurationValue(configurationKey, configurationKey.getDataType()); return getConfigurationValue(configurationKey, configurationKey.getDataType());
} }
@@ -185,7 +177,8 @@ public class TenantConfigurationManagement implements EnvironmentAware {
* if the property cannot be converted to the given * if the property cannot be converted to the given
* {@code propertyType} * {@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, public <T> T getGlobalConfigurationValue(final TenantConfigurationKey configurationKey,
final Class<T> propertyType) { final Class<T> propertyType) {
@@ -221,7 +214,7 @@ public class TenantConfigurationManagement implements EnvironmentAware {
* if the property cannot be converted to the given * if the property cannot be converted to the given
*/ */
@CacheEvict(value = "tenantConfiguration", key = "#configurationKey.getKeyName()") @CacheEvict(value = "tenantConfiguration", key = "#configurationKey.getKeyName()")
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION) @PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
public <T> TenantConfigurationValue<T> addOrUpdateConfiguration(final TenantConfigurationKey configurationKey, public <T> TenantConfigurationValue<T> addOrUpdateConfiguration(final TenantConfigurationKey configurationKey,
@@ -264,7 +257,7 @@ public class TenantConfigurationManagement implements EnvironmentAware {
* the configuration key to be deleted * the configuration key to be deleted
*/ */
@CacheEvict(value = "tenantConfiguration", key = "#configurationKey.getKeyName()") @CacheEvict(value = "tenantConfiguration", key = "#configurationKey.getKeyName()")
@Transactional @Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Modifying @Modifying
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION) @PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
public void deleteConfiguration(final TenantConfigurationKey configurationKey) { public void deleteConfiguration(final TenantConfigurationKey configurationKey) {

View File

@@ -11,13 +11,14 @@ package org.eclipse.hawkbit.repository;
import java.util.List; import java.util.List;
import org.eclipse.hawkbit.repository.model.TenantConfiguration; import org.eclipse.hawkbit.repository.model.TenantConfiguration;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
/** /**
* The spring-data repository for the entity {@link TenantConfiguration}. * 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> { public interface TenantConfigurationRepository extends BaseEntityRepository<TenantConfiguration, Long> {
/** /**

View File

@@ -12,16 +12,14 @@ import java.util.List;
import org.eclipse.hawkbit.repository.model.TenantMetaData; import org.eclipse.hawkbit.repository.model.TenantMetaData;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
/** /**
* repository for operations on {@link TenantMetaData} entity. * repository for operations on {@link TenantMetaData} entity.
* *
*
*
*
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public interface TenantMetaDataRepository extends PagingAndSortingRepository<TenantMetaData, Long> { public interface TenantMetaDataRepository extends PagingAndSortingRepository<TenantMetaData, Long> {
/** /**

View File

@@ -38,7 +38,8 @@ import javax.persistence.OneToOne;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.Transient; 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.DurationHelper;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey;
import org.eclipse.persistence.annotations.CascadeOnDelete; import org.eclipse.persistence.annotations.CascadeOnDelete;
@@ -245,18 +246,21 @@ public class TargetInfo implements Persistable<Long>, Serializable {
if (lastTargetQuery == null) { if (lastTargetQuery == null) {
return null; return null;
} }
return SystemSecurityContextHolder.getInstance().getSystemSecurityContext().runAsSystem(() -> {
final Duration pollTime = DurationHelper.formattedStringToDuration(TenantConfigurationManagement.getInstance() final Duration pollTime = DurationHelper.formattedStringToDuration(TenantConfigurationManagementHolder
.getConfigurationValue(TenantConfigurationKey.POLLING_TIME_INTERVAL, String.class).getValue()); .getInstance().getTenantConfigurationManagement()
final Duration overdueTime = DurationHelper.formattedStringToDuration(TenantConfigurationManagement .getConfigurationValue(TenantConfigurationKey.POLLING_TIME_INTERVAL, String.class).getValue());
.getInstance().getConfigurationValue(TenantConfigurationKey.POLLING_OVERDUE_TIME_INTERVAL, String.class) final Duration overdueTime = DurationHelper.formattedStringToDuration(
.getValue()); TenantConfigurationManagementHolder.getInstance().getTenantConfigurationManagement()
final LocalDateTime currentDate = LocalDateTime.now(); .getConfigurationValue(TenantConfigurationKey.POLLING_OVERDUE_TIME_INTERVAL, String.class)
final LocalDateTime lastPollDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(lastTargetQuery), .getValue());
ZoneId.systemDefault()); final LocalDateTime currentDate = LocalDateTime.now();
final LocalDateTime nextPollDate = lastPollDate.plus(pollTime); final LocalDateTime lastPollDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(lastTargetQuery),
final LocalDateTime overdueDate = nextPollDate.plus(overdueTime); ZoneId.systemDefault());
return new PollStatus(lastPollDate, nextPollDate, overdueDate, currentDate); final LocalDateTime nextPollDate = lastPollDate.plus(pollTime);
final LocalDateTime overdueDate = nextPollDate.plus(overdueTime);
return new PollStatus(lastPollDate, nextPollDate, overdueDate, currentDate);
});
} }
/** /**

View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.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;
}
}

View File

@@ -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;
}
}

View File

@@ -66,10 +66,6 @@ import cz.jirutka.rsql.parser.ast.RSQLVisitor;
* <li>name==targetId1,description==plugAndPlay,updateStatus==UNKNOWN</li> * <li>name==targetId1,description==plugAndPlay,updateStatus==UNKNOWN</li>
* <li>name==targetId1 or description==plugAndPlay or updateStatus==UNKNOWN</li> * <li>name==targetId1 or description==plugAndPlay or updateStatus==UNKNOWN</li>
* </ul> * </ul>
*
*
*
*
*/ */
public final class RSQLUtility { public final class RSQLUtility {
@@ -279,6 +275,9 @@ public final class RSQLUtility {
} }
@Override @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) { public List<Predicate> visit(final ComparisonNode node, final String param) {
A fieldName = null; A fieldName = null;
try { try {
@@ -304,6 +303,9 @@ public final class RSQLUtility {
return mapToPredicate(node, fieldPath, node.getArguments(), transformedValue, fieldName); 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() { private List<String> getExpectedFieldList() {
final List<String> expectedFieldList = Arrays.stream(enumType.getEnumConstants()) final List<String> expectedFieldList = Arrays.stream(enumType.getEnumConstants())
.filter(enumField -> enumField.getSubEntityAttributes().isEmpty()).map(enumField -> { .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, private Object transformEnumValue(final ComparisonNode node, final String value,
final Class<? extends Object> javaType) { final Class<? extends Object> javaType) {
final Class<? extends Enum> tmpEnumType = (Class<? extends Enum>) javaType; final Class<? extends Enum> tmpEnumType = (Class<? extends Enum>) javaType;

View File

@@ -79,7 +79,7 @@ public class TestConfiguration implements AsyncConfigurer {
} }
/** /**
* Bean for the downlod id cache. * Bean for the download id cache.
* *
* @return the cache * @return the cache
*/ */

View File

@@ -14,6 +14,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import java.net.URI;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@@ -29,6 +30,7 @@ import javax.validation.ConstraintViolationException;
import org.eclipse.hawkbit.AbstractIntegrationTest; import org.eclipse.hawkbit.AbstractIntegrationTest;
import org.eclipse.hawkbit.TestDataUtil; import org.eclipse.hawkbit.TestDataUtil;
import org.eclipse.hawkbit.WithSpringAuthorityRule;
import org.eclipse.hawkbit.WithUser; import org.eclipse.hawkbit.WithUser;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.TenantNotExistException; import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
@@ -724,4 +726,20 @@ public class TargetManagementTest extends AbstractIntegrationTest {
assertThat(25).as("Targets with no tag").isEqualTo(targetsListWithNoTag.size()); assertThat(25).as("Targets with no tag").isEqualTo(targetsListWithNoTag.size());
} }
@Test
@Description("Tests the a target can be read with only the read target permission")
public void targetCanBeReadWithOnlyReadTargetPermission() throws Exception {
final String knownTargetControllerId = "readTarget";
controllerManagament.findOrRegisterTargetIfItDoesNotexist(knownTargetControllerId, new URI("http://127.0.0.1"));
securityRule.runAs(WithSpringAuthorityRule.withUser("bumlux", "READ_TARGET"), () -> {
final Target findTargetByControllerID = targetManagement.findTargetByControllerID(knownTargetControllerId);
assertThat(findTargetByControllerID).isNotNull();
assertThat(findTargetByControllerID.getTargetInfo()).isNotNull();
assertThat(findTargetByControllerID.getTargetInfo().getPollStatus()).isNotNull();
return null;
});
}
} }

View File

@@ -143,9 +143,11 @@ public class ArtifactStoreController {
actionStatus.setStatus(Status.DOWNLOAD); actionStatus.setStatus(Status.DOWNLOAD);
if (range != null) { if (range != null) {
actionStatus.addMessage("It is a partial download request: " + range); actionStatus.addMessage(ControllerManagement.SERVER_MESSAGE_PREFIX + "Target downloads range " + range
+ " of: " + request.getRequestURI());
} else { } else {
actionStatus.addMessage("Target downloads"); actionStatus.addMessage(
ControllerManagement.SERVER_MESSAGE_PREFIX + "Target downloads: " + request.getRequestURI());
} }
controllerManagement.addActionStatusMessage(actionStatus); controllerManagement.addActionStatusMessage(actionStatus);
return action; return action;

View File

@@ -31,7 +31,6 @@ import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.rest.resource.model.artifact.ArtifactHash; import org.eclipse.hawkbit.rest.resource.model.artifact.ArtifactHash;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.Link; import org.springframework.hateoas.Link;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
@@ -40,10 +39,6 @@ import com.google.common.base.Charsets;
* Utility class for the Controller API. * Utility class for the Controller API.
*/ */
public final class DataConversionHelper { public final class DataConversionHelper {
@Autowired
ArtifactUrlHandler artifactUrlHandler;
// utility class, private constructor. // utility class, private constructor.
private DataConversionHelper() { private DataConversionHelper() {
@@ -55,7 +50,6 @@ public final class DataConversionHelper {
.map(module -> new Chunk(mapChunkLegacyKeys(module.getType().getKey()), module.getVersion(), .map(module -> new Chunk(mapChunkLegacyKeys(module.getType().getKey()), module.getVersion(),
module.getName(), createArtifacts(targetid, module, artifactUrlHandler))) module.getName(), createArtifacts(targetid, module, artifactUrlHandler)))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private static String mapChunkLegacyKeys(final String key) { private static String mapChunkLegacyKeys(final String key) {
@@ -76,29 +70,39 @@ public final class DataConversionHelper {
* of the target * of the target
* @param module * @param module
* the software module * the software module
*
* @return a list of artifacts or a empty list. Cannot be <null>. * @return a list of artifacts or a empty list. Cannot be <null>.
*/ */
public static List<Artifact> createArtifacts(final String targetid, public static List<Artifact> createArtifacts(final String targetid,
final org.eclipse.hawkbit.repository.model.SoftwareModule module, final org.eclipse.hawkbit.repository.model.SoftwareModule module,
final ArtifactUrlHandler artifactUrlHandler) { final ArtifactUrlHandler artifactUrlHandler) {
final List<Artifact> files = new ArrayList<>(); final List<Artifact> files = new ArrayList<>();
module.getLocalArtifacts().forEach(artifact -> { module.getLocalArtifacts()
final Artifact file = new Artifact(); .forEach(artifact -> files.add(createArtifact(targetid, artifactUrlHandler, artifact)));
file.setHashes(new ArtifactHash(artifact.getSha1Hash(), artifact.getMd5Hash())); return files;
file.setFilename(artifact.getFilename()); }
file.setSize(artifact.getSize());
private static Artifact createArtifact(final String targetid, final ArtifactUrlHandler artifactUrlHandler,
final LocalArtifact artifact) {
final Artifact file = new Artifact();
file.setHashes(new ArtifactHash(artifact.getSha1Hash(), artifact.getMd5Hash()));
file.setFilename(artifact.getFilename());
file.setSize(artifact.getSize());
if (artifactUrlHandler.protocolSupported(UrlProtocol.HTTP)) {
final String linkHttp = artifactUrlHandler.getUrl(targetid, artifact.getSoftwareModule().getId(), final String linkHttp = artifactUrlHandler.getUrl(targetid, artifact.getSoftwareModule().getId(),
artifact.getFilename(), artifact.getSha1Hash(), UrlProtocol.HTTP); artifact.getFilename(), artifact.getSha1Hash(), UrlProtocol.HTTP);
file.add(new Link(linkHttp).withRel("download-http"));
file.add(new Link(linkHttp + ControllerConstants.ARTIFACT_MD5_DWNL_SUFFIX).withRel("md5sum-http"));
}
if (artifactUrlHandler.protocolSupported(UrlProtocol.HTTPS)) {
final String linkHttps = artifactUrlHandler.getUrl(targetid, artifact.getSoftwareModule().getId(), final String linkHttps = artifactUrlHandler.getUrl(targetid, artifact.getSoftwareModule().getId(),
artifact.getFilename(), artifact.getSha1Hash(), UrlProtocol.HTTPS); artifact.getFilename(), artifact.getSha1Hash(), UrlProtocol.HTTPS);
file.add(new Link(linkHttps).withRel("download")); file.add(new Link(linkHttps).withRel("download"));
file.add(new Link(linkHttps + ControllerConstants.ARTIFACT_MD5_DWNL_SUFFIX).withRel("md5sum")); file.add(new Link(linkHttps + ControllerConstants.ARTIFACT_MD5_DWNL_SUFFIX).withRel("md5sum"));
file.add(new Link(linkHttp).withRel("download-http")); }
file.add(new Link(linkHttp + ControllerConstants.ARTIFACT_MD5_DWNL_SUFFIX).withRel("md5sum-http")); return file;
files.add(file);
});
return files;
} }
static ControllerBase fromTarget(final Target target, final List<Action> actions, static ControllerBase fromTarget(final Target target, final List<Action> actions,

View File

@@ -216,9 +216,11 @@ public class RootController {
statusMessage.setStatus(Status.DOWNLOAD); statusMessage.setStatus(Status.DOWNLOAD);
if (range != null) { if (range != null) {
statusMessage.addMessage("It is a partial download request: " + range); statusMessage.addMessage(ControllerManagement.SERVER_MESSAGE_PREFIX + "Target downloads range " + range
+ " of: " + request.getRequestURI());
} else { } else {
statusMessage.addMessage("Controller downloads"); statusMessage.addMessage(
ControllerManagement.SERVER_MESSAGE_PREFIX + "Target downloads " + request.getRequestURI());
} }
controllerManagement.addActionStatusMessage(statusMessage); controllerManagement.addActionStatusMessage(statusMessage);
return action; return action;
@@ -316,8 +318,8 @@ public class RootController {
LOG.debug("Found an active UpdateAction for target {}. returning deyploment: {}", targetid, base); LOG.debug("Found an active UpdateAction for target {}. returning deyploment: {}", targetid, base);
controllerManagement.registerRetrieved(action, controllerManagement.registerRetrieved(action, ControllerManagement.SERVER_MESSAGE_PREFIX
"Controller retrieved update action and should start now the download."); + "Target retrieved update action and should start now the download.");
return new ResponseEntity<>(base, HttpStatus.OK); return new ResponseEntity<>(base, HttpStatus.OK);
} }
@@ -388,13 +390,13 @@ public class RootController {
LOG.debug("Controller confirmed cancel (actionid: {}, targetid: {}) as we got {} report.", actionid, LOG.debug("Controller confirmed cancel (actionid: {}, targetid: {}) as we got {} report.", actionid,
targetid, feedback.getStatus().getExecution()); targetid, feedback.getStatus().getExecution());
actionStatus.setStatus(Status.CANCELED); actionStatus.setStatus(Status.CANCELED);
actionStatus.addMessage("Controller confirmed cancelation"); actionStatus.addMessage(ControllerManagement.SERVER_MESSAGE_PREFIX + "Target confirmed cancelation.");
break; break;
case REJECTED: case REJECTED:
LOG.info("Controller reported internal error (actionid: {}, targetid: {}) as we got {} report.", actionid, LOG.info("Controller reported internal error (actionid: {}, targetid: {}) as we got {} report.", actionid,
targetid, feedback.getStatus().getExecution()); targetid, feedback.getStatus().getExecution());
actionStatus.setStatus(Status.WARNING); actionStatus.setStatus(Status.WARNING);
actionStatus.addMessage("Controller reported internal ERROR and REJECTED update."); actionStatus.addMessage(ControllerManagement.SERVER_MESSAGE_PREFIX + "Target REJECTED update.");
break; break;
case CLOSED: case CLOSED:
handleClosedUpdateStatus(feedback, targetid, actionid, actionStatus); handleClosedUpdateStatus(feedback, targetid, actionid, actionStatus);
@@ -421,7 +423,8 @@ public class RootController {
LOG.debug("Controller reported intermediate status (actionid: {}, targetid: {}) as we got {} report.", actionid, LOG.debug("Controller reported intermediate status (actionid: {}, targetid: {}) as we got {} report.", actionid,
targetid, feedback.getStatus().getExecution()); targetid, feedback.getStatus().getExecution());
actionStatus.setStatus(Status.RUNNING); actionStatus.setStatus(Status.RUNNING);
actionStatus.addMessage("Controller reported: " + feedback.getStatus().getExecution()); actionStatus.addMessage(
ControllerManagement.SERVER_MESSAGE_PREFIX + "Target reported " + feedback.getStatus().getExecution());
} }
private static void handleClosedUpdateStatus(final ActionFeedback feedback, final String targetid, private static void handleClosedUpdateStatus(final ActionFeedback feedback, final String targetid,
@@ -430,10 +433,10 @@ public class RootController {
feedback.getStatus().getExecution()); feedback.getStatus().getExecution());
if (feedback.getStatus().getResult().getFinished() == FinalResult.FAILURE) { if (feedback.getStatus().getResult().getFinished() == FinalResult.FAILURE) {
actionStatus.setStatus(Status.ERROR); actionStatus.setStatus(Status.ERROR);
actionStatus.addMessage("Controller reported CLOSED with ERROR!"); actionStatus.addMessage(ControllerManagement.SERVER_MESSAGE_PREFIX + "Target reported CLOSED with ERROR!");
} else { } else {
actionStatus.setStatus(Status.FINISHED); actionStatus.setStatus(Status.FINISHED);
actionStatus.addMessage("Controller reported CLOSED with OK!"); actionStatus.addMessage(ControllerManagement.SERVER_MESSAGE_PREFIX + "Target reported CLOSED with OK!");
} }
} }
@@ -494,8 +497,8 @@ public class RootController {
LOG.debug("Found an active CancelAction for target {}. returning cancel: {}", targetid, cancel); LOG.debug("Found an active CancelAction for target {}. returning cancel: {}", targetid, cancel);
controllerManagement.registerRetrieved(action, controllerManagement.registerRetrieved(action, ControllerManagement.SERVER_MESSAGE_PREFIX
"Controller retrieved cancel action and should start now the cancelation."); + "Target retrieved cancel action and should start now the cancelation.");
return new ResponseEntity<>(cancel, HttpStatus.OK); return new ResponseEntity<>(cancel, HttpStatus.OK);
} }

View File

@@ -19,12 +19,10 @@ import org.springframework.security.core.GrantedAuthority;
* *
*/ */
public class TenantUserPasswordAuthenticationToken extends UsernamePasswordAuthenticationToken { public class TenantUserPasswordAuthenticationToken extends UsernamePasswordAuthenticationToken {
/**
*
*/
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
// Exception squid:S1948 - no need to be Serializable
@SuppressWarnings({ "squid:S1948" })
final Object tenant; final Object tenant;
/** /**

View File

@@ -1,37 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.security;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
/**
* A Filter for device which download via coap.
*
*
*
*/
public class CoapAnonymousPreAuthenticatedFilter implements PreAuthenficationFilter {
@Override
public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecurityToken secruityToken) {
return new HeaderAuthentication(secruityToken.getControllerId(), TenantSecurityToken.COAP_TOKEN_VALUE);
}
@Override
public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecurityToken secruityToken) {
return new HeaderAuthentication(secruityToken.getControllerId(), TenantSecurityToken.COAP_TOKEN_VALUE);
}
@Override
public boolean isEnable(final TenantSecurityToken secruityToken) {
final String authHeader = secruityToken.getHeader(TenantSecurityToken.COAP_AUTHORIZATION_HEADER);
return TenantSecurityToken.COAP_TOKEN_VALUE.equals(authHeader);
}
}

View File

@@ -24,11 +24,11 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
/** /**
* An spring authentication provider which supportes authentication tokens of * An spring authentication provider which supports authentication tokens of
* type {@link PreAuthenticatedAuthenticationToken} created by the * type {@link PreAuthenticatedAuthenticationToken} created by the
* {@link ControllerPreAuthenticatedSecurityHeaderFilter}. * {@link ControllerPreAuthenticatedSecurityHeaderFilter}.
* *
* Addtionally to the authentication token providing the principal and the * Additionally to the authentication token providing the principal and the
* credentials which must be match, this authentication provider can also check * credentials which must be match, this authentication provider can also check
* the remote IP address of the request. * the remote IP address of the request.
* *

Some files were not shown because too many files have changed in this diff Show More